summaryrefslogtreecommitdiff
path: root/runtime
diff options
context:
space:
mode:
authorhaturau <135221985+haturatu@users.noreply.github.com>2024-11-20 01:20:47 +0900
committerGitHub <noreply@github.com>2024-11-20 01:20:47 +0900
commit85719a67e59c7aa45bead26e4942d7df8b1b42d4 (patch)
treeface0aecaac53e93ce2f23b53c48859bcf1a36ec /runtime
parent67697bc2e4a62a9670699fd18ad0dd8efc5bd955 (diff)
parent186b52731c6bb326c4d32905c5e732d082e83465 (diff)
Merge branch 'denoland:main' into main
Diffstat (limited to 'runtime')
-rw-r--r--runtime/Cargo.toml12
-rw-r--r--runtime/clippy.toml1
-rw-r--r--runtime/code_cache.rs11
-rw-r--r--runtime/errors.rs1651
-rw-r--r--runtime/examples/extension/main.rs1
-rw-r--r--runtime/fmt_errors.rs224
-rw-r--r--runtime/inspector_server.rs7
-rw-r--r--runtime/js/40_fs_events.js2
-rw-r--r--runtime/js/90_deno_ns.js19
-rw-r--r--runtime/js/98_global_scope_shared.js13
-rw-r--r--runtime/js/99_main.js22
-rw-r--r--runtime/js/telemetry.ts720
-rw-r--r--runtime/lib.rs29
-rw-r--r--runtime/ops/fs_events.rs121
-rw-r--r--runtime/ops/http.rs43
-rw-r--r--runtime/ops/mod.rs2
-rw-r--r--runtime/ops/os/mod.rs99
-rw-r--r--runtime/ops/otel.rs855
-rw-r--r--runtime/ops/permissions.rs43
-rw-r--r--runtime/ops/process.rs171
-rw-r--r--runtime/ops/runtime.rs6
-rw-r--r--runtime/ops/signal.rs663
-rw-r--r--runtime/ops/tty.rs54
-rw-r--r--runtime/ops/utils.rs12
-rw-r--r--runtime/ops/web_worker.rs10
-rw-r--r--runtime/ops/web_worker/sync_fetch.rs95
-rw-r--r--runtime/ops/worker_host.rs57
-rw-r--r--runtime/permissions.rs44
-rw-r--r--runtime/permissions/Cargo.toml3
-rw-r--r--runtime/permissions/lib.rs790
-rw-r--r--runtime/permissions/prompter.rs81
-rw-r--r--runtime/shared.rs10
-rw-r--r--runtime/snapshot.rs76
-rw-r--r--runtime/sys_info.rs (renamed from runtime/ops/os/sys_info.rs)0
-rw-r--r--runtime/tokio_util.rs10
-rw-r--r--runtime/web_worker.rs49
-rw-r--r--runtime/worker.rs30
-rw-r--r--runtime/worker_bootstrap.rs11
38 files changed, 4900 insertions, 1147 deletions
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
index ba9dc6243..b59cd14fa 100644
--- a/runtime/Cargo.toml
+++ b/runtime/Cargo.toml
@@ -2,7 +2,7 @@
[package]
name = "deno_runtime"
-version = "0.180.0"
+version = "0.186.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@@ -100,6 +100,8 @@ deno_websocket.workspace = true
deno_webstorage.workspace = true
node_resolver = { workspace = true, features = ["sync"] }
+async-trait.workspace = true
+color-print.workspace = true
dlopen2.workspace = true
encoding_rs.workspace = true
fastwebsockets.workspace = true
@@ -113,13 +115,21 @@ log.workspace = true
netif = "0.1.6"
notify.workspace = true
once_cell.workspace = true
+opentelemetry.workspace = true
+opentelemetry-http.workspace = true
+opentelemetry-otlp.workspace = true
+opentelemetry-semantic-conventions.workspace = true
+opentelemetry_sdk.workspace = true
percent-encoding.workspace = true
+pin-project.workspace = true
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"
tempfile.workspace = true
+thiserror.workspace = true
tokio.workspace = true
tokio-metrics.workspace = true
twox-hash.workspace = true
diff --git a/runtime/clippy.toml b/runtime/clippy.toml
index 53676a90e..79e6bbd08 100644
--- a/runtime/clippy.toml
+++ b/runtime/clippy.toml
@@ -42,4 +42,5 @@ disallowed-methods = [
{ path = "std::fs::write", reason = "File system operations should be done using FileSystem trait" },
{ path = "std::path::Path::canonicalize", reason = "File system operations should be done using FileSystem trait" },
{ path = "std::path::Path::exists", reason = "File system operations should be done using FileSystem trait" },
+ { path = "std::process::exit", reason = "use deno_runtime::exit instead" },
]
diff --git a/runtime/code_cache.rs b/runtime/code_cache.rs
index 2a56543a4..b4a7ce188 100644
--- a/runtime/code_cache.rs
+++ b/runtime/code_cache.rs
@@ -2,20 +2,12 @@
use deno_core::ModuleSpecifier;
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CodeCacheType {
EsModule,
Script,
}
-impl CodeCacheType {
- pub fn as_str(&self) -> &str {
- match self {
- Self::EsModule => "esmodule",
- Self::Script => "script",
- }
- }
-}
-
pub trait CodeCache: Send + Sync {
fn get_sync(
&self,
@@ -23,6 +15,7 @@ pub trait CodeCache: Send + Sync {
code_cache_type: CodeCacheType,
source_hash: u64,
) -> Option<Vec<u8>>;
+
fn set_sync(
&self,
specifier: ModuleSpecifier,
diff --git a/runtime/errors.rs b/runtime/errors.rs
index 4c6aeab98..4268fbd50 100644
--- a/runtime/errors.rs
+++ b/runtime/errors.rs
@@ -9,6 +9,16 @@
//! Diagnostics are compile-time type errors, whereas JsErrors are runtime
//! exceptions.
+use crate::ops::fs_events::FsEventsError;
+use crate::ops::http::HttpStartError;
+use crate::ops::os::OsError;
+use crate::ops::permissions::PermissionError;
+use crate::ops::process::CheckRunPermissionError;
+use crate::ops::process::ProcessError;
+use crate::ops::signal::SignalError;
+use crate::ops::tty::TtyError;
+use crate::ops::web_worker::SyncFetchError;
+use crate::ops::worker_host::CreateWorkerError;
use deno_broadcast_channel::BroadcastChannelError;
use deno_cache::CacheError;
use deno_canvas::CanvasError;
@@ -17,12 +27,100 @@ use deno_core::serde_json;
use deno_core::url;
use deno_core::ModuleResolutionError;
use deno_cron::CronError;
+use deno_crypto::DecryptError;
+use deno_crypto::EncryptError;
+use deno_crypto::ExportKeyError;
+use deno_crypto::GenerateKeyError;
+use deno_crypto::ImportKeyError;
+use deno_fetch::FetchError;
+use deno_fetch::HttpClientCreateError;
+use deno_ffi::CallError;
+use deno_ffi::CallbackError;
+use deno_ffi::DlfcnError;
+use deno_ffi::IRError;
+use deno_ffi::ReprError;
+use deno_ffi::StaticError;
+use deno_fs::FsOpsError;
+use deno_fs::FsOpsErrorKind;
+use deno_http::HttpError;
+use deno_http::HttpNextError;
+use deno_http::WebSocketUpgradeError;
+use deno_io::fs::FsError;
+use deno_kv::KvCheckError;
+use deno_kv::KvError;
+use deno_kv::KvErrorKind;
+use deno_kv::KvMutationError;
+use deno_napi::NApiError;
+use deno_net::ops::NetError;
+use deno_permissions::ChildPermissionError;
+use deno_permissions::NetDescriptorFromUrlParseError;
+use deno_permissions::PathResolveError;
+use deno_permissions::PermissionCheckError;
+use deno_permissions::RunDescriptorParseError;
+use deno_permissions::SysDescriptorParseError;
use deno_tls::TlsError;
+use deno_web::BlobError;
+use deno_web::CompressionError;
+use deno_web::MessagePortError;
+use deno_web::StreamResourceError;
+use deno_web::WebError;
+use deno_websocket::HandshakeError;
+use deno_websocket::WebsocketError;
+use deno_webstorage::WebStorageError;
+use rustyline::error::ReadlineError;
use std::env;
use std::error::Error;
use std::io;
use std::sync::Arc;
+fn get_run_descriptor_parse_error(e: &RunDescriptorParseError) -> &'static str {
+ match e {
+ RunDescriptorParseError::Which(_) => "Error",
+ RunDescriptorParseError::PathResolve(e) => get_path_resolve_error(e),
+ RunDescriptorParseError::EmptyRunQuery => "Error",
+ }
+}
+
+fn get_sys_descriptor_parse_error(e: &SysDescriptorParseError) -> &'static str {
+ match e {
+ SysDescriptorParseError::InvalidKind(_) => "TypeError",
+ SysDescriptorParseError::Empty => "Error",
+ }
+}
+
+fn get_path_resolve_error(e: &PathResolveError) -> &'static str {
+ match e {
+ PathResolveError::CwdResolve(e) => get_io_error_class(e),
+ PathResolveError::EmptyPath => "Error",
+ }
+}
+
+fn get_permission_error_class(e: &PermissionError) -> &'static str {
+ match e {
+ PermissionError::InvalidPermissionName(_) => "ReferenceError",
+ PermissionError::PathResolve(e) => get_path_resolve_error(e),
+ PermissionError::NetDescriptorParse(_) => "URIError",
+ PermissionError::SysDescriptorParse(e) => get_sys_descriptor_parse_error(e),
+ PermissionError::RunDescriptorParse(e) => get_run_descriptor_parse_error(e),
+ }
+}
+
+fn get_permission_check_error_class(e: &PermissionCheckError) -> &'static str {
+ match e {
+ PermissionCheckError::PermissionDenied(_) => "NotCapable",
+ PermissionCheckError::InvalidFilePath(_) => "URIError",
+ PermissionCheckError::NetDescriptorForUrlParse(e) => match e {
+ NetDescriptorFromUrlParseError::MissingHost(_) => "TypeError",
+ NetDescriptorFromUrlParseError::Host(_) => "URIError",
+ },
+ PermissionCheckError::SysDescriptorParse(e) => {
+ get_sys_descriptor_parse_error(e)
+ }
+ PermissionCheckError::PathResolve(e) => get_path_resolve_error(e),
+ PermissionCheckError::HostParse(_) => "URIError",
+ }
+}
+
fn get_dlopen_error_class(error: &dlopen2::Error) -> &'static str {
use dlopen2::Error::*;
match error {
@@ -158,6 +256,381 @@ pub fn get_nix_error_class(error: &nix::Error) -> &'static str {
}
}
+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",
@@ -217,21 +690,1191 @@ fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str {
}
}
+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::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),
+ FetchError::Hyper(e) => get_hyper_error_class(e),
+ }
+}
+
+fn get_http_client_create_error(error: &HttpClientCreateError) -> &'static str {
+ match error {
+ HttpClientCreateError::Tls(_) => "TypeError",
+ HttpClientCreateError::InvalidUserAgent(_) => "TypeError",
+ HttpClientCreateError::InvalidProxyUrl => "TypeError",
+ HttpClientCreateError::HttpVersionSelectionInvalid => "TypeError",
+ HttpClientCreateError::RootCertStore(_) => "TypeError",
+ }
+}
+
+fn get_websocket_error(error: &WebsocketError) -> &'static str {
+ match error {
+ WebsocketError::Resource(e) => get_error_class_name(e).unwrap_or("Error"),
+ WebsocketError::Permission(e) => get_permission_check_error_class(e),
+ WebsocketError::Url(e) => get_url_parse_error_class(e),
+ WebsocketError::Io(e) => get_io_error_class(e),
+ WebsocketError::WebSocket(_) => "TypeError",
+ WebsocketError::ConnectionFailed(_) => "DOMExceptionNetworkError",
+ WebsocketError::Uri(_) => "Error",
+ WebsocketError::Canceled(e) => {
+ let io_err: io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ }
+}
+
+fn get_websocket_handshake_error(error: &HandshakeError) -> &'static str {
+ match error {
+ HandshakeError::RootStoreError(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ HandshakeError::Tls(e) => get_tls_error_class(e),
+ HandshakeError::MissingPath => "TypeError",
+ HandshakeError::Http(_) => "Error",
+ HandshakeError::InvalidHostname(_) => "TypeError",
+ HandshakeError::Io(e) => get_io_error_class(e),
+ HandshakeError::Rustls(_) => "Error",
+ HandshakeError::H2(_) => "Error",
+ HandshakeError::NoH2Alpn => "Error",
+ HandshakeError::InvalidStatusCode(_) => "Error",
+ HandshakeError::WebSocket(_) => "TypeError",
+ HandshakeError::HeaderName(_) => "TypeError",
+ HandshakeError::HeaderValue(_) => "TypeError",
+ }
+}
+
+fn get_fs_ops_error(error: &FsOpsError) -> &'static str {
+ use FsOpsErrorKind::*;
+ match error.as_kind() {
+ Io(e) => get_io_error_class(e),
+ OperationError(e) => get_fs_error(&e.err),
+ Permission(e) => get_permission_check_error_class(e),
+ Resource(e) | Other(e) => get_error_class_name(e).unwrap_or("Error"),
+ InvalidUtf8(_) => "InvalidData",
+ StripPrefix(_) => "Error",
+ Canceled(e) => {
+ let io_err: io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ InvalidSeekMode(_) => "TypeError",
+ InvalidControlCharacter(_) => "Error",
+ InvalidCharacter(_) => "Error",
+ #[cfg(windows)]
+ InvalidTrailingCharacter => "Error",
+ NotCapableAccess { .. } => "NotCapable",
+ NotCapable(_) => "NotCapable",
+ }
+}
+
+fn get_kv_error(error: &KvError) -> &'static str {
+ use KvErrorKind::*;
+ match error.as_kind() {
+ DatabaseHandler(e) | Resource(e) | Kv(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ TooManyRanges(_) => "TypeError",
+ TooManyEntries(_) => "TypeError",
+ TooManyChecks(_) => "TypeError",
+ TooManyMutations(_) => "TypeError",
+ TooManyKeys(_) => "TypeError",
+ InvalidLimit => "TypeError",
+ InvalidBoundaryKey => "TypeError",
+ KeyTooLargeToRead(_) => "TypeError",
+ KeyTooLargeToWrite(_) => "TypeError",
+ TotalMutationTooLarge(_) => "TypeError",
+ TotalKeyTooLarge(_) => "TypeError",
+ Io(e) => get_io_error_class(e),
+ QueueMessageNotFound => "TypeError",
+ StartKeyNotInKeyspace => "TypeError",
+ EndKeyNotInKeyspace => "TypeError",
+ StartKeyGreaterThanEndKey => "TypeError",
+ InvalidCheck(e) => match e {
+ KvCheckError::InvalidVersionstamp => "TypeError",
+ KvCheckError::Io(e) => get_io_error_class(e),
+ },
+ InvalidMutation(e) => match e {
+ KvMutationError::BigInt(_) => "Error",
+ KvMutationError::Io(e) => get_io_error_class(e),
+ KvMutationError::InvalidMutationWithValue(_) => "TypeError",
+ KvMutationError::InvalidMutationWithoutValue(_) => "TypeError",
+ },
+ InvalidEnqueue(e) => get_io_error_class(e),
+ EmptyKey => "TypeError",
+ ValueTooLarge(_) => "TypeError",
+ EnqueuePayloadTooLarge(_) => "TypeError",
+ InvalidCursor => "TypeError",
+ CursorOutOfBounds => "TypeError",
+ InvalidRange => "TypeError",
+ }
+}
+
+fn get_net_error(error: &NetError) -> &'static str {
+ match error {
+ NetError::ListenerClosed => "BadResource",
+ NetError::ListenerBusy => "Busy",
+ NetError::SocketClosed => "BadResource",
+ NetError::SocketClosedNotConnected => "NotConnected",
+ NetError::SocketBusy => "Busy",
+ NetError::Io(e) => get_io_error_class(e),
+ NetError::AcceptTaskOngoing => "Busy",
+ NetError::RootCertStore(e) | NetError::Resource(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ NetError::Permission(e) => get_permission_check_error_class(e),
+ NetError::NoResolvedAddress => "Error",
+ NetError::AddrParse(_) => "Error",
+ NetError::Map(e) => get_net_map_error(e),
+ NetError::Canceled(e) => {
+ let io_err: io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ NetError::DnsNotFound(_) => "NotFound",
+ NetError::DnsNotConnected(_) => "NotConnected",
+ NetError::DnsTimedOut(_) => "TimedOut",
+ NetError::Dns(_) => "Error",
+ NetError::UnsupportedRecordType => "NotSupported",
+ NetError::InvalidUtf8(_) => "InvalidData",
+ NetError::UnexpectedKeyType => "Error",
+ NetError::InvalidHostname(_) => "TypeError",
+ NetError::TcpStreamBusy => "Busy",
+ NetError::Rustls(_) => "Error",
+ NetError::Tls(e) => get_tls_error_class(e),
+ NetError::ListenTlsRequiresKey => "InvalidData",
+ NetError::Reunite(_) => "Error",
+ }
+}
+
+fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str {
+ match error {
+ deno_net::io::MapError::Io(e) => get_io_error_class(e),
+ deno_net::io::MapError::NoResources => "Error",
+ }
+}
+
+fn get_child_permission_error(e: &ChildPermissionError) -> &'static str {
+ match e {
+ ChildPermissionError::Escalation => "NotCapable",
+ ChildPermissionError::PathResolve(e) => get_path_resolve_error(e),
+ ChildPermissionError::NetDescriptorParse(_) => "URIError",
+ ChildPermissionError::EnvDescriptorParse(_) => "Error",
+ ChildPermissionError::SysDescriptorParse(e) => {
+ get_sys_descriptor_parse_error(e)
+ }
+ ChildPermissionError::RunDescriptorParse(e) => {
+ get_run_descriptor_parse_error(e)
+ }
+ }
+}
+
+fn get_create_worker_error(error: &CreateWorkerError) -> &'static str {
+ match error {
+ CreateWorkerError::ClassicWorkers => "DOMExceptionNotSupportedError",
+ CreateWorkerError::Permission(e) => get_child_permission_error(e),
+ CreateWorkerError::ModuleResolution(e) => {
+ get_module_resolution_error_class(e)
+ }
+ CreateWorkerError::Io(e) => get_io_error_class(e),
+ CreateWorkerError::MessagePort(e) => get_web_message_port_error_class(e),
+ }
+}
+
+fn get_tty_error(error: &TtyError) -> &'static str {
+ match error {
+ TtyError::Resource(e) | TtyError::Other(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ TtyError::Io(e) => get_io_error_class(e),
+ #[cfg(unix)]
+ TtyError::Nix(e) => get_nix_error_class(e),
+ }
+}
+
+fn get_readline_error(error: &ReadlineError) -> &'static str {
+ match error {
+ ReadlineError::Io(e) => get_io_error_class(e),
+ ReadlineError::Eof => "Error",
+ ReadlineError::Interrupted => "Error",
+ #[cfg(unix)]
+ ReadlineError::Errno(e) => get_nix_error_class(e),
+ ReadlineError::WindowResized => "Error",
+ #[cfg(windows)]
+ ReadlineError::Decode(_) => "Error",
+ #[cfg(windows)]
+ ReadlineError::SystemError(_) => "Error",
+ _ => "Error",
+ }
+}
+
+fn get_signal_error(error: &SignalError) -> &'static str {
+ match error {
+ SignalError::InvalidSignalStr(_) => "TypeError",
+ SignalError::InvalidSignalInt(_) => "TypeError",
+ SignalError::SignalNotAllowed(_) => "TypeError",
+ SignalError::Io(e) => get_io_error_class(e),
+ }
+}
+
+fn get_fs_events_error(error: &FsEventsError) -> &'static str {
+ match error {
+ FsEventsError::Resource(e) => get_error_class_name(e).unwrap_or("Error"),
+ FsEventsError::Permission(e) => get_permission_check_error_class(e),
+ FsEventsError::Notify(e) => get_notify_error_class(e),
+ FsEventsError::Canceled(e) => {
+ let io_err: io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ }
+}
+
+fn get_http_start_error(error: &HttpStartError) -> &'static str {
+ match error {
+ HttpStartError::TcpStreamInUse => "Busy",
+ HttpStartError::TlsStreamInUse => "Busy",
+ HttpStartError::UnixSocketInUse => "Busy",
+ HttpStartError::ReuniteTcp(_) => "Error",
+ #[cfg(unix)]
+ HttpStartError::ReuniteUnix(_) => "Error",
+ HttpStartError::Io(e) => get_io_error_class(e),
+ HttpStartError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
+ }
+}
+
+fn get_process_error(error: &ProcessError) -> &'static str {
+ match error {
+ ProcessError::SpawnFailed { error, .. } => get_process_error(error),
+ ProcessError::FailedResolvingCwd(e) | ProcessError::Io(e) => {
+ get_io_error_class(e)
+ }
+ ProcessError::Permission(e) => get_permission_check_error_class(e),
+ ProcessError::Resource(e) => get_error_class_name(e).unwrap_or("Error"),
+ ProcessError::BorrowMut(_) => "Error",
+ ProcessError::Which(_) => "Error",
+ ProcessError::ChildProcessAlreadyTerminated => "TypeError",
+ ProcessError::Signal(e) => get_signal_error(e),
+ ProcessError::MissingCmd => "Error",
+ ProcessError::InvalidPid => "TypeError",
+ #[cfg(unix)]
+ ProcessError::Nix(e) => get_nix_error_class(e),
+ ProcessError::RunPermission(e) => match e {
+ CheckRunPermissionError::Permission(e) => {
+ get_permission_check_error_class(e)
+ }
+ CheckRunPermissionError::Other(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ },
+ }
+}
+
+fn get_http_error(error: &HttpError) -> &'static str {
+ match error {
+ HttpError::Canceled(e) => {
+ let io_err: io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ HttpError::HyperV014(e) => get_hyper_v014_error_class(e),
+ HttpError::InvalidHeaderName(_) => "Error",
+ HttpError::InvalidHeaderValue(_) => "Error",
+ HttpError::Http(_) => "Error",
+ HttpError::ResponseHeadersAlreadySent => "Http",
+ HttpError::ConnectionClosedWhileSendingResponse => "Http",
+ HttpError::AlreadyInUse => "Http",
+ HttpError::Io(e) => get_io_error_class(e),
+ HttpError::NoResponseHeaders => "Http",
+ HttpError::ResponseAlreadyCompleted => "Http",
+ HttpError::UpgradeBodyUsed => "Http",
+ HttpError::Resource(e) | HttpError::Other(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ }
+}
+
+fn get_http_next_error(error: &HttpNextError) -> &'static str {
+ match error {
+ HttpNextError::Io(e) => get_io_error_class(e),
+ HttpNextError::WebSocketUpgrade(e) => get_websocket_upgrade_error(e),
+ HttpNextError::Hyper(e) => get_hyper_error_class(e),
+ HttpNextError::JoinError(_) => "Error",
+ HttpNextError::Canceled(e) => {
+ let io_err: io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ HttpNextError::UpgradeUnavailable(_) => "Error",
+ HttpNextError::HttpPropertyExtractor(e) | HttpNextError::Resource(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ }
+}
+
+fn get_websocket_upgrade_error(error: &WebSocketUpgradeError) -> &'static str {
+ match error {
+ WebSocketUpgradeError::InvalidHeaders => "Http",
+ WebSocketUpgradeError::HttpParse(_) => "Error",
+ WebSocketUpgradeError::Http(_) => "Error",
+ WebSocketUpgradeError::Utf8(_) => "Error",
+ WebSocketUpgradeError::InvalidHeaderName(_) => "Error",
+ WebSocketUpgradeError::InvalidHeaderValue(_) => "Error",
+ WebSocketUpgradeError::InvalidHttpStatusLine => "Http",
+ WebSocketUpgradeError::UpgradeBufferAlreadyCompleted => "Http",
+ }
+}
+
+fn get_fs_error(e: &FsError) -> &'static str {
+ match &e {
+ FsError::Io(e) => get_io_error_class(e),
+ FsError::FileBusy => "Busy",
+ FsError::NotSupported => "NotSupported",
+ FsError::NotCapable(_) => "NotCapable",
+ }
+}
+
+mod node {
+ use super::get_error_class_name;
+ use super::get_io_error_class;
+ use super::get_permission_check_error_class;
+ use super::get_serde_json_error_class;
+ use super::get_url_parse_error_class;
+ pub use deno_node::ops::blocklist::BlocklistError;
+ pub use deno_node::ops::crypto::cipher::CipherContextError;
+ pub use deno_node::ops::crypto::cipher::CipherError;
+ pub use deno_node::ops::crypto::cipher::DecipherContextError;
+ pub use deno_node::ops::crypto::cipher::DecipherError;
+ pub use deno_node::ops::crypto::digest::HashError;
+ pub use deno_node::ops::crypto::keys::AsymmetricPrivateKeyDerError;
+ pub use deno_node::ops::crypto::keys::AsymmetricPrivateKeyError;
+ pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyDerError;
+ pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyError;
+ pub use deno_node::ops::crypto::keys::AsymmetricPublicKeyJwkError;
+ pub use deno_node::ops::crypto::keys::EcJwkError;
+ pub use deno_node::ops::crypto::keys::EdRawError;
+ pub use deno_node::ops::crypto::keys::ExportPrivateKeyPemError;
+ pub use deno_node::ops::crypto::keys::ExportPublicKeyPemError;
+ pub use deno_node::ops::crypto::keys::GenerateRsaPssError;
+ pub use deno_node::ops::crypto::keys::RsaJwkError;
+ pub use deno_node::ops::crypto::keys::RsaPssParamsParseError;
+ pub use deno_node::ops::crypto::keys::X509PublicKeyError;
+ pub use deno_node::ops::crypto::sign::KeyObjectHandlePrehashedSignAndVerifyError;
+ pub use deno_node::ops::crypto::x509::X509Error;
+ pub use deno_node::ops::crypto::DiffieHellmanError;
+ pub use deno_node::ops::crypto::EcdhEncodePubKey;
+ pub use deno_node::ops::crypto::HkdfError;
+ pub use deno_node::ops::crypto::Pbkdf2Error;
+ pub use deno_node::ops::crypto::PrivateEncryptDecryptError;
+ pub use deno_node::ops::crypto::ScryptAsyncError;
+ pub use deno_node::ops::crypto::SignEd25519Error;
+ pub use deno_node::ops::crypto::VerifyEd25519Error;
+ pub use deno_node::ops::fs::FsError;
+ pub use deno_node::ops::http2::Http2Error;
+ pub use deno_node::ops::idna::IdnaError;
+ pub use deno_node::ops::ipc::IpcError;
+ pub use deno_node::ops::ipc::IpcJsonStreamError;
+ use deno_node::ops::os::priority::PriorityError;
+ pub use deno_node::ops::os::OsError;
+ pub use deno_node::ops::require::RequireError;
+ use deno_node::ops::require::RequireErrorKind;
+ pub use deno_node::ops::worker_threads::WorkerThreadsFilenameError;
+ pub use deno_node::ops::zlib::brotli::BrotliError;
+ pub use deno_node::ops::zlib::mode::ModeError;
+ pub use deno_node::ops::zlib::ZlibError;
+
+ pub fn get_blocklist_error(error: &BlocklistError) -> &'static str {
+ match error {
+ BlocklistError::AddrParse(_) => "Error",
+ BlocklistError::IpNetwork(_) => "Error",
+ BlocklistError::InvalidAddress => "Error",
+ BlocklistError::IpVersionMismatch => "Error",
+ }
+ }
+
+ pub fn get_fs_error(error: &FsError) -> &'static str {
+ match error {
+ FsError::Permission(e) => get_permission_check_error_class(e),
+ FsError::Io(e) => get_io_error_class(e),
+ #[cfg(windows)]
+ FsError::PathHasNoRoot => "Error",
+ #[cfg(not(any(unix, windows)))]
+ FsError::UnsupportedPlatform => "Error",
+ FsError::Fs(e) => super::get_fs_error(e),
+ }
+ }
+
+ pub fn get_idna_error(error: &IdnaError) -> &'static str {
+ match error {
+ IdnaError::InvalidInput => "RangeError",
+ IdnaError::InputTooLong => "Error",
+ IdnaError::IllegalInput => "RangeError",
+ }
+ }
+
+ pub fn get_ipc_json_stream_error(error: &IpcJsonStreamError) -> &'static str {
+ match error {
+ IpcJsonStreamError::Io(e) => get_io_error_class(e),
+ IpcJsonStreamError::SimdJson(_) => "Error",
+ }
+ }
+
+ pub fn get_ipc_error(error: &IpcError) -> &'static str {
+ match error {
+ IpcError::Resource(e) => get_error_class_name(e).unwrap_or("Error"),
+ IpcError::IpcJsonStream(e) => get_ipc_json_stream_error(e),
+ IpcError::Canceled(e) => {
+ let io_err: std::io::Error = e.to_owned().into();
+ get_io_error_class(&io_err)
+ }
+ IpcError::SerdeJson(e) => get_serde_json_error_class(e),
+ }
+ }
+
+ pub fn get_worker_threads_filename_error(
+ error: &WorkerThreadsFilenameError,
+ ) -> &'static str {
+ match error {
+ WorkerThreadsFilenameError::Permission(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ WorkerThreadsFilenameError::UrlParse(e) => get_url_parse_error_class(e),
+ WorkerThreadsFilenameError::InvalidRelativeUrl => "Error",
+ WorkerThreadsFilenameError::UrlFromPathString => "Error",
+ WorkerThreadsFilenameError::UrlToPathString => "Error",
+ WorkerThreadsFilenameError::UrlToPath => "Error",
+ WorkerThreadsFilenameError::FileNotFound(_) => "Error",
+ WorkerThreadsFilenameError::Fs(e) => super::get_fs_error(e),
+ }
+ }
+
+ pub fn get_require_error(error: &RequireError) -> &'static str {
+ use RequireErrorKind::*;
+ match error.as_kind() {
+ UrlParse(e) => get_url_parse_error_class(e),
+ Permission(e) => get_error_class_name(e).unwrap_or("Error"),
+ PackageExportsResolve(_)
+ | PackageJsonLoad(_)
+ | ClosestPkgJson(_)
+ | FilePathConversion(_)
+ | UrlConversion(_)
+ | ReadModule(_)
+ | PackageImportsResolve(_) => "Error",
+ Fs(e) | UnableToGetCwd(e) => super::get_fs_error(e),
+ }
+ }
+
+ pub fn get_http2_error(error: &Http2Error) -> &'static str {
+ match error {
+ Http2Error::Resource(e) => get_error_class_name(e).unwrap_or("Error"),
+ Http2Error::UrlParse(e) => get_url_parse_error_class(e),
+ Http2Error::H2(_) => "Error",
+ }
+ }
+
+ pub fn get_os_error(error: &OsError) -> &'static str {
+ match error {
+ OsError::Priority(e) => match e {
+ PriorityError::Io(e) => get_io_error_class(e),
+ #[cfg(windows)]
+ PriorityError::InvalidPriority => "TypeError",
+ },
+ OsError::Permission(e) => get_permission_check_error_class(e),
+ OsError::FailedToGetCpuInfo => "TypeError",
+ OsError::FailedToGetUserInfo(e) => get_io_error_class(e),
+ }
+ }
+
+ pub fn get_brotli_error(error: &BrotliError) -> &'static str {
+ match error {
+ BrotliError::InvalidEncoderMode => "TypeError",
+ BrotliError::CompressFailed => "TypeError",
+ BrotliError::DecompressFailed => "TypeError",
+ BrotliError::Join(_) => "Error",
+ BrotliError::Resource(e) => get_error_class_name(e).unwrap_or("Error"),
+ BrotliError::Io(e) => get_io_error_class(e),
+ }
+ }
+
+ pub fn get_mode_error(_: &ModeError) -> &'static str {
+ "Error"
+ }
+
+ pub fn get_zlib_error(e: &ZlibError) -> &'static str {
+ match e {
+ ZlibError::NotInitialized => "TypeError",
+ ZlibError::Mode(e) => get_mode_error(e),
+ ZlibError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
+ }
+ }
+
+ pub fn get_crypto_cipher_context_error(
+ e: &CipherContextError,
+ ) -> &'static str {
+ match e {
+ CipherContextError::ContextInUse => "TypeError",
+ CipherContextError::Cipher(e) => get_crypto_cipher_error(e),
+ CipherContextError::Resource(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ }
+ }
+
+ pub fn get_crypto_cipher_error(e: &CipherError) -> &'static str {
+ match e {
+ CipherError::InvalidIvLength => "TypeError",
+ CipherError::InvalidKeyLength => "RangeError",
+ CipherError::InvalidInitializationVector => "TypeError",
+ CipherError::CannotPadInputData => "TypeError",
+ CipherError::UnknownCipher(_) => "TypeError",
+ }
+ }
+
+ pub fn get_crypto_decipher_context_error(
+ e: &DecipherContextError,
+ ) -> &'static str {
+ match e {
+ DecipherContextError::ContextInUse => "TypeError",
+ DecipherContextError::Decipher(e) => get_crypto_decipher_error(e),
+ DecipherContextError::Resource(e) => {
+ get_error_class_name(e).unwrap_or("Error")
+ }
+ }
+ }
+
+ pub fn get_crypto_decipher_error(e: &DecipherError) -> &'static str {
+ match e {
+ DecipherError::InvalidIvLength => "TypeError",
+ DecipherError::InvalidKeyLength => "RangeError",
+ DecipherError::InvalidInitializationVector => "TypeError",
+ DecipherError::CannotUnpadInputData => "TypeError",
+ DecipherError::DataAuthenticationFailed => "TypeError",
+ DecipherError::SetAutoPaddingFalseAes128GcmUnsupported => "TypeError",
+ DecipherError::SetAutoPaddingFalseAes256GcmUnsupported => "TypeError",
+ DecipherError::UnknownCipher(_) => "TypeError",
+ }
+ }
+
+ pub fn get_x509_error(_: &X509Error) -> &'static str {
+ "Error"
+ }
+
+ pub fn get_crypto_key_object_handle_prehashed_sign_and_verify_error(
+ e: &KeyObjectHandlePrehashedSignAndVerifyError,
+ ) -> &'static str {
+ match e {
+ KeyObjectHandlePrehashedSignAndVerifyError::InvalidDsaSignatureEncoding => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPrivate => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaSignature(_) => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsa => "Error",
+ KeyObjectHandlePrehashedSignAndVerifyError::DigestNotAllowedForRsaPssSignature(_) => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithRsaPss => "Error",
+ KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigestWithDsa => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::RsaPssHashAlgorithmUnsupported => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::PrivateKeyDisallowsUsage { .. } => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::FailedToSignDigest => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForSigning => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedSigning => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForSigning => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::KeyIsNotPublicOrPrivate => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::InvalidDsaSignature => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::X25519KeyCannotBeUsedForVerification => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::Ed25519KeyCannotBeUsedForPrehashedVerification => "TypeError",
+ KeyObjectHandlePrehashedSignAndVerifyError::DhKeyCannotBeUsedForVerification => "TypeError",
+ }
+ }
+
+ pub fn get_crypto_hash_error(_: &HashError) -> &'static str {
+ "Error"
+ }
+
+ pub fn get_asymmetric_public_key_jwk_error(
+ e: &AsymmetricPublicKeyJwkError,
+ ) -> &'static str {
+ match e {
+ AsymmetricPublicKeyJwkError::UnsupportedJwkEcCurveP224 => "TypeError",
+ AsymmetricPublicKeyJwkError::JwkExportNotImplementedForKeyType => {
+ "TypeError"
+ }
+ AsymmetricPublicKeyJwkError::KeyIsNotAsymmetricPublicKey => "TypeError",
+ }
+ }
+
+ pub fn get_generate_rsa_pss_error(_: &GenerateRsaPssError) -> &'static str {
+ "TypeError"
+ }
+
+ pub fn get_asymmetric_private_key_der_error(
+ e: &AsymmetricPrivateKeyDerError,
+ ) -> &'static str {
+ match e {
+ AsymmetricPrivateKeyDerError::KeyIsNotAsymmetricPrivateKey => "TypeError",
+ AsymmetricPrivateKeyDerError::InvalidRsaPrivateKey => "TypeError",
+ AsymmetricPrivateKeyDerError::ExportingNonRsaPrivateKeyAsPkcs1Unsupported => "TypeError",
+ AsymmetricPrivateKeyDerError::InvalidEcPrivateKey => "TypeError",
+ AsymmetricPrivateKeyDerError::ExportingNonEcPrivateKeyAsSec1Unsupported => "TypeError",
+ AsymmetricPrivateKeyDerError::ExportingNonRsaPssPrivateKeyAsPkcs8Unsupported => "Error",
+ AsymmetricPrivateKeyDerError::InvalidDsaPrivateKey => "TypeError",
+ AsymmetricPrivateKeyDerError::InvalidX25519PrivateKey => "TypeError",
+ AsymmetricPrivateKeyDerError::InvalidEd25519PrivateKey => "TypeError",
+ AsymmetricPrivateKeyDerError::InvalidDhPrivateKey => "TypeError",
+ AsymmetricPrivateKeyDerError::UnsupportedKeyType(_) => "TypeError",
+ }
+ }
+
+ pub fn get_asymmetric_public_key_der_error(
+ _: &AsymmetricPublicKeyDerError,
+ ) -> &'static str {
+ "TypeError"
+ }
+
+ pub fn get_export_public_key_pem_error(
+ e: &ExportPublicKeyPemError,
+ ) -> &'static str {
+ match e {
+ ExportPublicKeyPemError::AsymmetricPublicKeyDer(e) => {
+ get_asymmetric_public_key_der_error(e)
+ }
+ ExportPublicKeyPemError::VeryLargeData => "TypeError",
+ ExportPublicKeyPemError::Der(_) => "Error",
+ }
+ }
+
+ pub fn get_export_private_key_pem_error(
+ e: &ExportPrivateKeyPemError,
+ ) -> &'static str {
+ match e {
+ ExportPrivateKeyPemError::AsymmetricPublicKeyDer(e) => {
+ get_asymmetric_private_key_der_error(e)
+ }
+ ExportPrivateKeyPemError::VeryLargeData => "TypeError",
+ ExportPrivateKeyPemError::Der(_) => "Error",
+ }
+ }
+
+ pub fn get_x509_public_key_error(e: &X509PublicKeyError) -> &'static str {
+ match e {
+ X509PublicKeyError::X509(_) => "Error",
+ X509PublicKeyError::Rsa(_) => "Error",
+ X509PublicKeyError::Asn1(_) => "Error",
+ X509PublicKeyError::Ec(_) => "Error",
+ X509PublicKeyError::UnsupportedEcNamedCurve => "TypeError",
+ X509PublicKeyError::MissingEcParameters => "TypeError",
+ X509PublicKeyError::MalformedDssPublicKey => "TypeError",
+ X509PublicKeyError::UnsupportedX509KeyType => "TypeError",
+ }
+ }
+
+ pub fn get_rsa_jwk_error(e: &RsaJwkError) -> &'static str {
+ match e {
+ RsaJwkError::Base64(_) => "Error",
+ RsaJwkError::Rsa(_) => "Error",
+ RsaJwkError::MissingRsaPrivateComponent => "TypeError",
+ }
+ }
+
+ pub fn get_ec_jwk_error(e: &EcJwkError) -> &'static str {
+ match e {
+ EcJwkError::Ec(_) => "Error",
+ EcJwkError::UnsupportedCurve(_) => "TypeError",
+ }
+ }
+
+ pub fn get_ed_raw_error(e: &EdRawError) -> &'static str {
+ match e {
+ EdRawError::Ed25519Signature(_) => "Error",
+ EdRawError::InvalidEd25519Key => "TypeError",
+ EdRawError::UnsupportedCurve => "TypeError",
+ }
+ }
+
+ pub fn get_pbkdf2_error(e: &Pbkdf2Error) -> &'static str {
+ match e {
+ Pbkdf2Error::UnsupportedDigest(_) => "TypeError",
+ Pbkdf2Error::Join(_) => "Error",
+ }
+ }
+
+ pub fn get_scrypt_async_error(e: &ScryptAsyncError) -> &'static str {
+ match e {
+ ScryptAsyncError::Join(_) => "Error",
+ ScryptAsyncError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
+ }
+ }
+
+ pub fn get_hkdf_error_error(e: &HkdfError) -> &'static str {
+ match e {
+ HkdfError::ExpectedSecretKey => "TypeError",
+ HkdfError::HkdfExpandFailed => "TypeError",
+ HkdfError::UnsupportedDigest(_) => "TypeError",
+ HkdfError::Join(_) => "Error",
+ }
+ }
+
+ pub fn get_rsa_pss_params_parse_error(
+ _: &RsaPssParamsParseError,
+ ) -> &'static str {
+ "TypeError"
+ }
+
+ pub fn get_asymmetric_private_key_error(
+ e: &AsymmetricPrivateKeyError,
+ ) -> &'static str {
+ match e {
+ AsymmetricPrivateKeyError::InvalidPemPrivateKeyInvalidUtf8(_) => "TypeError",
+ AsymmetricPrivateKeyError::InvalidEncryptedPemPrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::InvalidPemPrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::EncryptedPrivateKeyRequiresPassphraseToDecrypt => "TypeError",
+ AsymmetricPrivateKeyError::InvalidPkcs1PrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::InvalidSec1PrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::UnsupportedPemLabel(_) => "TypeError",
+ AsymmetricPrivateKeyError::RsaPssParamsParse(e) => get_rsa_pss_params_parse_error(e),
+ AsymmetricPrivateKeyError::InvalidEncryptedPkcs8PrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::InvalidPkcs8PrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::Pkcs1PrivateKeyDoesNotSupportEncryptionWithPassphrase => "TypeError",
+ AsymmetricPrivateKeyError::Sec1PrivateKeyDoesNotSupportEncryptionWithPassphrase => "TypeError",
+ AsymmetricPrivateKeyError::UnsupportedEcNamedCurve => "TypeError",
+ AsymmetricPrivateKeyError::InvalidPrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::InvalidDsaPrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::MalformedOrMissingNamedCurveInEcParameters => "TypeError",
+ AsymmetricPrivateKeyError::UnsupportedKeyType(_) => "TypeError",
+ AsymmetricPrivateKeyError::UnsupportedKeyFormat(_) => "TypeError",
+ AsymmetricPrivateKeyError::InvalidX25519PrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::X25519PrivateKeyIsWrongLength => "TypeError",
+ AsymmetricPrivateKeyError::InvalidEd25519PrivateKey => "TypeError",
+ AsymmetricPrivateKeyError::MissingDhParameters => "TypeError",
+ AsymmetricPrivateKeyError::UnsupportedPrivateKeyOid => "TypeError",
+ }
+ }
+
+ pub fn get_asymmetric_public_key_error(
+ e: &AsymmetricPublicKeyError,
+ ) -> &'static str {
+ match e {
+ AsymmetricPublicKeyError::InvalidPemPrivateKeyInvalidUtf8(_) => {
+ "TypeError"
+ }
+ AsymmetricPublicKeyError::InvalidPemPublicKey => "TypeError",
+ AsymmetricPublicKeyError::InvalidPkcs1PublicKey => "TypeError",
+ AsymmetricPublicKeyError::AsymmetricPrivateKey(e) => {
+ get_asymmetric_private_key_error(e)
+ }
+ AsymmetricPublicKeyError::InvalidX509Certificate => "TypeError",
+ AsymmetricPublicKeyError::X509(_) => "Error",
+ AsymmetricPublicKeyError::X509PublicKey(e) => {
+ get_x509_public_key_error(e)
+ }
+ AsymmetricPublicKeyError::UnsupportedPemLabel(_) => "TypeError",
+ AsymmetricPublicKeyError::InvalidSpkiPublicKey => "TypeError",
+ AsymmetricPublicKeyError::UnsupportedKeyType(_) => "TypeError",
+ AsymmetricPublicKeyError::UnsupportedKeyFormat(_) => "TypeError",
+ AsymmetricPublicKeyError::Spki(_) => "Error",
+ AsymmetricPublicKeyError::Pkcs1(_) => "Error",
+ AsymmetricPublicKeyError::RsaPssParamsParse(_) => "TypeError",
+ AsymmetricPublicKeyError::MalformedDssPublicKey => "TypeError",
+ AsymmetricPublicKeyError::MalformedOrMissingNamedCurveInEcParameters => {
+ "TypeError"
+ }
+ AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInEcSpki => {
+ "TypeError"
+ }
+ AsymmetricPublicKeyError::Ec(_) => "Error",
+ AsymmetricPublicKeyError::UnsupportedEcNamedCurve => "TypeError",
+ AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInX25519Spki => {
+ "TypeError"
+ }
+ AsymmetricPublicKeyError::X25519PublicKeyIsTooShort => "TypeError",
+ AsymmetricPublicKeyError::InvalidEd25519PublicKey => "TypeError",
+ AsymmetricPublicKeyError::MissingDhParameters => "TypeError",
+ AsymmetricPublicKeyError::MalformedDhParameters => "TypeError",
+ AsymmetricPublicKeyError::MalformedOrMissingPublicKeyInDhSpki => {
+ "TypeError"
+ }
+ AsymmetricPublicKeyError::UnsupportedPrivateKeyOid => "TypeError",
+ }
+ }
+
+ pub fn get_private_encrypt_decrypt_error(
+ e: &PrivateEncryptDecryptError,
+ ) -> &'static str {
+ match e {
+ PrivateEncryptDecryptError::Pkcs8(_) => "Error",
+ PrivateEncryptDecryptError::Spki(_) => "Error",
+ PrivateEncryptDecryptError::Utf8(_) => "Error",
+ PrivateEncryptDecryptError::Rsa(_) => "Error",
+ PrivateEncryptDecryptError::UnknownPadding => "TypeError",
+ }
+ }
+
+ pub fn get_ecdh_encode_pub_key_error(e: &EcdhEncodePubKey) -> &'static str {
+ match e {
+ EcdhEncodePubKey::InvalidPublicKey => "TypeError",
+ EcdhEncodePubKey::UnsupportedCurve => "TypeError",
+ EcdhEncodePubKey::Sec1(_) => "Error",
+ }
+ }
+
+ pub fn get_diffie_hellman_error(_: &DiffieHellmanError) -> &'static str {
+ "TypeError"
+ }
+
+ pub fn get_sign_ed25519_error(_: &SignEd25519Error) -> &'static str {
+ "TypeError"
+ }
+
+ pub fn get_verify_ed25519_error(_: &VerifyEd25519Error) -> &'static str {
+ "TypeError"
+ }
+}
+
+fn get_os_error(error: &OsError) -> &'static str {
+ match error {
+ OsError::Permission(e) => get_permission_check_error_class(e),
+ OsError::InvalidUtf8(_) => "InvalidData",
+ OsError::EnvEmptyKey => "TypeError",
+ OsError::EnvInvalidKey(_) => "TypeError",
+ OsError::EnvInvalidValue(_) => "TypeError",
+ OsError::Io(e) => get_io_error_class(e),
+ OsError::Var(e) => get_env_var_error_class(e),
+ }
+}
+
+fn get_sync_fetch_error(error: &SyncFetchError) -> &'static str {
+ match error {
+ SyncFetchError::BlobUrlsNotSupportedInContext => "TypeError",
+ SyncFetchError::Io(e) => get_io_error_class(e),
+ SyncFetchError::InvalidScriptUrl => "TypeError",
+ SyncFetchError::InvalidStatusCode(_) => "TypeError",
+ SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(_) => "TypeError",
+ SyncFetchError::InvalidUri(_) => "Error",
+ SyncFetchError::InvalidMimeType(_) => "DOMExceptionNetworkError",
+ SyncFetchError::MissingMimeType => "DOMExceptionNetworkError",
+ SyncFetchError::Fetch(e) => get_fetch_error(e),
+ SyncFetchError::Join(_) => "Error",
+ SyncFetchError::Other(e) => get_error_class_name(e).unwrap_or("Error"),
+ }
+}
+
pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> {
deno_core::error::get_custom_error_class(e)
- .or_else(|| deno_webgpu::error::get_error_class_name(e))
- .or_else(|| deno_web::get_error_class_name(e))
- .or_else(|| deno_webstorage::get_not_supported_error_class_name(e))
- .or_else(|| deno_websocket::get_network_error_class_name(e))
+ .or_else(|| {
+ e.downcast_ref::<ChildPermissionError>()
+ .map(get_child_permission_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<PermissionCheckError>()
+ .map(get_permission_check_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<PermissionError>()
+ .map(get_permission_error_class)
+ })
+ .or_else(|| e.downcast_ref::<FsError>().map(get_fs_error))
+ .or_else(|| {
+ e.downcast_ref::<node::BlocklistError>()
+ .map(node::get_blocklist_error)
+ })
+ .or_else(|| e.downcast_ref::<node::FsError>().map(node::get_fs_error))
+ .or_else(|| {
+ e.downcast_ref::<node::IdnaError>()
+ .map(node::get_idna_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::IpcJsonStreamError>()
+ .map(node::get_ipc_json_stream_error)
+ })
+ .or_else(|| e.downcast_ref::<node::IpcError>().map(node::get_ipc_error))
+ .or_else(|| {
+ e.downcast_ref::<node::WorkerThreadsFilenameError>()
+ .map(node::get_worker_threads_filename_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::RequireError>()
+ .map(node::get_require_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::Http2Error>()
+ .map(node::get_http2_error)
+ })
+ .or_else(|| e.downcast_ref::<node::OsError>().map(node::get_os_error))
+ .or_else(|| {
+ e.downcast_ref::<node::BrotliError>()
+ .map(node::get_brotli_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::ModeError>()
+ .map(node::get_mode_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::ZlibError>()
+ .map(node::get_zlib_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::CipherError>()
+ .map(node::get_crypto_cipher_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::CipherContextError>()
+ .map(node::get_crypto_cipher_context_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::DecipherError>()
+ .map(node::get_crypto_decipher_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::DecipherContextError>()
+ .map(node::get_crypto_decipher_context_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::X509Error>()
+ .map(node::get_x509_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::KeyObjectHandlePrehashedSignAndVerifyError>()
+ .map(node::get_crypto_key_object_handle_prehashed_sign_and_verify_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::HashError>()
+ .map(node::get_crypto_hash_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::AsymmetricPublicKeyJwkError>()
+ .map(node::get_asymmetric_public_key_jwk_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::GenerateRsaPssError>()
+ .map(node::get_generate_rsa_pss_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::AsymmetricPrivateKeyDerError>()
+ .map(node::get_asymmetric_private_key_der_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::AsymmetricPublicKeyDerError>()
+ .map(node::get_asymmetric_public_key_der_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::ExportPublicKeyPemError>()
+ .map(node::get_export_public_key_pem_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::ExportPrivateKeyPemError>()
+ .map(node::get_export_private_key_pem_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::RsaJwkError>()
+ .map(node::get_rsa_jwk_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::EcJwkError>()
+ .map(node::get_ec_jwk_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::EdRawError>()
+ .map(node::get_ed_raw_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::Pbkdf2Error>()
+ .map(node::get_pbkdf2_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::ScryptAsyncError>()
+ .map(node::get_scrypt_async_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::HkdfError>()
+ .map(node::get_hkdf_error_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::RsaPssParamsParseError>()
+ .map(node::get_rsa_pss_params_parse_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::AsymmetricPrivateKeyError>()
+ .map(node::get_asymmetric_private_key_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::AsymmetricPublicKeyError>()
+ .map(node::get_asymmetric_public_key_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::PrivateEncryptDecryptError>()
+ .map(node::get_private_encrypt_decrypt_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::EcdhEncodePubKey>()
+ .map(node::get_ecdh_encode_pub_key_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::DiffieHellmanError>()
+ .map(node::get_diffie_hellman_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::SignEd25519Error>()
+ .map(node::get_sign_ed25519_error)
+ })
+ .or_else(|| {
+ e.downcast_ref::<node::VerifyEd25519Error>()
+ .map(node::get_verify_ed25519_error)
+ })
+ .or_else(|| e.downcast_ref::<NApiError>().map(get_napi_error_class))
+ .or_else(|| e.downcast_ref::<WebError>().map(get_web_error_class))
+ .or_else(|| {
+ e.downcast_ref::<CreateWorkerError>()
+ .map(get_create_worker_error)
+ })
+ .or_else(|| e.downcast_ref::<TtyError>().map(get_tty_error))
+ .or_else(|| e.downcast_ref::<ReadlineError>().map(get_readline_error))
+ .or_else(|| e.downcast_ref::<SignalError>().map(get_signal_error))
+ .or_else(|| e.downcast_ref::<FsEventsError>().map(get_fs_events_error))
+ .or_else(|| e.downcast_ref::<HttpStartError>().map(get_http_start_error))
+ .or_else(|| e.downcast_ref::<ProcessError>().map(get_process_error))
+ .or_else(|| e.downcast_ref::<OsError>().map(get_os_error))
+ .or_else(|| e.downcast_ref::<SyncFetchError>().map(get_sync_fetch_error))
+ .or_else(|| {
+ e.downcast_ref::<CompressionError>()
+ .map(get_web_compression_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<MessagePortError>()
+ .map(get_web_message_port_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<StreamResourceError>()
+ .map(get_web_stream_resource_error_class)
+ })
+ .or_else(|| e.downcast_ref::<BlobError>().map(get_web_blob_error_class))
+ .or_else(|| e.downcast_ref::<IRError>().map(|_| "TypeError"))
+ .or_else(|| e.downcast_ref::<ReprError>().map(get_ffi_repr_error_class))
+ .or_else(|| e.downcast_ref::<HttpError>().map(get_http_error))
+ .or_else(|| e.downcast_ref::<HttpNextError>().map(get_http_next_error))
+ .or_else(|| {
+ e.downcast_ref::<WebSocketUpgradeError>()
+ .map(get_websocket_upgrade_error)
+ })
+ .or_else(|| e.downcast_ref::<FsOpsError>().map(get_fs_ops_error))
+ .or_else(|| {
+ e.downcast_ref::<DlfcnError>()
+ .map(get_ffi_dlfcn_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<StaticError>()
+ .map(get_ffi_static_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<CallbackError>()
+ .map(get_ffi_callback_error_class)
+ })
+ .or_else(|| e.downcast_ref::<CallError>().map(get_ffi_call_error_class))
.or_else(|| e.downcast_ref::<TlsError>().map(get_tls_error_class))
.or_else(|| e.downcast_ref::<CronError>().map(get_cron_error_class))
.or_else(|| e.downcast_ref::<CanvasError>().map(get_canvas_error))
.or_else(|| e.downcast_ref::<CacheError>().map(get_cache_error))
+ .or_else(|| e.downcast_ref::<WebsocketError>().map(get_websocket_error))
+ .or_else(|| {
+ e.downcast_ref::<HandshakeError>()
+ .map(get_websocket_handshake_error)
+ })
+ .or_else(|| e.downcast_ref::<KvError>().map(get_kv_error))
+ .or_else(|| e.downcast_ref::<FetchError>().map(get_fetch_error))
+ .or_else(|| {
+ e.downcast_ref::<HttpClientCreateError>()
+ .map(get_http_client_create_error)
+ })
+ .or_else(|| e.downcast_ref::<NetError>().map(get_net_error))
+ .or_else(|| {
+ e.downcast_ref::<deno_net::io::MapError>()
+ .map(get_net_map_error)
+ })
.or_else(|| {
e.downcast_ref::<BroadcastChannelError>()
.map(get_broadcast_channel_error)
})
.or_else(|| {
+ e.downcast_ref::<deno_webgpu::InitError>()
+ .map(get_webgpu_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_webgpu::buffer::BufferError>()
+ .map(get_webgpu_buffer_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_webgpu::bundle::BundleError>()
+ .map(get_webgpu_bundle_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_webgpu::byow::ByowError>()
+ .map(get_webgpu_byow_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_webgpu::render_pass::RenderPassError>()
+ .map(get_webgpu_render_pass_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_webgpu::surface::SurfaceError>()
+ .map(get_webgpu_surface_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<DecryptError>()
+ .map(get_crypto_decrypt_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<EncryptError>()
+ .map(get_crypto_encrypt_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_crypto::SharedError>()
+ .map(get_crypto_shared_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_crypto::Ed25519Error>()
+ .map(get_crypto_ed25519_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<ExportKeyError>()
+ .map(get_crypto_export_key_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<GenerateKeyError>()
+ .map(get_crypto_generate_key_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<ImportKeyError>()
+ .map(get_crypto_import_key_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_crypto::X448Error>()
+ .map(get_crypto_x448_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_crypto::X25519Error>()
+ .map(get_crypto_x25519_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_crypto::Error>()
+ .map(get_crypto_error_class)
+ })
+ .or_else(|| {
+ e.downcast_ref::<WebStorageError>()
+ .map(get_webstorage_class_name)
+ })
+ .or_else(|| {
+ e.downcast_ref::<deno_url::UrlPatternError>()
+ .map(|_| "TypeError")
+ })
+ .or_else(|| {
e.downcast_ref::<dlopen2::Error>()
.map(get_dlopen_error_class)
})
diff --git a/runtime/examples/extension/main.rs b/runtime/examples/extension/main.rs
index 9889b28dc..1ff16ec83 100644
--- a/runtime/examples/extension/main.rs
+++ b/runtime/examples/extension/main.rs
@@ -50,6 +50,7 @@ async fn main() -> Result<(), AnyError> {
node_services: Default::default(),
npm_process_state_provider: Default::default(),
root_cert_store_provider: Default::default(),
+ fetch_dns_resolver: Default::default(),
shared_array_buffer_store: Default::default(),
compiled_wasm_module_store: Default::default(),
v8_code_cache: Default::default(),
diff --git a/runtime/fmt_errors.rs b/runtime/fmt_errors.rs
index 44a947732..6c05fbc63 100644
--- a/runtime/fmt_errors.rs
+++ b/runtime/fmt_errors.rs
@@ -1,11 +1,10 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
//! This mod provides DenoError to unify errors across Deno.
+use color_print::cformat;
+use color_print::cstr;
use deno_core::error::format_frame;
use deno_core::error::JsError;
-use deno_terminal::colors::cyan;
-use deno_terminal::colors::italic_bold;
-use deno_terminal::colors::red;
-use deno_terminal::colors::yellow;
+use deno_terminal::colors;
use std::fmt::Write as _;
#[derive(Debug, Clone)]
@@ -24,6 +23,7 @@ struct IndexedErrorReference<'a> {
enum FixSuggestionKind {
Info,
Hint,
+ Docs,
}
#[derive(Debug)]
@@ -66,6 +66,13 @@ impl<'a> FixSuggestion<'a> {
message: FixSuggestionMessage::Multiline(messages),
}
}
+
+ pub fn docs(url: &'a str) -> Self {
+ Self {
+ kind: FixSuggestionKind::Docs,
+ message: FixSuggestionMessage::Single(url),
+ }
+ }
}
struct AnsiColors;
@@ -78,10 +85,10 @@ impl deno_core::error::ErrorFormat for AnsiColors {
use deno_core::error::ErrorElement::*;
match element {
Anonymous | NativeFrame | FileName | EvalOrigin => {
- cyan(s).to_string().into()
+ colors::cyan(s).to_string().into()
}
- LineNumber | ColumnNumber => yellow(s).to_string().into(),
- FunctionName | PromiseAll => italic_bold(s).to_string().into(),
+ LineNumber | ColumnNumber => colors::yellow(s).to_string().into(),
+ FunctionName | PromiseAll => colors::italic_bold(s).to_string().into(),
}
}
}
@@ -114,7 +121,7 @@ fn format_maybe_source_line(
if column_number as usize > source_line.len() {
return format!(
"\n{} Couldn't format source line: Column {} is out of bounds (source may have changed at runtime)",
- yellow("Warning"), column_number,
+ colors::yellow("Warning"), column_number,
);
}
@@ -127,9 +134,9 @@ fn format_maybe_source_line(
}
s.push('^');
let color_underline = if is_error {
- red(&s).to_string()
+ colors::red(&s).to_string()
} else {
- cyan(&s).to_string()
+ colors::cyan(&s).to_string()
};
let indent = format!("{:indent$}", "", indent = level);
@@ -200,7 +207,8 @@ fn format_js_error_inner(
if let Some(circular) = &circular {
if js_error.is_same_error(circular.reference.to) {
- write!(s, " {}", cyan(format!("<ref *{}>", circular.index))).unwrap();
+ write!(s, " {}", colors::cyan(format!("<ref *{}>", circular.index)))
+ .unwrap();
}
}
@@ -238,7 +246,8 @@ fn format_js_error_inner(
.unwrap_or(false);
let error_string = if is_caused_by_circular {
- cyan(format!("[Circular *{}]", circular.unwrap().index)).to_string()
+ colors::cyan(format!("[Circular *{}]", circular.unwrap().index))
+ .to_string()
} else {
format_js_error_inner(cause, circular, false, vec![])
};
@@ -255,12 +264,23 @@ fn format_js_error_inner(
for (index, suggestion) in suggestions.iter().enumerate() {
write!(s, " ").unwrap();
match suggestion.kind {
- FixSuggestionKind::Hint => write!(s, "{} ", cyan("hint:")).unwrap(),
- FixSuggestionKind::Info => write!(s, "{} ", yellow("info:")).unwrap(),
+ FixSuggestionKind::Hint => {
+ write!(s, "{} ", colors::cyan("hint:")).unwrap()
+ }
+ FixSuggestionKind::Info => {
+ write!(s, "{} ", colors::yellow("info:")).unwrap()
+ }
+ FixSuggestionKind::Docs => {
+ write!(s, "{} ", colors::green("docs:")).unwrap()
+ }
};
match suggestion.message {
FixSuggestionMessage::Single(msg) => {
- write!(s, "{}", msg).unwrap();
+ if matches!(suggestion.kind, FixSuggestionKind::Docs) {
+ write!(s, "{}", cformat!("<u>{}</>", msg)).unwrap();
+ } else {
+ write!(s, "{}", msg).unwrap();
+ }
}
FixSuggestionMessage::Multiline(messages) => {
for (idx, message) in messages.iter().enumerate() {
@@ -282,28 +302,174 @@ fn format_js_error_inner(
s
}
-/// Format a [`JsError`] for terminal output.
-pub fn format_js_error(js_error: &JsError) -> String {
- let circular =
- find_recursive_cause(js_error).map(|reference| IndexedErrorReference {
- reference,
- index: 1,
- });
+fn get_suggestions_for_terminal_errors(e: &JsError) -> Vec<FixSuggestion> {
+ if let Some(msg) = &e.message {
+ if msg.contains("module is not defined")
+ || msg.contains("exports is not defined")
+ || msg.contains("require is not defined")
+ {
+ return vec![
+ FixSuggestion::info_multiline(&[
+ cstr!("Deno supports CommonJS modules in <u>.cjs</> files, or when the closest"),
+ cstr!("<u>package.json</> has a <i>\"type\": \"commonjs\"</> option.")
+ ]),
+ FixSuggestion::hint_multiline(&[
+ "Rewrite this module to ESM,",
+ cstr!("or change the file extension to <u>.cjs</u>,"),
+ cstr!("or add <u>package.json</> next to the file with <i>\"type\": \"commonjs\"</> option."),
+ ]),
+ FixSuggestion::docs("https://docs.deno.com/go/commonjs"),
+ ];
+ } else if msg.contains("__filename is not defined") {
+ return vec![
+ FixSuggestion::info(cstr!(
+ "<u>__filename</> global is not available in ES modules."
+ )),
+ FixSuggestion::hint(cstr!("Use <u>import.meta.filename</> instead.")),
+ ];
+ } else if msg.contains("__dirname is not defined") {
+ return vec![
+ FixSuggestion::info(cstr!(
+ "<u>__dirname</> global is not available in ES modules."
+ )),
+ FixSuggestion::hint(cstr!("Use <u>import.meta.dirname</> instead.")),
+ ];
+ } else if msg.contains("Buffer is not defined") {
+ return vec![
+ FixSuggestion::info(cstr!(
+ "<u>Buffer</> is not available in the global scope in Deno."
+ )),
+ FixSuggestion::hint_multiline(&[
+ cstr!("Import it explicitly with <u>import { Buffer } from \"node:buffer\";</>,"),
+ cstr!("or run again with <u>--unstable-node-globals</> flag to add this global."),
+ ]),
+ ];
+ } else if msg.contains("clearImmediate is not defined") {
+ return vec![
+ FixSuggestion::info(cstr!(
+ "<u>clearImmediate</> is not available in the global scope in Deno."
+ )),
+ FixSuggestion::hint_multiline(&[
+ cstr!("Import it explicitly with <u>import { clearImmediate } from \"node:timers\";</>,"),
+ cstr!("or run again with <u>--unstable-node-globals</> flag to add this global."),
+ ]),
+ ];
+ } else if msg.contains("setImmediate is not defined") {
+ return vec![
+ FixSuggestion::info(cstr!(
+ "<u>setImmediate</> is not available in the global scope in Deno."
+ )),
+ FixSuggestion::hint_multiline(
+ &[cstr!("Import it explicitly with <u>import { setImmediate } from \"node:timers\";</>,"),
+ cstr!("or run again with <u>--unstable-node-globals</> flag to add this global."),
+ ]),
+ ];
+ } else if msg.contains("global is not defined") {
+ return vec![
+ FixSuggestion::info(cstr!(
+ "<u>global</> is not available in the global scope in Deno."
+ )),
+ FixSuggestion::hint_multiline(&[
+ cstr!("Use <u>globalThis</> instead, or assign <u>globalThis.global = globalThis</>,"),
+ cstr!("or run again with <u>--unstable-node-globals</> flag to add this global."),
+ ]),
+ ];
+ } else if msg.contains("openKv is not a function") {
+ return vec![
+ FixSuggestion::info("Deno.openKv() is an unstable API."),
+ FixSuggestion::hint(
+ "Run again with `--unstable-kv` flag to enable this API.",
+ ),
+ ];
+ } else if msg.contains("cron is not a function") {
+ return vec![
+ FixSuggestion::info("Deno.cron() is an unstable API."),
+ FixSuggestion::hint(
+ "Run again with `--unstable-cron` flag to enable this API.",
+ ),
+ ];
+ } else if msg.contains("WebSocketStream is not defined") {
+ return vec![
+ FixSuggestion::info("new WebSocketStream() is an unstable API."),
+ FixSuggestion::hint(
+ "Run again with `--unstable-net` flag to enable this API.",
+ ),
+ ];
+ } else if msg.contains("Temporal is not defined") {
+ return vec![
+ FixSuggestion::info("Temporal is an unstable API."),
+ FixSuggestion::hint(
+ "Run again with `--unstable-temporal` flag to enable this API.",
+ ),
+ ];
+ } else if msg.contains("BroadcastChannel is not defined") {
+ return vec![
+ FixSuggestion::info("BroadcastChannel is an unstable API."),
+ FixSuggestion::hint(
+ "Run again with `--unstable-broadcast-channel` flag to enable this API.",
+ ),
+ ];
+ } else if msg.contains("window is not defined") {
+ return vec![
+ FixSuggestion::info("window global is not available in Deno 2."),
+ FixSuggestion::hint("Replace `window` with `globalThis`."),
+ ];
+ } else if msg.contains("UnsafeWindowSurface is not a constructor") {
+ return vec![
+ FixSuggestion::info("Deno.UnsafeWindowSurface is an unstable API."),
+ FixSuggestion::hint(
+ "Run again with `--unstable-webgpu` flag to enable this API.",
+ ),
+ ];
+ // Try to capture errors like:
+ // ```
+ // Uncaught Error: Cannot find module '../build/Release/canvas.node'
+ // Require stack:
+ // - /.../deno/npm/registry.npmjs.org/canvas/2.11.2/lib/bindings.js
+ // - /.../.cache/deno/npm/registry.npmjs.org/canvas/2.11.2/lib/canvas.js
+ // ```
+ } else if msg.contains("Cannot find module")
+ && msg.contains("Require stack")
+ && msg.contains(".node'")
+ {
+ return vec![
+ FixSuggestion::info_multiline(
+ &[
+ "Trying to execute an npm package using Node-API addons,",
+ "these packages require local `node_modules` directory to be present."
+ ]
+ ),
+ FixSuggestion::hint_multiline(
+ &[
+ "Add `\"nodeModulesDir\": \"auto\" option to `deno.json`, and then run",
+ "`deno install --allow-scripts=npm:<package> --entrypoint <script>` to setup `node_modules` directory."
+ ]
+ )
+ ];
+ } else if msg.contains("document is not defined") {
+ return vec![
+ FixSuggestion::info(cstr!(
+ "<u>document</> global is not available in Deno."
+ )),
+ FixSuggestion::hint_multiline(&[
+ cstr!("Use a library like <u>happy-dom</>, <u>deno_dom</>, <u>linkedom</> or <u>JSDom</>"),
+ cstr!("and setup the <u>document</> global according to the library documentation."),
+ ]),
+ ];
+ }
+ }
- format_js_error_inner(js_error, circular, true, vec![])
+ vec![]
}
-/// Format a [`JsError`] for terminal output, printing additional suggestions.
-pub fn format_js_error_with_suggestions(
- js_error: &JsError,
- suggestions: Vec<FixSuggestion>,
-) -> String {
+/// Format a [`JsError`] for terminal output.
+pub fn format_js_error(js_error: &JsError) -> String {
let circular =
find_recursive_cause(js_error).map(|reference| IndexedErrorReference {
reference,
index: 1,
});
-
+ let suggestions = get_suggestions_for_terminal_errors(js_error);
format_js_error_inner(js_error, circular, true, suggestions)
}
diff --git a/runtime/inspector_server.rs b/runtime/inspector_server.rs
index 1f8cd5e71..a789dd3dc 100644
--- a/runtime/inspector_server.rs
+++ b/runtime/inspector_server.rs
@@ -19,6 +19,8 @@ use deno_core::serde_json::Value;
use deno_core::unsync::spawn;
use deno_core::url::Url;
use deno_core::InspectorMsg;
+use deno_core::InspectorSessionKind;
+use deno_core::InspectorSessionOptions;
use deno_core::InspectorSessionProxy;
use deno_core::JsRuntime;
use fastwebsockets::Frame;
@@ -192,6 +194,11 @@ fn handle_ws_request(
let inspector_session_proxy = InspectorSessionProxy {
tx: outbound_tx,
rx: inbound_rx,
+ options: InspectorSessionOptions {
+ kind: InspectorSessionKind::NonBlocking {
+ wait_for_disconnect: true,
+ },
+ },
};
log::info!("Debugger session started.");
diff --git a/runtime/js/40_fs_events.js b/runtime/js/40_fs_events.js
index ec2474c0a..322ee6b3c 100644
--- a/runtime/js/40_fs_events.js
+++ b/runtime/js/40_fs_events.js
@@ -21,7 +21,7 @@ class FsWatcher {
constructor(paths, options) {
const { recursive } = options;
- this.#rid = op_fs_events_open({ recursive, paths });
+ this.#rid = op_fs_events_open(recursive, paths);
}
unref() {
diff --git a/runtime/js/90_deno_ns.js b/runtime/js/90_deno_ns.js
index fd2ac00f2..6300f599d 100644
--- a/runtime/js/90_deno_ns.js
+++ b/runtime/js/90_deno_ns.js
@@ -29,6 +29,7 @@ import * as tty from "ext:runtime/40_tty.js";
import * as kv from "ext:deno_kv/01_db.ts";
import * as cron from "ext:deno_cron/01_cron.ts";
import * as webgpuSurface from "ext:deno_webgpu/02_surface.js";
+import * as telemetry from "ext:runtime/telemetry.ts";
const denoNs = {
Process: process.Process,
@@ -134,7 +135,7 @@ const denoNs = {
createHttpClient: httpClient.createHttpClient,
};
-// NOTE(bartlomieju): keep IDs in sync with `cli/main.rs`
+// NOTE(bartlomieju): keep IDs in sync with `runtime/lib.rs`
const unstableIds = {
broadcastChannel: 1,
cron: 2,
@@ -143,11 +144,13 @@ const unstableIds = {
http: 5,
kv: 6,
net: 7,
- process: 8,
- temporal: 9,
- unsafeProto: 10,
- webgpu: 11,
- workerOptions: 12,
+ nodeGlobals: 8,
+ otel: 9,
+ process: 10,
+ temporal: 11,
+ unsafeProto: 12,
+ webgpu: 13,
+ workerOptions: 14,
};
const denoNsUnstableById = { __proto__: null };
@@ -181,4 +184,8 @@ denoNsUnstableById[unstableIds.webgpu] = {
// denoNsUnstableById[unstableIds.workerOptions] = { __proto__: null }
+denoNsUnstableById[unstableIds.otel] = {
+ telemetry: telemetry.telemetry,
+};
+
export { denoNs, denoNsUnstableById, unstableIds };
diff --git a/runtime/js/98_global_scope_shared.js b/runtime/js/98_global_scope_shared.js
index 7a2723899..f8e76b7ce 100644
--- a/runtime/js/98_global_scope_shared.js
+++ b/runtime/js/98_global_scope_shared.js
@@ -32,6 +32,8 @@ import { DOMException } from "ext:deno_web/01_dom_exception.js";
import * as abortSignal from "ext:deno_web/03_abort_signal.js";
import * as imageData from "ext:deno_web/16_image_data.js";
import process from "node:process";
+import Buffer from "node:buffer";
+import { clearImmediate, setImmediate } from "node:timers";
import { loadWebGPU } from "ext:deno_webgpu/00_init.js";
import * as webgpuSurface from "ext:deno_webgpu/02_surface.js";
import { unstableIds } from "ext:runtime/90_deno_ns.js";
@@ -300,4 +302,15 @@ unstableForWindowOrWorkerGlobalScope[unstableIds.net] = {
unstableForWindowOrWorkerGlobalScope[unstableIds.webgpu] = {};
+unstableForWindowOrWorkerGlobalScope[unstableIds.nodeGlobals] = {
+ Buffer: core.propWritable(Buffer),
+ setImmediate: core.propWritable(setImmediate),
+ clearImmediate: core.propWritable(clearImmediate),
+ global: {
+ enumerable: true,
+ configurable: true,
+ get: () => globalThis,
+ },
+};
+
export { unstableForWindowOrWorkerGlobalScope, windowOrWorkerGlobalScope };
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js
index 56a5b411b..eedca3396 100644
--- a/runtime/js/99_main.js
+++ b/runtime/js/99_main.js
@@ -27,7 +27,6 @@ const {
ArrayPrototypeForEach,
ArrayPrototypeIncludes,
ArrayPrototypeMap,
- DateNow,
Error,
ErrorPrototype,
FunctionPrototypeBind,
@@ -87,6 +86,8 @@ import {
workerRuntimeGlobalProperties,
} from "ext:runtime/98_global_scope_worker.js";
import { SymbolDispose, SymbolMetadata } from "ext:deno_web/00_infra.js";
+import { bootstrap as bootstrapOtel } from "ext:runtime/telemetry.ts";
+
// deno-lint-ignore prefer-primordials
if (Symbol.metadata) {
throw "V8 supports Symbol.metadata now, no need to shim it";
@@ -470,6 +471,8 @@ const NOT_IMPORTED_OPS = [
// Related to `Deno.jupyter` API
"op_jupyter_broadcast",
"op_jupyter_input",
+ // Used in jupyter API
+ "op_base64_encode",
// Related to `Deno.test()` API
"op_test_event_step_result_failed",
@@ -574,6 +577,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {
10: serveHost,
11: serveIsMain,
12: serveWorkerCount,
+ 13: otelConfig,
} = runtimeOptions;
if (mode === executionModes.serve) {
@@ -642,7 +646,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {
removeImportedOps();
- performance.setTimeOrigin(DateNow());
+ performance.setTimeOrigin();
globalThis_ = globalThis;
// Remove bootstrapping data from the global scope
@@ -674,9 +678,10 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {
});
ObjectSetPrototypeOf(globalThis, Window.prototype);
+ bootstrapOtel(otelConfig);
+
if (inspectFlag) {
- const consoleFromDeno = globalThis.console;
- core.wrapConsole(consoleFromDeno, core.v8Console);
+ core.wrapConsole(globalThis.console, core.v8Console);
}
event.defineEventHandler(globalThis, "error");
@@ -696,6 +701,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) {
// are lost.
let jupyterNs = undefined;
ObjectDefineProperty(finalDenoNs, "jupyter", {
+ __proto__: null,
get() {
if (jupyterNs) {
return jupyterNs;
@@ -855,9 +861,10 @@ function bootstrapWorkerRuntime(
5: hasNodeModulesDir,
6: argv0,
7: nodeDebug,
+ 13: otelConfig,
} = runtimeOptions;
- performance.setTimeOrigin(DateNow());
+ performance.setTimeOrigin();
globalThis_ = globalThis;
// Remove bootstrapping data from the global scope
@@ -882,8 +889,9 @@ function bootstrapWorkerRuntime(
}
ObjectSetPrototypeOf(globalThis, DedicatedWorkerGlobalScope.prototype);
- const consoleFromDeno = globalThis.console;
- core.wrapConsole(consoleFromDeno, core.v8Console);
+ bootstrapOtel(otelConfig);
+
+ core.wrapConsole(globalThis.console, core.v8Console);
event.defineEventHandler(self, "message");
event.defineEventHandler(self, "error", undefined, true);
diff --git a/runtime/js/telemetry.ts b/runtime/js/telemetry.ts
new file mode 100644
index 000000000..ecef3b5e6
--- /dev/null
+++ b/runtime/js/telemetry.ts
@@ -0,0 +1,720 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+import { core, primordials } from "ext:core/mod.js";
+import {
+ op_crypto_get_random_values,
+ op_otel_instrumentation_scope_create_and_enter,
+ op_otel_instrumentation_scope_enter,
+ op_otel_instrumentation_scope_enter_builtin,
+ op_otel_log,
+ op_otel_span_attribute,
+ 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,
+} from "ext:core/ops";
+import { Console } from "ext:deno_console/01_console.js";
+import { performance } from "ext:deno_web/15_performance.js";
+
+const {
+ SafeWeakMap,
+ Array,
+ ObjectEntries,
+ SafeMap,
+ ReflectApply,
+ SymbolFor,
+ Error,
+ Uint8Array,
+ TypedArrayPrototypeSubarray,
+ ObjectAssign,
+ ObjectDefineProperty,
+ WeakRefPrototypeDeref,
+ String,
+ ObjectPrototypeIsPrototypeOf,
+ DataView,
+ DataViewPrototypeSetUint32,
+ SafeWeakRef,
+ TypedArrayPrototypeGetBuffer,
+} = primordials;
+const { AsyncVariable, setAsyncContext } = core;
+
+let TRACING_ENABLED = false;
+let DETERMINISTIC = false;
+
+enum SpanKind {
+ INTERNAL = 0,
+ SERVER = 1,
+ CLIENT = 2,
+ PRODUCER = 3,
+ CONSUMER = 4,
+}
+
+interface TraceState {
+ set(key: string, value: string): TraceState;
+ unset(key: string): TraceState;
+ get(key: string): string | undefined;
+ serialize(): string;
+}
+
+interface SpanContext {
+ traceId: string;
+ spanId: string;
+ isRemote?: boolean;
+ traceFlags: number;
+ traceState?: TraceState;
+}
+
+type HrTime = [number, number];
+
+enum SpanStatusCode {
+ UNSET = 0,
+ OK = 1,
+ ERROR = 2,
+}
+
+interface SpanStatus {
+ code: SpanStatusCode;
+ message?: string;
+}
+
+export type AttributeValue =
+ | string
+ | number
+ | boolean
+ | Array<null | undefined | string>
+ | Array<null | undefined | number>
+ | Array<null | undefined | boolean>;
+
+interface Attributes {
+ [attributeKey: string]: AttributeValue | undefined;
+}
+
+type SpanAttributes = Attributes;
+
+interface Link {
+ context: SpanContext;
+ attributes?: SpanAttributes;
+ droppedAttributesCount?: number;
+}
+
+interface TimedEvent {
+ time: HrTime;
+ name: string;
+ attributes?: SpanAttributes;
+ droppedAttributesCount?: number;
+}
+
+interface IArrayValue {
+ values: IAnyValue[];
+}
+
+interface IAnyValue {
+ stringValue?: string | null;
+ boolValue?: boolean | null;
+ intValue?: number | null;
+ doubleValue?: number | null;
+ arrayValue?: IArrayValue;
+ kvlistValue?: IKeyValueList;
+ bytesValue?: Uint8Array;
+}
+
+interface IKeyValueList {
+ values: IKeyValue[];
+}
+
+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);
+}
+
+const TRACE_FLAG_SAMPLED = 1 << 0;
+
+const instrumentationScopes = new SafeWeakMap<
+ InstrumentationLibrary,
+ { __key: "instrumentation-library" }
+>();
+let activeInstrumentationLibrary: WeakRef<InstrumentationLibrary> | null = null;
+
+function submit(
+ 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 (!(traceFlags & TRACE_FLAG_SAMPLED)) return;
+
+ // TODO(@lucacasonato): `resource` is ignored for now, should we implement it?
+
+ const instrumentationLibrary = span.instrumentationLibrary;
+ if (
+ !activeInstrumentationLibrary ||
+ WeakRefPrototypeDeref(activeInstrumentationLibrary) !==
+ instrumentationLibrary
+ ) {
+ activeInstrumentationLibrary = new SafeWeakRef(instrumentationLibrary);
+ if (instrumentationLibrary === BUILTIN_INSTRUMENTATION_LIBRARY) {
+ op_otel_instrumentation_scope_enter_builtin();
+ } else {
+ let instrumentationScope = instrumentationScopes
+ .get(instrumentationLibrary);
+
+ if (instrumentationScope === undefined) {
+ instrumentationScope = op_otel_instrumentation_scope_create_and_enter(
+ instrumentationLibrary.name,
+ instrumentationLibrary.version,
+ instrumentationLibrary.schemaUrl,
+ ) as { __key: "instrumentation-library" };
+ instrumentationScopes.set(
+ instrumentationLibrary,
+ instrumentationScope,
+ );
+ } else {
+ op_otel_instrumentation_scope_enter(
+ instrumentationScope,
+ );
+ }
+ }
+ }
+
+ op_otel_span_start(
+ traceId,
+ spanId,
+ parentSpanId,
+ span.kind,
+ span.name,
+ startTime,
+ endTime,
+ );
+
+ const status = span.status;
+ if (status !== null && status.code !== 0) {
+ op_otel_span_continue(status.code, status.message ?? "");
+ }
+
+ const attributeKvs = ObjectEntries(span.attributes);
+ let i = 0;
+ while (i < attributeKvs.length) {
+ if (i + 2 < attributeKvs.length) {
+ op_otel_span_attribute3(
+ attributeKvs.length,
+ attributeKvs[i][0],
+ attributeKvs[i][1],
+ attributeKvs[i + 1][0],
+ attributeKvs[i + 1][1],
+ attributeKvs[i + 2][0],
+ attributeKvs[i + 2][1],
+ );
+ i += 3;
+ } else if (i + 1 < attributeKvs.length) {
+ op_otel_span_attribute2(
+ attributeKvs.length,
+ attributeKvs[i][0],
+ attributeKvs[i][1],
+ attributeKvs[i + 1][0],
+ attributeKvs[i + 1][1],
+ );
+ i += 2;
+ } else {
+ op_otel_span_attribute(
+ attributeKvs.length,
+ attributeKvs[i][0],
+ attributeKvs[i][1],
+ );
+ i += 1;
+ }
+ }
+
+ // TODO(@lucacasonato): implement links
+ // TODO(@lucacasonato): implement events
+
+ const droppedAttributesCount = span.droppedAttributesCount;
+ const droppedLinksCount = span.droppedLinksCount + span.links.length;
+ const droppedEventsCount = span.droppedEventsCount + span.events.length;
+ if (
+ droppedAttributesCount > 0 || droppedLinksCount > 0 ||
+ droppedEventsCount > 0
+ ) {
+ op_otel_span_set_dropped(
+ droppedAttributesCount,
+ droppedLinksCount,
+ droppedEventsCount,
+ );
+ }
+
+ op_otel_span_flush();
+}
+
+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 COUNTER = 1;
+
+export let enterSpan: (span: Span) => void;
+export let exitSpan: (span: Span) => void;
+export let endSpan: (span: Span) => void;
+
+export class Span {
+ #traceId: string | Uint8Array;
+ #spanId: Uint8Array;
+ #traceFlags = TRACE_FLAG_SAMPLED;
+
+ #spanContext: SpanContext | null = null;
+
+ #parentSpanId: string | Uint8Array | null = null;
+ #parentSpanIdString: string | null = null;
+
+ #recording = TRACING_ENABLED;
+
+ #kind: number = 0;
+ #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;
+
+ 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;
+ };
+
+ exitSpan = (span: Span) => {
+ const endTime = now();
+ submit(
+ 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,
+ );
+ };
+ }
+
+ constructor(
+ name: string,
+ attributes?: Attributes,
+ ) {
+ 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 = attributes ?? { __proto__: null } as never;
+
+ const currentSpan: Span | {
+ spanContext(): { traceId: string; spanId: string };
+ } = CURRENT.get()?.getValue(SPAN_KEY);
+ if (!currentSpan) {
+ const buffer = new Uint8Array(TRACE_ID_BYTES + SPAN_ID_BYTES);
+ if (DETERMINISTIC) {
+ DataViewPrototypeSetUint32(
+ new DataView(TypedArrayPrototypeGetBuffer(buffer)),
+ TRACE_ID_BYTES - 4,
+ COUNTER,
+ true,
+ );
+ COUNTER += 1;
+ DataViewPrototypeSetUint32(
+ new DataView(TypedArrayPrototypeGetBuffer(buffer)),
+ TRACE_ID_BYTES + SPAN_ID_BYTES - 4,
+ COUNTER,
+ true,
+ );
+ COUNTER += 1;
+ } else {
+ op_crypto_get_random_values(buffer);
+ }
+ this.#traceId = TypedArrayPrototypeSubarray(buffer, 0, TRACE_ID_BYTES);
+ this.#spanId = TypedArrayPrototypeSubarray(buffer, TRACE_ID_BYTES);
+ } else {
+ this.#spanId = new Uint8Array(SPAN_ID_BYTES);
+ if (DETERMINISTIC) {
+ DataViewPrototypeSetUint32(
+ new DataView(TypedArrayPrototypeGetBuffer(this.#spanId)),
+ SPAN_ID_BYTES - 4,
+ COUNTER,
+ true,
+ );
+ COUNTER += 1;
+ } else {
+ 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;
+ }
+ }
+ }
+
+ 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,
+ };
+ }
+ 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;
+ return this;
+ }
+
+ setAttributes(attributes: Attributes) {
+ if (this.#recording) ObjectAssign(this.#attributes, attributes);
+ 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;
+ }
+ }
+ return this;
+ }
+
+ updateName(name: string) {
+ if (this.#recording) this.#name = name;
+ return this;
+ }
+
+ addEvent(_name: never) {
+ // TODO(@lucacasonato): implement events
+ if (this.#recording) this.#droppedEventsCount += 1;
+ 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();
+ submit(
+ context.spanId,
+ context.traceId,
+ context.traceFlags,
+ span.parentSpanId ?? null,
+ span,
+ hrToSecs(span.startTime),
+ hrToSecs(span.endTime),
+ );
+ }
+ resultCallback({ code: 0 });
+ } catch (error) {
+ resultCallback({
+ code: 1,
+ error: ObjectPrototypeIsPrototypeOf(error, Error)
+ ? error as Error
+ : new Error(String(error)),
+ });
+ }
+ }
+
+ async shutdown() {}
+
+ async forceFlush() {}
+}
+
+const CURRENT = new AsyncVariable();
+
+class Context {
+ #data = new SafeMap();
+
+ // deno-lint-ignore no-explicit-any
+ constructor(data?: Iterable<readonly [any, any]> | null | undefined) {
+ this.#data = data ? new SafeMap(data) : new SafeMap();
+ }
+
+ getValue(key: symbol): unknown {
+ return this.#data.get(key);
+ }
+
+ setValue(key: symbol, value: unknown): Context {
+ const c = new Context(this.#data);
+ c.#data.set(key, value);
+ return c;
+ }
+
+ deleteValue(key: symbol): Context {
+ const c = new Context(this.#data);
+ c.#data.delete(key);
+ return c;
+ }
+}
+
+// TODO(lucacasonato): @opentelemetry/api defines it's own ROOT_CONTEXT
+const ROOT_CONTEXT = new Context();
+
+// Context manager for opentelemetry js library
+class ContextManager {
+ active(): Context {
+ return CURRENT.get() ?? ROOT_CONTEXT;
+ }
+
+ with<A extends unknown[], F extends (...args: A) => ReturnType<F>>(
+ context: Context,
+ fn: F,
+ thisArg?: ThisParameterType<F>,
+ ...args: A
+ ): ReturnType<F> {
+ const ctx = CURRENT.enter(context);
+ try {
+ return ReflectApply(fn, thisArg, args);
+ } finally {
+ setAsyncContext(ctx);
+ }
+ }
+
+ // deno-lint-ignore no-explicit-any
+ bind<T extends (...args: any[]) => any>(
+ context: Context,
+ target: T,
+ ): T {
+ return ((...args) => {
+ const ctx = CURRENT.enter(context);
+ try {
+ return ReflectApply(target, this, args);
+ } finally {
+ setAsyncContext(ctx);
+ }
+ }) as T;
+ }
+
+ enable() {
+ return this;
+ }
+
+ disable() {
+ return this;
+ }
+}
+
+const otelConsoleConfig = {
+ ignore: 0,
+ capture: 1,
+ replace: 2,
+};
+
+export function bootstrap(
+ config: [] | [
+ typeof otelConsoleConfig[keyof typeof otelConsoleConfig],
+ number,
+ ],
+): void {
+ if (config.length === 0) return;
+ const { 0: consoleConfig, 1: deterministic } = config;
+
+ TRACING_ENABLED = true;
+ DETERMINISTIC = deterministic === 1;
+
+ switch (consoleConfig) {
+ case otelConsoleConfig.capture:
+ core.wrapConsole(globalThis.console, new Console(otelLog));
+ break;
+ case otelConsoleConfig.replace:
+ ObjectDefineProperty(
+ globalThis,
+ "console",
+ core.propNonEnumerable(new Console(otelLog)),
+ );
+ break;
+ default:
+ break;
+ }
+}
+
+export const telemetry = { SpanExporter, ContextManager };
diff --git a/runtime/lib.rs b/runtime/lib.rs
index f0b1129ce..a6e60ced1 100644
--- a/runtime/lib.rs
+++ b/runtime/lib.rs
@@ -35,6 +35,7 @@ pub mod js;
pub mod ops;
pub mod permissions;
pub mod snapshot;
+pub mod sys_info;
pub mod tokio_util;
pub mod web_worker;
pub mod worker;
@@ -99,18 +100,30 @@ pub static UNSTABLE_GRANULAR_FLAGS: &[UnstableGranularFlag] = &[
show_in_help: true,
id: 7,
},
+ UnstableGranularFlag {
+ name: "node-globals",
+ help_text: "Expose Node globals everywhere",
+ show_in_help: true,
+ id: 8,
+ },
+ UnstableGranularFlag {
+ name: "otel",
+ help_text: "Enable unstable OpenTelemetry features",
+ show_in_help: false,
+ id: 9,
+ },
// TODO(bartlomieju): consider removing it
UnstableGranularFlag {
name: ops::process::UNSTABLE_FEATURE_NAME,
help_text: "Enable unstable process APIs",
show_in_help: false,
- id: 8,
+ id: 10,
},
UnstableGranularFlag {
name: "temporal",
help_text: "Enable unstable Temporal API",
show_in_help: true,
- id: 9,
+ id: 11,
},
UnstableGranularFlag {
name: "unsafe-proto",
@@ -118,22 +131,28 @@ pub static UNSTABLE_GRANULAR_FLAGS: &[UnstableGranularFlag] = &[
show_in_help: true,
// This number is used directly in the JS code. Search
// for "unstableIds" to see where it's used.
- id: 10,
+ id: 12,
},
UnstableGranularFlag {
name: deno_webgpu::UNSTABLE_FEATURE_NAME,
help_text: "Enable unstable `WebGPU` APIs",
show_in_help: true,
- id: 11,
+ id: 13,
},
UnstableGranularFlag {
name: ops::worker_host::UNSTABLE_FEATURE_NAME,
help_text: "Enable unstable Web Worker APIs",
show_in_help: true,
- id: 12,
+ id: 14,
},
];
+pub fn exit(code: i32) -> ! {
+ crate::ops::otel::flush();
+ #[allow(clippy::disallowed_methods)]
+ std::process::exit(code);
+}
+
#[cfg(test)]
mod test {
use super::*;
diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs
index d88a32d91..c8e0228bc 100644
--- a/runtime/ops/fs_events.rs
+++ b/runtime/ops/fs_events.rs
@@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::AsyncRefCell;
use deno_core::CancelFuture;
@@ -20,13 +19,14 @@ use notify::EventKind;
use notify::RecommendedWatcher;
use notify::RecursiveMode;
use notify::Watcher;
-use serde::Deserialize;
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!(
@@ -35,9 +35,7 @@ deno_core::extension!(
);
struct FsEventsResource {
- #[allow(unused)]
- watcher: RecommendedWatcher,
- receiver: AsyncRefCell<mpsc::Receiver<Result<FsEvent, AnyError>>>,
+ receiver: AsyncRefCell<mpsc::Receiver<Result<FsEvent, NotifyError>>>,
cancel: CancelHandle,
}
@@ -59,7 +57,7 @@ impl Resource for FsEventsResource {
///
/// Feel free to expand this struct as long as you can add tests to demonstrate
/// the complexity.
-#[derive(Serialize, Debug)]
+#[derive(Serialize, Debug, Clone)]
struct FsEvent {
kind: &'static str,
paths: Vec<PathBuf>,
@@ -93,43 +91,102 @@ impl From<NotifyEvent> for FsEvent {
}
}
-#[derive(Deserialize)]
-pub struct OpenArgs {
- recursive: bool,
- paths: Vec<String>,
+type WatchSender = (Vec<String>, mpsc::Sender<Result<FsEvent, NotifyError>>);
+
+struct WatcherState {
+ senders: Arc<Mutex<Vec<WatchSender>>>,
+ watcher: RecommendedWatcher,
}
-#[op2]
-#[smi]
-fn op_fs_events_open(
+fn starts_with_canonicalized(path: &Path, prefix: &str) -> bool {
+ #[allow(clippy::disallowed_methods)]
+ let path = path.canonicalize().ok();
+ #[allow(clippy::disallowed_methods)]
+ let prefix = std::fs::canonicalize(prefix).ok();
+ match (path, prefix) {
+ (Some(path), Some(prefix)) => path.starts_with(prefix),
+ _ => false,
+ }
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum FsEventsError {
+ #[error(transparent)]
+ Resource(deno_core::error::AnyError),
+ #[error(transparent)]
+ Permission(#[from] deno_permissions::PermissionCheckError),
+ #[error(transparent)]
+ Notify(#[from] NotifyError),
+ #[error(transparent)]
+ Canceled(#[from] deno_core::Canceled),
+}
+
+fn start_watcher(
state: &mut OpState,
- #[serde] args: OpenArgs,
-) -> Result<ResourceId, AnyError> {
- let (sender, receiver) = mpsc::channel::<Result<FsEvent, AnyError>>(16);
- let sender = Mutex::new(sender);
- let mut watcher: RecommendedWatcher = Watcher::new(
+ paths: Vec<String>,
+ sender: mpsc::Sender<Result<FsEvent, NotifyError>>,
+) -> Result<(), FsEventsError> {
+ if let Some(watcher) = state.try_borrow_mut::<WatcherState>() {
+ watcher.senders.lock().push((paths, sender));
+ return Ok(());
+ }
+
+ let senders = Arc::new(Mutex::new(vec![(paths, sender)]));
+
+ let sender_clone = senders.clone();
+ let watcher: RecommendedWatcher = Watcher::new(
move |res: Result<NotifyEvent, NotifyError>| {
- let res2 = res.map(FsEvent::from).map_err(AnyError::from);
- let sender = sender.lock();
- // Ignore result, if send failed it means that watcher was already closed,
- // but not all messages have been flushed.
- let _ = sender.try_send(res2);
+ let res2 = res.map(FsEvent::from).map_err(FsEventsError::Notify);
+ 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.
+
+ // Only send the event if the path matches one of the paths that the user is watching
+ if let Ok(event) = &res2 {
+ if paths.iter().any(|path| {
+ event.paths.iter().any(|event_path| {
+ same_file::is_same_file(event_path, path).unwrap_or(false)
+ || starts_with_canonicalized(event_path, path)
+ })
+ }) {
+ let _ = sender.try_send(Ok(event.clone()));
+ }
+ }
+ }
},
Default::default(),
)?;
- let recursive_mode = if args.recursive {
+
+ state.put::<WatcherState>(WatcherState { watcher, senders });
+
+ Ok(())
+}
+
+#[op2]
+#[smi]
+fn op_fs_events_open(
+ state: &mut OpState,
+ recursive: bool,
+ #[serde] paths: Vec<String>,
+) -> Result<ResourceId, FsEventsError> {
+ let (sender, receiver) = mpsc::channel::<Result<FsEvent, NotifyError>>(16);
+
+ start_watcher(state, paths.clone(), sender)?;
+
+ let recursive_mode = if recursive {
RecursiveMode::Recursive
} else {
RecursiveMode::NonRecursive
};
- for path in &args.paths {
+ for path in &paths {
let path = state
.borrow_mut::<PermissionsContainer>()
.check_read(path, "Deno.watchFs()")?;
- watcher.watch(&path, recursive_mode)?;
+
+ let watcher = state.borrow_mut::<WatcherState>();
+ watcher.watcher.watch(&path, recursive_mode)?;
}
let resource = FsEventsResource {
- watcher,
receiver: AsyncRefCell::new(receiver),
cancel: Default::default(),
};
@@ -142,14 +199,18 @@ fn op_fs_events_open(
async fn op_fs_events_poll(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<Option<FsEvent>, AnyError> {
- let resource = state.borrow().resource_table.get::<FsEventsResource>(rid)?;
+) -> Result<Option<FsEvent>, FsEventsError> {
+ let resource = state
+ .borrow()
+ .resource_table
+ .get::<FsEventsResource>(rid)
+ .map_err(FsEventsError::Resource)?;
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(err),
+ Some(Err(err)) => Err(FsEventsError::Notify(err)),
None => Ok(None),
}
}
diff --git a/runtime/ops/http.rs b/runtime/ops/http.rs
index cec8b0ef8..6e3157668 100644
--- a/runtime/ops/http.rs
+++ b/runtime/ops/http.rs
@@ -2,9 +2,6 @@
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::op2;
use deno_core::OpState;
use deno_core::ResourceId;
@@ -16,12 +13,31 @@ pub const UNSTABLE_FEATURE_NAME: &str = "http";
deno_core::extension!(deno_http_runtime, ops = [op_http_start],);
+#[derive(Debug, thiserror::Error)]
+pub enum HttpStartError {
+ #[error("TCP stream is currently in use")]
+ TcpStreamInUse,
+ #[error("TLS stream is currently in use")]
+ TlsStreamInUse,
+ #[error("Unix socket is currently in use")]
+ UnixSocketInUse,
+ #[error(transparent)]
+ ReuniteTcp(#[from] tokio::net::tcp::ReuniteError),
+ #[cfg(unix)]
+ #[error(transparent)]
+ ReuniteUnix(#[from] tokio::net::unix::ReuniteError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[error(transparent)]
+ Other(deno_core::error::AnyError),
+}
+
#[op2(fast)]
#[smi]
fn op_http_start(
state: &mut OpState,
#[smi] tcp_stream_rid: ResourceId,
-) -> Result<ResourceId, AnyError> {
+) -> Result<ResourceId, HttpStartError> {
if let Ok(resource_rc) = state
.resource_table
.take::<TcpStreamResource>(tcp_stream_rid)
@@ -30,11 +46,11 @@ fn op_http_start(
// process of starting a HTTP server on top of this TCP connection, so we just return a Busy error.
// See also: https://github.com/denoland/deno/pull/16242
let resource = Rc::try_unwrap(resource_rc)
- .map_err(|_| custom_error("Busy", "TCP stream is currently in use"))?;
+ .map_err(|_| HttpStartError::TcpStreamInUse)?;
let (read_half, write_half) = resource.into_inner();
let tcp_stream = read_half.reunite(write_half)?;
let addr = tcp_stream.local_addr()?;
- return http_create_conn_resource(state, tcp_stream, addr, "http");
+ return Ok(http_create_conn_resource(state, tcp_stream, addr, "http"));
}
if let Ok(resource_rc) = state
@@ -45,11 +61,11 @@ fn op_http_start(
// process of starting a HTTP server on top of this TLS connection, so we just return a Busy error.
// See also: https://github.com/denoland/deno/pull/16242
let resource = Rc::try_unwrap(resource_rc)
- .map_err(|_| custom_error("Busy", "TLS stream is currently in use"))?;
+ .map_err(|_| HttpStartError::TlsStreamInUse)?;
let (read_half, write_half) = resource.into_inner();
let tls_stream = read_half.unsplit(write_half);
let addr = tls_stream.local_addr()?;
- return http_create_conn_resource(state, tls_stream, addr, "https");
+ return Ok(http_create_conn_resource(state, tls_stream, addr, "https"));
}
#[cfg(unix)]
@@ -61,12 +77,17 @@ fn op_http_start(
// process of starting a HTTP server on top of this UNIX socket, so we just return a Busy error.
// See also: https://github.com/denoland/deno/pull/16242
let resource = Rc::try_unwrap(resource_rc)
- .map_err(|_| custom_error("Busy", "Unix socket is currently in use"))?;
+ .map_err(|_| HttpStartError::UnixSocketInUse)?;
let (read_half, write_half) = resource.into_inner();
let unix_stream = read_half.reunite(write_half)?;
let addr = unix_stream.local_addr()?;
- return http_create_conn_resource(state, unix_stream, addr, "http+unix");
+ return Ok(http_create_conn_resource(
+ state,
+ unix_stream,
+ addr,
+ "http+unix",
+ ));
}
- Err(bad_resource_id())
+ Err(HttpStartError::Other(deno_core::error::bad_resource_id()))
}
diff --git a/runtime/ops/mod.rs b/runtime/ops/mod.rs
index feed5052b..c2e402f33 100644
--- a/runtime/ops/mod.rs
+++ b/runtime/ops/mod.rs
@@ -4,12 +4,12 @@ pub mod bootstrap;
pub mod fs_events;
pub mod http;
pub mod os;
+pub mod otel;
pub mod permissions;
pub mod process;
pub mod runtime;
pub mod signal;
pub mod tty;
-mod utils;
pub mod web_worker;
pub mod worker_host;
diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs
index bd9260e97..74c708c53 100644
--- a/runtime/ops/os/mod.rs
+++ b/runtime/ops/os/mod.rs
@@ -1,9 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use super::utils::into_string;
+use crate::sys_info;
use crate::worker::ExitCode;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::v8;
use deno_core::OpState;
@@ -14,8 +12,6 @@ use serde::Serialize;
use std::collections::HashMap;
use std::env;
-mod sys_info;
-
deno_core::extension!(
deno_os,
ops = [
@@ -73,9 +69,27 @@ deno_core::extension!(
},
);
+#[derive(Debug, thiserror::Error)]
+pub enum OsError {
+ #[error(transparent)]
+ Permission(#[from] deno_permissions::PermissionCheckError),
+ #[error("File name or path {0:?} is not valid UTF-8")]
+ InvalidUtf8(std::ffi::OsString),
+ #[error("Key is an empty string.")]
+ EnvEmptyKey,
+ #[error("Key contains invalid characters: {0:?}")]
+ EnvInvalidKey(String),
+ #[error("Value contains invalid characters: {0:?}")]
+ EnvInvalidValue(String),
+ #[error(transparent)]
+ Var(#[from] env::VarError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+}
+
#[op2]
#[string]
-fn op_exec_path(state: &mut OpState) -> Result<String, AnyError> {
+fn op_exec_path(state: &mut OpState) -> Result<String, OsError> {
let current_exe = env::current_exe().unwrap();
state
.borrow_mut::<PermissionsContainer>()
@@ -83,7 +97,10 @@ fn op_exec_path(state: &mut OpState) -> Result<String, AnyError> {
// normalize path so it doesn't include '.' or '..' components
let path = normalize_path(current_exe);
- into_string(path.into_os_string())
+ path
+ .into_os_string()
+ .into_string()
+ .map_err(OsError::InvalidUtf8)
}
#[op2(fast)]
@@ -91,20 +108,16 @@ fn op_set_env(
state: &mut OpState,
#[string] key: &str,
#[string] value: &str,
-) -> Result<(), AnyError> {
+) -> Result<(), OsError> {
state.borrow_mut::<PermissionsContainer>().check_env(key)?;
if key.is_empty() {
- return Err(type_error("Key is an empty string."));
+ return Err(OsError::EnvEmptyKey);
}
if key.contains(&['=', '\0'] as &[char]) {
- return Err(type_error(format!(
- "Key contains invalid characters: {key:?}"
- )));
+ return Err(OsError::EnvInvalidKey(key.to_string()));
}
if value.contains('\0') {
- return Err(type_error(format!(
- "Value contains invalid characters: {value:?}"
- )));
+ return Err(OsError::EnvInvalidValue(value.to_string()));
}
env::set_var(key, value);
Ok(())
@@ -112,7 +125,9 @@ fn op_set_env(
#[op2]
#[serde]
-fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
+fn op_env(
+ state: &mut OpState,
+) -> Result<HashMap<String, String>, deno_core::error::AnyError> {
state.borrow_mut::<PermissionsContainer>().check_env_all()?;
Ok(env::vars().collect())
}
@@ -122,7 +137,7 @@ fn op_env(state: &mut OpState) -> Result<HashMap<String, String>, AnyError> {
fn op_get_env(
state: &mut OpState,
#[string] key: String,
-) -> Result<Option<String>, AnyError> {
+) -> Result<Option<String>, OsError> {
let skip_permission_check = NODE_ENV_VAR_ALLOWLIST.contains(&key);
if !skip_permission_check {
@@ -130,13 +145,11 @@ fn op_get_env(
}
if key.is_empty() {
- return Err(type_error("Key is an empty string."));
+ return Err(OsError::EnvEmptyKey);
}
if key.contains(&['=', '\0'] as &[char]) {
- return Err(type_error(format!(
- "Key contains invalid characters: {key:?}"
- )));
+ return Err(OsError::EnvInvalidKey(key.to_string()));
}
let r = match env::var(key) {
@@ -150,10 +163,10 @@ fn op_get_env(
fn op_delete_env(
state: &mut OpState,
#[string] key: String,
-) -> Result<(), AnyError> {
+) -> Result<(), OsError> {
state.borrow_mut::<PermissionsContainer>().check_env(&key)?;
if key.is_empty() || key.contains(&['=', '\0'] as &[char]) {
- return Err(type_error("Key contains invalid characters."));
+ return Err(OsError::EnvInvalidKey(key.to_string()));
}
env::remove_var(key);
Ok(())
@@ -173,12 +186,14 @@ fn op_get_exit_code(state: &mut OpState) -> i32 {
#[op2(fast)]
fn op_exit(state: &mut OpState) {
let code = state.borrow::<ExitCode>().get();
- std::process::exit(code)
+ crate::exit(code)
}
#[op2]
#[serde]
-fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
+fn op_loadavg(
+ state: &mut OpState,
+) -> Result<(f64, f64, f64), deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("loadavg", "Deno.loadavg()")?;
@@ -187,7 +202,9 @@ fn op_loadavg(state: &mut OpState) -> Result<(f64, f64, f64), AnyError> {
#[op2]
#[string]
-fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
+fn op_hostname(
+ state: &mut OpState,
+) -> Result<String, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("hostname", "Deno.hostname()")?;
@@ -196,7 +213,9 @@ fn op_hostname(state: &mut OpState) -> Result<String, AnyError> {
#[op2]
#[string]
-fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
+fn op_os_release(
+ state: &mut OpState,
+) -> Result<String, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("osRelease", "Deno.osRelease()")?;
@@ -207,7 +226,7 @@ fn op_os_release(state: &mut OpState) -> Result<String, AnyError> {
#[serde]
fn op_network_interfaces(
state: &mut OpState,
-) -> Result<Vec<NetworkInterface>, AnyError> {
+) -> Result<Vec<NetworkInterface>, OsError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("networkInterfaces", "Deno.networkInterfaces()")?;
@@ -259,7 +278,7 @@ impl From<netif::Interface> for NetworkInterface {
#[serde]
fn op_system_memory_info(
state: &mut OpState,
-) -> Result<Option<sys_info::MemInfo>, AnyError> {
+) -> Result<Option<sys_info::MemInfo>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("systemMemoryInfo", "Deno.systemMemoryInfo()")?;
@@ -269,7 +288,9 @@ fn op_system_memory_info(
#[cfg(not(windows))]
#[op2]
#[smi]
-fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_gid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("gid", "Deno.gid()")?;
@@ -283,7 +304,9 @@ fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
#[cfg(windows)]
#[op2]
#[smi]
-fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_gid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("gid", "Deno.gid()")?;
@@ -293,7 +316,9 @@ fn op_gid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
#[cfg(not(windows))]
#[op2]
#[smi]
-fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_uid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("uid", "Deno.uid()")?;
@@ -307,7 +332,9 @@ fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
#[cfg(windows)]
#[op2]
#[smi]
-fn op_uid(state: &mut OpState) -> Result<Option<u32>, AnyError> {
+fn op_uid(
+ state: &mut OpState,
+) -> Result<Option<u32>, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("uid", "Deno.uid()")?;
@@ -485,7 +512,7 @@ fn rss() -> usize {
}
}
-fn os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
+fn os_uptime(state: &mut OpState) -> Result<u64, deno_core::error::AnyError> {
state
.borrow_mut::<PermissionsContainer>()
.check_sys("osUptime", "Deno.osUptime()")?;
@@ -494,6 +521,8 @@ fn os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
#[op2(fast)]
#[number]
-fn op_os_uptime(state: &mut OpState) -> Result<u64, AnyError> {
+fn op_os_uptime(
+ state: &mut OpState,
+) -> Result<u64, deno_core::error::AnyError> {
os_uptime(state)
}
diff --git a/runtime/ops/otel.rs b/runtime/ops/otel.rs
new file mode 100644
index 000000000..61a7b0ef0
--- /dev/null
+++ b/runtime/ops/otel.rs
@@ -0,0 +1,855 @@
+// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+
+use crate::tokio_util::create_basic_runtime;
+use deno_core::anyhow;
+use deno_core::anyhow::anyhow;
+use deno_core::futures::channel::mpsc;
+use deno_core::futures::channel::mpsc::UnboundedSender;
+use deno_core::futures::future::BoxFuture;
+use deno_core::futures::stream;
+use deno_core::futures::Stream;
+use deno_core::futures::StreamExt;
+use deno_core::op2;
+use deno_core::v8;
+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::trace::SpanContext;
+use opentelemetry::trace::SpanId;
+use opentelemetry::trace::SpanKind;
+use opentelemetry::trace::Status as SpanStatus;
+use opentelemetry::trace::TraceFlags;
+use opentelemetry::trace::TraceId;
+use opentelemetry::Key;
+use opentelemetry::KeyValue;
+use opentelemetry::StringValue;
+use opentelemetry::Value;
+use opentelemetry_otlp::HttpExporterBuilder;
+use opentelemetry_otlp::Protocol;
+use opentelemetry_otlp::WithExportConfig;
+use opentelemetry_otlp::WithHttpConfig;
+use opentelemetry_sdk::export::trace::SpanData;
+use opentelemetry_sdk::logs::BatchLogProcessor;
+use opentelemetry_sdk::logs::LogProcessor as LogProcessorTrait;
+use opentelemetry_sdk::logs::LogRecord;
+use opentelemetry_sdk::trace::BatchSpanProcessor;
+use opentelemetry_sdk::trace::SpanProcessor as SpanProcessorTrait;
+use opentelemetry_sdk::Resource;
+use opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_NAME;
+use opentelemetry_semantic_conventions::resource::PROCESS_RUNTIME_VERSION;
+use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_LANGUAGE;
+use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_NAME;
+use opentelemetry_semantic_conventions::resource::TELEMETRY_SDK_VERSION;
+use serde::Deserialize;
+use serde::Serialize;
+use std::borrow::Cow;
+use std::env;
+use std::fmt::Debug;
+use std::pin::Pin;
+use std::task::Context;
+use std::task::Poll;
+use std::thread;
+use std::time::Duration;
+use std::time::SystemTime;
+
+type SpanProcessor = BatchSpanProcessor<OtelSharedRuntime>;
+type LogProcessor = BatchLogProcessor<OtelSharedRuntime>;
+
+deno_core::extension!(
+ deno_otel,
+ 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_span_attribute2,
+ op_otel_span_attribute3,
+ op_otel_span_set_dropped,
+ op_otel_span_flush,
+ ],
+);
+
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct OtelConfig {
+ pub runtime_name: Cow<'static, str>,
+ pub runtime_version: Cow<'static, str>,
+ pub console: OtelConsoleConfig,
+ pub deterministic: bool,
+}
+
+#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
+#[repr(u8)]
+pub enum OtelConsoleConfig {
+ Ignore = 0,
+ Capture = 1,
+ Replace = 2,
+}
+
+impl Default for OtelConfig {
+ fn default() -> Self {
+ Self {
+ runtime_name: Cow::Borrowed(env!("CARGO_PKG_NAME")),
+ runtime_version: Cow::Borrowed(env!("CARGO_PKG_VERSION")),
+ console: OtelConsoleConfig::Capture,
+ deterministic: false,
+ }
+ }
+}
+
+static OTEL_SHARED_RUNTIME_SPAWN_TASK_TX: Lazy<
+ UnboundedSender<BoxFuture<'static, ()>>,
+> = Lazy::new(otel_create_shared_runtime);
+
+fn otel_create_shared_runtime() -> UnboundedSender<BoxFuture<'static, ()>> {
+ let (spawn_task_tx, mut spawn_task_rx) =
+ mpsc::unbounded::<BoxFuture<'static, ()>>();
+
+ thread::spawn(move || {
+ let rt = create_basic_runtime();
+ rt.block_on(async move {
+ while let Some(task) = spawn_task_rx.next().await {
+ tokio::spawn(task);
+ }
+ });
+ });
+
+ spawn_task_tx
+}
+
+#[derive(Clone, Copy)]
+struct OtelSharedRuntime;
+
+impl hyper::rt::Executor<BoxFuture<'static, ()>> for OtelSharedRuntime {
+ fn execute(&self, fut: BoxFuture<'static, ()>) {
+ (*OTEL_SHARED_RUNTIME_SPAWN_TASK_TX)
+ .unbounded_send(fut)
+ .expect("failed to send task to shared OpenTelemetry runtime");
+ }
+}
+
+impl opentelemetry_sdk::runtime::Runtime for OtelSharedRuntime {
+ type Interval = Pin<Box<dyn Stream<Item = ()> + Send + 'static>>;
+ type Delay = Pin<Box<tokio::time::Sleep>>;
+
+ fn interval(&self, period: Duration) -> Self::Interval {
+ stream::repeat(())
+ .then(move |_| tokio::time::sleep(period))
+ .boxed()
+ }
+
+ fn spawn(&self, future: BoxFuture<'static, ()>) {
+ (*OTEL_SHARED_RUNTIME_SPAWN_TASK_TX)
+ .unbounded_send(future)
+ .expect("failed to send task to shared OpenTelemetry runtime");
+ }
+
+ fn delay(&self, duration: Duration) -> Self::Delay {
+ Box::pin(tokio::time::sleep(duration))
+ }
+}
+
+impl opentelemetry_sdk::runtime::RuntimeChannel for OtelSharedRuntime {
+ type Receiver<T: Debug + Send> = BatchMessageChannelReceiver<T>;
+ type Sender<T: Debug + Send> = BatchMessageChannelSender<T>;
+
+ fn batch_message_channel<T: Debug + Send>(
+ &self,
+ capacity: usize,
+ ) -> (Self::Sender<T>, Self::Receiver<T>) {
+ let (batch_tx, batch_rx) = tokio::sync::mpsc::channel::<T>(capacity);
+ (batch_tx.into(), batch_rx.into())
+ }
+}
+
+#[derive(Debug)]
+pub struct BatchMessageChannelSender<T: Send> {
+ sender: tokio::sync::mpsc::Sender<T>,
+}
+
+impl<T: Send> From<tokio::sync::mpsc::Sender<T>>
+ for BatchMessageChannelSender<T>
+{
+ fn from(sender: tokio::sync::mpsc::Sender<T>) -> Self {
+ Self { sender }
+ }
+}
+
+impl<T: Send> opentelemetry_sdk::runtime::TrySend
+ for BatchMessageChannelSender<T>
+{
+ type Message = T;
+
+ fn try_send(
+ &self,
+ item: Self::Message,
+ ) -> Result<(), opentelemetry_sdk::runtime::TrySendError> {
+ self.sender.try_send(item).map_err(|err| match err {
+ tokio::sync::mpsc::error::TrySendError::Full(_) => {
+ opentelemetry_sdk::runtime::TrySendError::ChannelFull
+ }
+ tokio::sync::mpsc::error::TrySendError::Closed(_) => {
+ opentelemetry_sdk::runtime::TrySendError::ChannelClosed
+ }
+ })
+ }
+}
+
+pub struct BatchMessageChannelReceiver<T> {
+ receiver: tokio::sync::mpsc::Receiver<T>,
+}
+
+impl<T> From<tokio::sync::mpsc::Receiver<T>>
+ for BatchMessageChannelReceiver<T>
+{
+ fn from(receiver: tokio::sync::mpsc::Receiver<T>) -> Self {
+ Self { receiver }
+ }
+}
+
+impl<T> Stream for BatchMessageChannelReceiver<T> {
+ type Item = T;
+
+ fn poll_next(
+ mut self: Pin<&mut Self>,
+ cx: &mut Context<'_>,
+ ) -> Poll<Option<Self::Item>> {
+ self.receiver.poll_recv(cx)
+ }
+}
+
+mod hyper_client {
+ use http_body_util::BodyExt;
+ use http_body_util::Full;
+ use hyper::body::Body as HttpBody;
+ use hyper::body::Frame;
+ use hyper_util::client::legacy::connect::HttpConnector;
+ use hyper_util::client::legacy::Client;
+ use opentelemetry_http::Bytes;
+ use opentelemetry_http::HttpError;
+ 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;
+
+ // same as opentelemetry_http::HyperClient except it uses OtelSharedRuntime
+ #[derive(Debug, Clone)]
+ pub struct HyperClient {
+ inner: Client<HttpConnector, Body>,
+ }
+
+ impl HyperClient {
+ pub fn new() -> Self {
+ Self {
+ inner: Client::builder(OtelSharedRuntime).build(HttpConnector::new()),
+ }
+ }
+ }
+
+ #[async_trait::async_trait]
+ impl opentelemetry_http::HttpClient for HyperClient {
+ async fn send(
+ &self,
+ request: Request<Vec<u8>>,
+ ) -> Result<Response<Bytes>, HttpError> {
+ let (parts, body) = request.into_parts();
+ let request = Request::from_parts(parts, Body(Full::from(body)));
+ let mut response = self.inner.request(request).await?;
+ let headers = std::mem::take(response.headers_mut());
+
+ let mut http_response = Response::builder()
+ .status(response.status())
+ .body(response.into_body().collect().await?.to_bytes())?;
+ *http_response.headers_mut() = headers;
+
+ Ok(http_response.error_for_status()?)
+ }
+ }
+
+ #[pin_project::pin_project]
+ pub struct Body(#[pin] Full<Bytes>);
+
+ impl HttpBody for Body {
+ type Data = Bytes;
+ type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
+
+ #[inline]
+ fn poll_frame(
+ self: Pin<&mut Self>,
+ cx: &mut task::Context<'_>,
+ ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
+ self.project().0.poll_frame(cx).map_err(Into::into)
+ }
+
+ #[inline]
+ fn is_end_stream(&self) -> bool {
+ self.0.is_end_stream()
+ }
+
+ #[inline]
+ fn size_hint(&self) -> hyper::body::SizeHint {
+ self.0.size_hint()
+ }
+ }
+}
+
+static OTEL_PROCESSORS: OnceCell<(SpanProcessor, LogProcessor)> =
+ OnceCell::new();
+
+static BUILT_IN_INSTRUMENTATION_SCOPE: OnceCell<
+ opentelemetry::InstrumentationScope,
+> = OnceCell::new();
+
+pub fn init(config: OtelConfig) -> anyhow::Result<()> {
+ // Parse the `OTEL_EXPORTER_OTLP_PROTOCOL` variable. The opentelemetry_*
+ // crates don't do this automatically.
+ // TODO(piscisaureus): enable GRPC support.
+ let protocol = match env::var("OTEL_EXPORTER_OTLP_PROTOCOL").as_deref() {
+ Ok("http/protobuf") => Protocol::HttpBinary,
+ Ok("http/json") => Protocol::HttpJson,
+ Ok("") | Err(env::VarError::NotPresent) => {
+ return Ok(());
+ }
+ Ok(protocol) => {
+ return Err(anyhow!(
+ "Env var OTEL_EXPORTER_OTLP_PROTOCOL specifies an unsupported protocol: {}",
+ protocol
+ ));
+ }
+ Err(err) => {
+ return Err(anyhow!(
+ "Failed to read env var OTEL_EXPORTER_OTLP_PROTOCOL: {}",
+ err
+ ));
+ }
+ };
+
+ // Define the resource attributes that will be attached to all log records.
+ // These attributes are sourced as follows (in order of precedence):
+ // * The `service.name` attribute from the `OTEL_SERVICE_NAME` env var.
+ // * Additional attributes from the `OTEL_RESOURCE_ATTRIBUTES` env var.
+ // * Default attribute values defined here.
+ // TODO(piscisaureus): add more default attributes (e.g. script path).
+ let mut resource = Resource::default();
+
+ // 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(
+ TELEMETRY_SDK_LANGUAGE,
+ format!(
+ "deno-{}",
+ resource.get(Key::new(TELEMETRY_SDK_LANGUAGE)).unwrap()
+ ),
+ ),
+ KeyValue::new(
+ TELEMETRY_SDK_NAME,
+ format!(
+ "deno-{}",
+ resource.get(Key::new(TELEMETRY_SDK_NAME)).unwrap()
+ ),
+ ),
+ KeyValue::new(
+ TELEMETRY_SDK_VERSION,
+ format!(
+ "{}-{}",
+ config.runtime_version,
+ resource.get(Key::new(TELEMETRY_SDK_VERSION)).unwrap()
+ ),
+ ),
+ ]));
+
+ // The OTLP endpoint is automatically picked up from the
+ // `OTEL_EXPORTER_OTLP_ENDPOINT` environment variable. Additional headers can
+ // be specified using `OTEL_EXPORTER_OTLP_HEADERS`.
+
+ let client = hyper_client::HyperClient::new();
+
+ let span_exporter = HttpExporterBuilder::default()
+ .with_http_client(client.clone())
+ .with_protocol(protocol)
+ .build_span_exporter()?;
+ let mut span_processor =
+ BatchSpanProcessor::builder(span_exporter, OtelSharedRuntime).build();
+ span_processor.set_resource(&resource);
+
+ let log_exporter = HttpExporterBuilder::default()
+ .with_http_client(client)
+ .with_protocol(protocol)
+ .build_log_exporter()?;
+ let log_processor =
+ BatchLogProcessor::builder(log_exporter, OtelSharedRuntime).build();
+ log_processor.set_resource(&resource);
+
+ OTEL_PROCESSORS
+ .set((span_processor, log_processor))
+ .map_err(|_| anyhow!("failed to init otel"))?;
+
+ let builtin_instrumentation_scope =
+ opentelemetry::InstrumentationScope::builder("deno")
+ .with_version(config.runtime_version.clone())
+ .build();
+ BUILT_IN_INSTRUMENTATION_SCOPE
+ .set(builtin_instrumentation_scope)
+ .map_err(|_| anyhow!("failed to init otel"))?;
+
+ Ok(())
+}
+
+/// This function is called by the runtime whenever it is about to call
+/// `process::exit()`, to ensure that all OpenTelemetry logs are properly
+/// flushed before the process terminates.
+pub fn flush() {
+ if let Some((span_processor, log_processor)) = OTEL_PROCESSORS.get() {
+ let _ = span_processor.force_flush();
+ let _ = log_processor.force_flush();
+ }
+}
+
+pub fn handle_log(record: &log::Record) {
+ use log::Level;
+
+ let Some((_, log_processor)) = OTEL_PROCESSORS.get() else {
+ return;
+ };
+
+ let mut log_record = LogRecord::default();
+
+ log_record.set_observed_timestamp(SystemTime::now());
+ log_record.set_severity_number(match record.level() {
+ Level::Error => Severity::Error,
+ Level::Warn => Severity::Warn,
+ Level::Info => Severity::Info,
+ Level::Debug => Severity::Debug,
+ Level::Trace => Severity::Trace,
+ });
+ log_record.set_severity_text(record.level().as_str());
+ log_record.set_body(record.args().to_string().into());
+ log_record.set_target(record.metadata().target().to_string());
+
+ struct Visitor<'s>(&'s mut LogRecord);
+
+ impl<'s, 'kvs> log::kv::VisitSource<'kvs> for Visitor<'s> {
+ fn visit_pair(
+ &mut self,
+ key: log::kv::Key<'kvs>,
+ value: log::kv::Value<'kvs>,
+ ) -> Result<(), log::kv::Error> {
+ #[allow(clippy::manual_map)]
+ let value = if let Some(v) = value.to_bool() {
+ Some(AnyValue::Boolean(v))
+ } else if let Some(v) = value.to_borrowed_str() {
+ Some(AnyValue::String(v.to_owned().into()))
+ } else if let Some(v) = value.to_f64() {
+ Some(AnyValue::Double(v))
+ } else if let Some(v) = value.to_i64() {
+ Some(AnyValue::Int(v))
+ } else {
+ None
+ };
+
+ if let Some(value) = value {
+ let key = Key::from(key.as_str().to_owned());
+ self.0.add_attribute(key, value);
+ }
+
+ Ok(())
+ }
+ }
+
+ let _ = record.key_values().visit(&mut Visitor(&mut log_record));
+
+ log_processor.emit(
+ &mut log_record,
+ BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap(),
+ );
+}
+
+fn parse_trace_id(
+ scope: &mut v8::HandleScope<'_>,
+ trace_id: v8::Local<'_, v8::Value>,
+) -> TraceId {
+ if let Ok(string) = trace_id.try_cast() {
+ let value_view = v8::ValueView::new(scope, string);
+ match value_view.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ TraceId::from_hex(&String::from_utf8_lossy(bytes))
+ .unwrap_or(TraceId::INVALID)
+ }
+
+ _ => TraceId::INVALID,
+ }
+ } else if let Ok(uint8array) = trace_id.try_cast::<v8::Uint8Array>() {
+ let data = uint8array.data();
+ let byte_length = uint8array.byte_length();
+ if byte_length != 16 {
+ return TraceId::INVALID;
+ }
+ // SAFETY: We have ensured that the byte length is 16, so it is safe to
+ // cast the data to an array of 16 bytes.
+ let bytes = unsafe { &*(data as *const u8 as *const [u8; 16]) };
+ TraceId::from_bytes(*bytes)
+ } else {
+ TraceId::INVALID
+ }
+}
+
+fn parse_span_id(
+ scope: &mut v8::HandleScope<'_>,
+ span_id: v8::Local<'_, v8::Value>,
+) -> SpanId {
+ if let Ok(string) = span_id.try_cast() {
+ let value_view = v8::ValueView::new(scope, string);
+ match value_view.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ SpanId::from_hex(&String::from_utf8_lossy(bytes))
+ .unwrap_or(SpanId::INVALID)
+ }
+ _ => SpanId::INVALID,
+ }
+ } else if let Ok(uint8array) = span_id.try_cast::<v8::Uint8Array>() {
+ let data = uint8array.data();
+ let byte_length = uint8array.byte_length();
+ if byte_length != 8 {
+ return SpanId::INVALID;
+ }
+ // SAFETY: We have ensured that the byte length is 8, so it is safe to
+ // cast the data to an array of 8 bytes.
+ let bytes = unsafe { &*(data as *const u8 as *const [u8; 8]) };
+ SpanId::from_bytes(*bytes)
+ } else {
+ SpanId::INVALID
+ }
+}
+
+macro_rules! attr {
+ ($scope:ident, $attributes:expr $(=> $dropped_attributes_count:expr)?, $name:expr, $value:expr) => {
+ let name = if let Ok(name) = $name.try_cast() {
+ let view = v8::ValueView::new($scope, name);
+ match view.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ Some(String::from_utf8_lossy(bytes).into_owned())
+ }
+ v8::ValueViewData::TwoByte(bytes) => {
+ Some(String::from_utf16_lossy(bytes))
+ }
+ }
+ } else {
+ None
+ };
+ let value = if let Ok(string) = $value.try_cast::<v8::String>() {
+ Some(Value::String(StringValue::from({
+ let x = v8::ValueView::new($scope, string);
+ match x.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ String::from_utf8_lossy(bytes).into_owned()
+ }
+ v8::ValueViewData::TwoByte(bytes) => String::from_utf16_lossy(bytes),
+ }
+ })))
+ } else if let Ok(number) = $value.try_cast::<v8::Number>() {
+ Some(Value::F64(number.value()))
+ } else if let Ok(boolean) = $value.try_cast::<v8::Boolean>() {
+ Some(Value::Bool(boolean.is_true()))
+ } else if let Ok(bigint) = $value.try_cast::<v8::BigInt>() {
+ let (i64_value, _lossless) = bigint.i64_value();
+ Some(Value::I64(i64_value))
+ } else {
+ None
+ };
+ if let (Some(name), Some(value)) = (name, value) {
+ $attributes.push(KeyValue::new(name, value));
+ }
+ $(
+ else {
+ $dropped_attributes_count += 1;
+ }
+ )?
+ };
+}
+
+#[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>,
+ #[string] schema_url: Option<String>,
+) -> 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,
+) {
+ state.put(scope.clone());
+}
+
+#[op2(fast)]
+fn op_otel_instrumentation_scope_enter_builtin(state: &mut OpState) {
+ state.put(InstrumentationScope(
+ BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap().clone(),
+ ));
+}
+
+#[op2(fast)]
+fn op_otel_log(
+ scope: &mut v8::HandleScope<'_>,
+ #[string] message: String,
+ #[smi] level: i32,
+ trace_id: v8::Local<'_, v8::Value>,
+ span_id: v8::Local<'_, v8::Value>,
+ #[smi] trace_flags: u8,
+) {
+ let Some((_, log_processor)) = OTEL_PROCESSORS.get() else {
+ return;
+ };
+
+ // 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 trace_id = parse_trace_id(scope, trace_id);
+ let span_id = parse_span_id(scope, span_id);
+
+ let mut log_record = LogRecord::default();
+
+ log_record.set_observed_timestamp(SystemTime::now());
+ log_record.set_body(message.into());
+ log_record.set_severity_number(severity);
+ log_record.set_severity_text(severity.name());
+ if trace_id != TraceId::INVALID && span_id != SpanId::INVALID {
+ log_record.set_trace_context(
+ trace_id,
+ span_id,
+ Some(TraceFlags::new(trace_flags)),
+ );
+ }
+
+ log_processor.emit(
+ &mut log_record,
+ BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap(),
+ );
+}
+
+struct TemporarySpan(SpanData);
+
+#[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::<TemporarySpan>() {
+ let Some((span_processor, _)) = OTEL_PROCESSORS.get() else {
+ return Ok(());
+ };
+ span_processor.on_end(temporary_span.0);
+ };
+
+ let Some(InstrumentationScope(instrumentation_scope)) =
+ state.try_borrow::<InstrumentationScope>()
+ 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"));
+ }
+
+ let span_id = parse_span_id(scope, span_id);
+ if span_id == SpanId::INVALID {
+ return Err(anyhow!("invalid span_id"));
+ }
+
+ let parent_span_id = parse_span_id(scope, parent_span_id);
+
+ let name = {
+ let x = v8::ValueView::new(scope, name.try_cast()?);
+ match x.data() {
+ v8::ValueViewData::OneByte(bytes) => {
+ String::from_utf8_lossy(bytes).into_owned()
+ }
+ v8::ValueViewData::TwoByte(bytes) => String::from_utf16_lossy(bytes),
+ }
+ };
+
+ 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 {
+ 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);
+
+ Ok(())
+}
+
+#[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::<TemporarySpan>() {
+ temporary_span.0.status = match status {
+ 0 => SpanStatus::Unset,
+ 1 => SpanStatus::Ok,
+ 2 => SpanStatus::Error {
+ description: Cow::Owned(error_description.into_owned()),
+ },
+ _ => return,
+ };
+ }
+}
+
+#[op2(fast)]
+fn op_otel_span_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(temporary_span) = state.try_borrow_mut::<TemporarySpan>() {
+ temporary_span.0.attributes.reserve_exact(
+ (capacity as usize) - temporary_span.0.attributes.capacity(),
+ );
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key, value);
+ }
+}
+
+#[op2(fast)]
+fn op_otel_span_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(temporary_span) = state.try_borrow_mut::<TemporarySpan>() {
+ temporary_span.0.attributes.reserve_exact(
+ (capacity as usize) - temporary_span.0.attributes.capacity(),
+ );
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2);
+ }
+}
+
+#[allow(clippy::too_many_arguments)]
+#[op2(fast)]
+fn op_otel_span_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>,
+) {
+ if let Some(temporary_span) = state.try_borrow_mut::<TemporarySpan>() {
+ temporary_span.0.attributes.reserve_exact(
+ (capacity as usize) - temporary_span.0.attributes.capacity(),
+ );
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key1, value1);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key2, value2);
+ attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key3, value3);
+ }
+}
+
+#[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,
+) {
+ if let Some(temporary_span) = state.try_borrow_mut::<TemporarySpan>() {
+ 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;
+ }
+}
+
+#[op2(fast)]
+fn op_otel_span_flush(state: &mut OpState) {
+ let Some(temporary_span) = state.try_take::<TemporarySpan>() else {
+ return;
+ };
+
+ let Some((span_processor, _)) = OTEL_PROCESSORS.get() else {
+ return;
+ };
+
+ span_processor.on_end(temporary_span.0);
+}
diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs
index 1dbc85259..9ad963f3b 100644
--- a/runtime/ops/permissions.rs
+++ b/runtime/ops/permissions.rs
@@ -2,8 +2,6 @@
use ::deno_permissions::PermissionState;
use ::deno_permissions::PermissionsContainer;
-use deno_core::error::custom_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
use serde::Deserialize;
@@ -47,12 +45,26 @@ impl From<PermissionState> for PermissionStatus {
}
}
+#[derive(Debug, thiserror::Error)]
+pub enum PermissionError {
+ #[error("No such permission name: {0}")]
+ InvalidPermissionName(String),
+ #[error("{0}")]
+ PathResolve(#[from] ::deno_permissions::PathResolveError),
+ #[error("{0}")]
+ NetDescriptorParse(#[from] ::deno_permissions::NetDescriptorParseError),
+ #[error("{0}")]
+ SysDescriptorParse(#[from] ::deno_permissions::SysDescriptorParseError),
+ #[error("{0}")]
+ RunDescriptorParse(#[from] ::deno_permissions::RunDescriptorParseError),
+}
+
#[op2]
#[serde]
pub fn op_query_permission(
state: &mut OpState,
#[serde] args: PermissionArgs,
-) -> Result<PermissionStatus, AnyError> {
+) -> Result<PermissionStatus, PermissionError> {
let permissions = state.borrow::<PermissionsContainer>();
let perm = match args.name.as_ref() {
"read" => permissions.query_read(args.path.as_deref())?,
@@ -62,12 +74,7 @@ pub fn op_query_permission(
"sys" => permissions.query_sys(args.kind.as_deref())?,
"run" => permissions.query_run(args.command.as_deref())?,
"ffi" => permissions.query_ffi(args.path.as_deref())?,
- n => {
- return Err(custom_error(
- "ReferenceError",
- format!("No such permission name: {n}"),
- ))
- }
+ _ => return Err(PermissionError::InvalidPermissionName(args.name)),
};
Ok(PermissionStatus::from(perm))
}
@@ -77,7 +84,7 @@ pub fn op_query_permission(
pub fn op_revoke_permission(
state: &mut OpState,
#[serde] args: PermissionArgs,
-) -> Result<PermissionStatus, AnyError> {
+) -> Result<PermissionStatus, PermissionError> {
let permissions = state.borrow::<PermissionsContainer>();
let perm = match args.name.as_ref() {
"read" => permissions.revoke_read(args.path.as_deref())?,
@@ -87,12 +94,7 @@ pub fn op_revoke_permission(
"sys" => permissions.revoke_sys(args.kind.as_deref())?,
"run" => permissions.revoke_run(args.command.as_deref())?,
"ffi" => permissions.revoke_ffi(args.path.as_deref())?,
- n => {
- return Err(custom_error(
- "ReferenceError",
- format!("No such permission name: {n}"),
- ))
- }
+ _ => return Err(PermissionError::InvalidPermissionName(args.name)),
};
Ok(PermissionStatus::from(perm))
}
@@ -102,7 +104,7 @@ pub fn op_revoke_permission(
pub fn op_request_permission(
state: &mut OpState,
#[serde] args: PermissionArgs,
-) -> Result<PermissionStatus, AnyError> {
+) -> Result<PermissionStatus, PermissionError> {
let permissions = state.borrow::<PermissionsContainer>();
let perm = match args.name.as_ref() {
"read" => permissions.request_read(args.path.as_deref())?,
@@ -112,12 +114,7 @@ pub fn op_request_permission(
"sys" => permissions.request_sys(args.kind.as_deref())?,
"run" => permissions.request_run(args.command.as_deref())?,
"ffi" => permissions.request_ffi(args.path.as_deref())?,
- n => {
- return Err(custom_error(
- "ReferenceError",
- format!("No such permission name: {n}"),
- ))
- }
+ _ => return Err(PermissionError::InvalidPermissionName(args.name)),
};
Ok(PermissionStatus::from(perm))
}
diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs
index f6555e932..ee2f660dc 100644
--- a/runtime/ops/process.rs
+++ b/runtime/ops/process.rs
@@ -1,8 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::anyhow::Context;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::serde_json;
use deno_core::AsyncMutFuture;
@@ -35,6 +32,7 @@ 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)]
@@ -105,11 +103,12 @@ impl StdioOrRid {
pub fn as_stdio(
&self,
state: &mut OpState,
- ) -> Result<std::process::Stdio, AnyError> {
+ ) -> Result<std::process::Stdio, ProcessError> {
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)
}
}
}
@@ -191,6 +190,41 @@ pub struct SpawnArgs {
needs_npm_process_state: bool,
}
+#[derive(Debug, thiserror::Error)]
+pub enum ProcessError {
+ #[error("Failed to spawn '{command}': {error}")]
+ SpawnFailed {
+ command: String,
+ #[source]
+ error: Box<ProcessError>,
+ },
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[cfg(unix)]
+ #[error(transparent)]
+ Nix(nix::Error),
+ #[error("failed resolving cwd: {0}")]
+ FailedResolvingCwd(#[source] std::io::Error),
+ #[error(transparent)]
+ Permission(#[from] deno_permissions::PermissionCheckError),
+ #[error(transparent)]
+ RunPermission(#[from] CheckRunPermissionError),
+ #[error(transparent)]
+ Resource(deno_core::error::AnyError),
+ #[error(transparent)]
+ BorrowMut(std::cell::BorrowMutError),
+ #[error(transparent)]
+ Which(which::Error),
+ #[error("Child process has already terminated.")]
+ ChildProcessAlreadyTerminated,
+ #[error("Invalid pid")]
+ InvalidPid,
+ #[error(transparent)]
+ Signal(#[from] SignalError),
+ #[error("Missing cmd")]
+ MissingCmd, // only for Deno.run
+}
+
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ChildStdio {
@@ -208,7 +242,7 @@ pub struct ChildStatus {
}
impl TryFrom<ExitStatus> for ChildStatus {
- type Error = AnyError;
+ type Error = SignalError;
fn try_from(status: ExitStatus) -> Result<Self, Self::Error> {
let code = status.code();
@@ -259,7 +293,7 @@ type CreateCommand = (
pub fn npm_process_state_tempfile(
contents: &[u8],
-) -> Result<deno_io::RawIoHandle, AnyError> {
+) -> Result<deno_io::RawIoHandle, std::io::Error> {
let mut temp_file = tempfile::tempfile()?;
temp_file.write_all(contents)?;
let handle = temp_file.into_raw_io_handle();
@@ -301,7 +335,7 @@ fn create_command(
state: &mut OpState,
mut args: SpawnArgs,
api_name: &str,
-) -> Result<CreateCommand, AnyError> {
+) -> Result<CreateCommand, ProcessError> {
let maybe_npm_process_state = if args.needs_npm_process_state {
let provider = state.borrow::<NpmProcessStateProviderRc>();
let process_state = provider.get_npm_process_state();
@@ -505,7 +539,7 @@ fn spawn_child(
ipc_pipe_rid: Option<ResourceId>,
extra_pipe_rids: Vec<Option<ResourceId>>,
detached: bool,
-) -> Result<Child, AnyError> {
+) -> Result<Child, ProcessError> {
let mut command = tokio::process::Command::from(command);
// TODO(@crowlkats): allow detaching processes.
// currently deno will orphan a process when exiting with an error or Deno.exit()
@@ -554,10 +588,10 @@ fn spawn_child(
}
}
- return Err(AnyError::from(err).context(format!(
- "Failed to spawn '{}'",
- command.get_program().to_string_lossy()
- )));
+ return Err(ProcessError::SpawnFailed {
+ command: command.get_program().to_string_lossy().to_string(),
+ error: Box::new(err.into()),
+ });
}
};
@@ -600,11 +634,19 @@ fn compute_run_cmd_and_check_permissions(
arg_clear_env: bool,
state: &mut OpState,
api_name: &str,
-) -> Result<(PathBuf, RunEnv), AnyError> {
- let run_env = compute_run_env(arg_cwd, arg_envs, arg_clear_env)
- .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
- let cmd = resolve_cmd(arg_cmd, &run_env)
- .with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
+) -> Result<(PathBuf, RunEnv), ProcessError> {
+ let run_env =
+ compute_run_env(arg_cwd, arg_envs, arg_clear_env).map_err(|e| {
+ ProcessError::SpawnFailed {
+ command: arg_cmd.to_string(),
+ error: Box::new(e),
+ }
+ })?;
+ let cmd =
+ resolve_cmd(arg_cmd, &run_env).map_err(|e| ProcessError::SpawnFailed {
+ command: arg_cmd.to_string(),
+ error: Box::new(e),
+ })?;
check_run_permission(
state,
&RunQueryDescriptor::Path {
@@ -631,9 +673,10 @@ fn compute_run_env(
arg_cwd: Option<&str>,
arg_envs: &[(String, String)],
arg_clear_env: bool,
-) -> Result<RunEnv, AnyError> {
+) -> Result<RunEnv, ProcessError> {
#[allow(clippy::disallowed_methods)]
- let cwd = std::env::current_dir().context("failed resolving cwd")?;
+ let cwd =
+ std::env::current_dir().map_err(ProcessError::FailedResolvingCwd)?;
let cwd = arg_cwd
.map(|cwd_arg| resolve_path(cwd_arg, &cwd))
.unwrap_or(cwd);
@@ -670,7 +713,7 @@ fn compute_run_env(
Ok(RunEnv { envs, cwd })
}
-fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
+fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, ProcessError> {
let is_path = cmd.contains('/');
#[cfg(windows)]
let is_path = is_path || cmd.contains('\\') || Path::new(&cmd).is_absolute();
@@ -683,7 +726,7 @@ fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
Err(which::Error::CannotFindBinaryPath) => {
Err(std::io::Error::from(std::io::ErrorKind::NotFound).into())
}
- Err(err) => Err(err.into()),
+ Err(err) => Err(ProcessError::Which(err)),
}
}
}
@@ -692,12 +735,20 @@ fn resolve_path(path: &str, cwd: &Path) -> PathBuf {
deno_path_util::normalize_path(cwd.join(path))
}
+#[derive(Debug, thiserror::Error)]
+pub enum CheckRunPermissionError {
+ #[error(transparent)]
+ Permission(#[from] deno_permissions::PermissionCheckError),
+ #[error("{0}")]
+ Other(deno_core::error::AnyError),
+}
+
fn check_run_permission(
state: &mut OpState,
cmd: &RunQueryDescriptor,
run_env: &RunEnv,
api_name: &str,
-) -> Result<(), AnyError> {
+) -> Result<(), CheckRunPermissionError> {
let permissions = state.borrow_mut::<PermissionsContainer>();
if !permissions.query_run_all(api_name) {
// error the same on all platforms
@@ -705,13 +756,16 @@ fn check_run_permission(
if !env_var_names.is_empty() {
// 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(deno_core::error::custom_error(
- "NotCapable",
- format!(
- "Requires --allow-all permissions to spawn subprocess with {} environment variable{}.",
- env_var_names.join(", "),
- if env_var_names.len() != 1 { "s" } else { "" }
- )
+ return Err(CheckRunPermissionError::Other(
+ deno_core::error::custom_error(
+ "NotCapable",
+ format!(
+ "Requires --allow-run permissions to spawn subprocess with {0} environment variable{1}. Alternatively, spawn with {2} environment variable{1} unset.",
+ env_var_names.join(", "),
+ if env_var_names.len() != 1 { "s" } else { "" },
+ if env_var_names.len() != 1 { "these" } else { "the" }
+ ),
+ ),
));
}
permissions.check_run(cmd, api_name)?;
@@ -754,7 +808,7 @@ fn op_spawn_child(
state: &mut OpState,
#[serde] args: SpawnArgs,
#[string] api_name: String,
-) -> Result<Child, AnyError> {
+) -> Result<Child, ProcessError> {
let detached = args.detached;
let (command, pipe_rid, extra_pipe_rids, handles_to_close) =
create_command(state, args, &api_name)?;
@@ -771,16 +825,23 @@ fn op_spawn_child(
async fn op_spawn_wait(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<ChildStatus, AnyError> {
+) -> Result<ChildStatus, ProcessError> {
let resource = state
.borrow_mut()
.resource_table
- .get::<ChildResource>(rid)?;
- let result = resource.0.try_borrow_mut()?.wait().await?.try_into();
+ .get::<ChildResource>(rid)
+ .map_err(ProcessError::Resource)?;
+ let result = resource
+ .0
+ .try_borrow_mut()
+ .map_err(ProcessError::BorrowMut)?
+ .wait()
+ .await?
+ .try_into()?;
if let Ok(resource) = state.borrow_mut().resource_table.take_any(rid) {
resource.close();
}
- result
+ Ok(result)
}
#[op2]
@@ -788,16 +849,14 @@ async fn op_spawn_wait(
fn op_spawn_sync(
state: &mut OpState,
#[serde] args: SpawnArgs,
-) -> Result<SpawnOutput, AnyError> {
+) -> Result<SpawnOutput, ProcessError> {
let stdout = matches!(args.stdio.stdout, StdioOrRid::Stdio(Stdio::Piped));
let stderr = matches!(args.stdio.stderr, StdioOrRid::Stdio(Stdio::Piped));
let (mut command, _, _, _) =
create_command(state, args, "Deno.Command().outputSync()")?;
- let output = command.output().with_context(|| {
- format!(
- "Failed to spawn '{}'",
- command.get_program().to_string_lossy()
- )
+ let output = command.output().map_err(|e| ProcessError::SpawnFailed {
+ command: command.get_program().to_string_lossy().to_string(),
+ error: Box::new(e.into()),
})?;
Ok(SpawnOutput {
@@ -820,17 +879,15 @@ fn op_spawn_kill(
state: &mut OpState,
#[smi] rid: ResourceId,
#[string] signal: String,
-) -> Result<(), AnyError> {
+) -> Result<(), ProcessError> {
if let Ok(child_resource) = state.resource_table.get::<ChildResource>(rid) {
deprecated::kill(child_resource.1 as i32, &signal)?;
return Ok(());
}
- Err(type_error("Child process has already terminated."))
+ Err(ProcessError::ChildProcessAlreadyTerminated)
}
mod deprecated {
- use deno_core::anyhow;
-
use super::*;
#[derive(Deserialize)]
@@ -876,9 +933,9 @@ mod deprecated {
pub fn op_run(
state: &mut OpState,
#[serde] run_args: RunArgs,
- ) -> Result<RunInfo, AnyError> {
+ ) -> Result<RunInfo, ProcessError> {
let args = run_args.cmd;
- let cmd = args.first().ok_or_else(|| anyhow::anyhow!("Missing cmd"))?;
+ let cmd = args.first().ok_or(ProcessError::MissingCmd)?;
let (cmd, run_env) = compute_run_cmd_and_check_permissions(
cmd,
run_args.cwd.as_deref(),
@@ -990,11 +1047,12 @@ mod deprecated {
pub async fn op_run_status(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
- ) -> Result<ProcessStatus, AnyError> {
+ ) -> Result<ProcessStatus, ProcessError> {
let resource = state
.borrow_mut()
.resource_table
- .get::<ChildResource>(rid)?;
+ .get::<ChildResource>(rid)
+ .map_err(ProcessError::Resource)?;
let mut child = resource.borrow_mut().await;
let run_status = child.wait().await?;
let code = run_status.code();
@@ -1017,17 +1075,17 @@ mod deprecated {
}
#[cfg(unix)]
- pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
+ pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
let signo = super::super::signal::signal_str_to_int(signal)?;
use nix::sys::signal::kill as unix_kill;
use nix::sys::signal::Signal;
use nix::unistd::Pid;
- let sig = Signal::try_from(signo)?;
- unix_kill(Pid::from_raw(pid), Option::Some(sig)).map_err(AnyError::from)
+ let sig = Signal::try_from(signo).map_err(ProcessError::Nix)?;
+ unix_kill(Pid::from_raw(pid), Some(sig)).map_err(ProcessError::Nix)
}
#[cfg(not(unix))]
- pub fn kill(pid: i32, signal: &str) -> Result<(), AnyError> {
+ pub fn kill(pid: i32, signal: &str) -> Result<(), ProcessError> {
use std::io::Error;
use std::io::ErrorKind::NotFound;
use winapi::shared::minwindef::DWORD;
@@ -1041,9 +1099,9 @@ mod deprecated {
use winapi::um::winnt::PROCESS_TERMINATE;
if !matches!(signal, "SIGKILL" | "SIGTERM") {
- Err(type_error(format!("Invalid signal: {signal}")))
+ Err(SignalError::InvalidSignalStr(signal.to_string()).into())
} else if pid <= 0 {
- Err(type_error("Invalid pid"))
+ Err(ProcessError::InvalidPid)
} else {
let handle =
// SAFETY: winapi call
@@ -1077,11 +1135,10 @@ mod deprecated {
#[smi] pid: i32,
#[string] signal: String,
#[string] api_name: String,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), ProcessError> {
state
.borrow_mut::<PermissionsContainer>()
.check_run_all(&api_name)?;
- kill(pid, &signal)?;
- Ok(())
+ kill(pid, &signal)
}
}
diff --git a/runtime/ops/runtime.rs b/runtime/ops/runtime.rs
index 419274ebd..8d54783fc 100644
--- a/runtime/ops/runtime.rs
+++ b/runtime/ops/runtime.rs
@@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::ModuleSpecifier;
use deno_core::OpState;
@@ -16,10 +15,9 @@ deno_core::extension!(
#[op2]
#[string]
-fn op_main_module(state: &mut OpState) -> Result<String, AnyError> {
+fn op_main_module(state: &mut OpState) -> String {
let main_url = state.borrow::<ModuleSpecifier>();
- let main_path = main_url.to_string();
- Ok(main_path)
+ main_url.to_string()
}
/// This is an op instead of being done at initialization time because
diff --git a/runtime/ops/signal.rs b/runtime/ops/signal.rs
index ebc6db6d1..e1e4ab68b 100644
--- a/runtime/ops/signal.rs
+++ b/runtime/ops/signal.rs
@@ -1,6 +1,4 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::AsyncRefCell;
use deno_core::CancelFuture;
@@ -46,6 +44,42 @@ deno_core::extension!(
}
);
+#[derive(Debug, thiserror::Error)]
+pub enum SignalError {
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "openbsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "solaris",
+ target_os = "illumos"
+ ))]
+ #[error("Invalid signal: {0}")]
+ InvalidSignalStr(String),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "openbsd",
+ target_os = "openbsd",
+ target_os = "macos",
+ target_os = "solaris",
+ target_os = "illumos"
+ ))]
+ #[error("Invalid signal: {0}")]
+ InvalidSignalInt(libc::c_int),
+ #[cfg(target_os = "windows")]
+ #[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
+ InvalidSignalStr(String),
+ #[cfg(target_os = "windows")]
+ #[error("Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK), but got {0}")]
+ InvalidSignalInt(libc::c_int),
+ #[error("Binding to signal '{0}' is not allowed")]
+ SignalNotAllowed(String),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+}
+
#[cfg(unix)]
#[derive(Default)]
struct SignalState {
@@ -147,438 +181,217 @@ impl Resource for SignalStreamResource {
}
}
-#[cfg(target_os = "freebsd")]
-pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
- match s {
- "SIGHUP" => Ok(1),
- "SIGINT" => Ok(2),
- "SIGQUIT" => Ok(3),
- "SIGILL" => Ok(4),
- "SIGTRAP" => Ok(5),
- "SIGIOT" => Ok(6),
- "SIGABRT" => Ok(6),
- "SIGEMT" => Ok(7),
- "SIGFPE" => Ok(8),
- "SIGKILL" => Ok(9),
- "SIGBUS" => Ok(10),
- "SIGSEGV" => Ok(11),
- "SIGSYS" => Ok(12),
- "SIGPIPE" => Ok(13),
- "SIGALRM" => Ok(14),
- "SIGTERM" => Ok(15),
- "SIGURG" => Ok(16),
- "SIGSTOP" => Ok(17),
- "SIGTSTP" => Ok(18),
- "SIGCONT" => Ok(19),
- "SIGCHLD" => Ok(20),
- "SIGTTIN" => Ok(21),
- "SIGTTOU" => Ok(22),
- "SIGIO" => Ok(23),
- "SIGXCPU" => Ok(24),
- "SIGXFSZ" => Ok(25),
- "SIGVTALRM" => Ok(26),
- "SIGPROF" => Ok(27),
- "SIGWINCH" => Ok(28),
- "SIGINFO" => Ok(29),
- "SIGUSR1" => Ok(30),
- "SIGUSR2" => Ok(31),
- "SIGTHR" => Ok(32),
- "SIGLIBRT" => Ok(33),
- _ => Err(type_error(format!("Invalid signal : {}", s))),
- }
+macro_rules! first_literal {
+ ($head:literal $(, $tail:literal)*) => {
+ $head
+ };
}
+macro_rules! signal_dict {
+ ($(($number:literal, $($name:literal)|+)),*) => {
+ pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, SignalError> {
+ match s {
+ $($($name)|* => Ok($number),)*
+ _ => Err(SignalError::InvalidSignalStr(s.to_string())),
+ }
+ }
-#[cfg(target_os = "freebsd")]
-pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
- match s {
- 1 => Ok("SIGHUP"),
- 2 => Ok("SIGINT"),
- 3 => Ok("SIGQUIT"),
- 4 => Ok("SIGILL"),
- 5 => Ok("SIGTRAP"),
- 6 => Ok("SIGABRT"),
- 7 => Ok("SIGEMT"),
- 8 => Ok("SIGFPE"),
- 9 => Ok("SIGKILL"),
- 10 => Ok("SIGBUS"),
- 11 => Ok("SIGSEGV"),
- 12 => Ok("SIGSYS"),
- 13 => Ok("SIGPIPE"),
- 14 => Ok("SIGALRM"),
- 15 => Ok("SIGTERM"),
- 16 => Ok("SIGURG"),
- 17 => Ok("SIGSTOP"),
- 18 => Ok("SIGTSTP"),
- 19 => Ok("SIGCONT"),
- 20 => Ok("SIGCHLD"),
- 21 => Ok("SIGTTIN"),
- 22 => Ok("SIGTTOU"),
- 23 => Ok("SIGIO"),
- 24 => Ok("SIGXCPU"),
- 25 => Ok("SIGXFSZ"),
- 26 => Ok("SIGVTALRM"),
- 27 => Ok("SIGPROF"),
- 28 => Ok("SIGWINCH"),
- 29 => Ok("SIGINFO"),
- 30 => Ok("SIGUSR1"),
- 31 => Ok("SIGUSR2"),
- 32 => Ok("SIGTHR"),
- 33 => Ok("SIGLIBRT"),
- _ => Err(type_error(format!("Invalid signal : {}", s))),
+ pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, SignalError> {
+ match s {
+ $($number => Ok(first_literal!($($name),+)),)*
+ _ => Err(SignalError::InvalidSignalInt(s)),
+ }
+ }
}
}
-#[cfg(target_os = "openbsd")]
-pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
- match s {
- "SIGHUP" => Ok(1),
- "SIGINT" => Ok(2),
- "SIGQUIT" => Ok(3),
- "SIGILL" => Ok(4),
- "SIGTRAP" => Ok(5),
- "SIGIOT" => Ok(6),
- "SIGABRT" => Ok(6),
- "SIGEMT" => Ok(7),
- "SIGFPE" => Ok(8),
- "SIGKILL" => Ok(9),
- "SIGBUS" => Ok(10),
- "SIGSEGV" => Ok(11),
- "SIGSYS" => Ok(12),
- "SIGPIPE" => Ok(13),
- "SIGALRM" => Ok(14),
- "SIGTERM" => Ok(15),
- "SIGURG" => Ok(16),
- "SIGSTOP" => Ok(17),
- "SIGTSTP" => Ok(18),
- "SIGCONT" => Ok(19),
- "SIGCHLD" => Ok(20),
- "SIGTTIN" => Ok(21),
- "SIGTTOU" => Ok(22),
- "SIGIO" => Ok(23),
- "SIGXCPU" => Ok(24),
- "SIGXFSZ" => Ok(25),
- "SIGVTALRM" => Ok(26),
- "SIGPROF" => Ok(27),
- "SIGWINCH" => Ok(28),
- "SIGINFO" => Ok(29),
- "SIGUSR1" => Ok(30),
- "SIGUSR2" => Ok(31),
- "SIGTHR" => Ok(32),
- _ => Err(type_error(format!("Invalid signal : {}", s))),
- }
-}
+#[cfg(target_os = "freebsd")]
+signal_dict!(
+ (1, "SIGHUP"),
+ (2, "SIGINT"),
+ (3, "SIGQUIT"),
+ (4, "SIGILL"),
+ (5, "SIGTRAP"),
+ (6, "SIGABRT" | "SIGIOT"),
+ (7, "SIGEMT"),
+ (8, "SIGFPE"),
+ (9, "SIGKILL"),
+ (10, "SIGBUS"),
+ (11, "SIGSEGV"),
+ (12, "SIGSYS"),
+ (13, "SIGPIPE"),
+ (14, "SIGALRM"),
+ (15, "SIGTERM"),
+ (16, "SIGURG"),
+ (17, "SIGSTOP"),
+ (18, "SIGTSTP"),
+ (19, "SIGCONT"),
+ (20, "SIGCHLD"),
+ (21, "SIGTTIN"),
+ (22, "SIGTTOU"),
+ (23, "SIGIO"),
+ (24, "SIGXCPU"),
+ (25, "SIGXFSZ"),
+ (26, "SIGVTALRM"),
+ (27, "SIGPROF"),
+ (28, "SIGWINCH"),
+ (29, "SIGINFO"),
+ (30, "SIGUSR1"),
+ (31, "SIGUSR2"),
+ (32, "SIGTHR"),
+ (33, "SIGLIBRT")
+);
#[cfg(target_os = "openbsd")]
-pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
- match s {
- 1 => Ok("SIGHUP"),
- 2 => Ok("SIGINT"),
- 3 => Ok("SIGQUIT"),
- 4 => Ok("SIGILL"),
- 5 => Ok("SIGTRAP"),
- 6 => Ok("SIGABRT"),
- 7 => Ok("SIGEMT"),
- 8 => Ok("SIGFPE"),
- 9 => Ok("SIGKILL"),
- 10 => Ok("SIGBUS"),
- 11 => Ok("SIGSEGV"),
- 12 => Ok("SIGSYS"),
- 13 => Ok("SIGPIPE"),
- 14 => Ok("SIGALRM"),
- 15 => Ok("SIGTERM"),
- 16 => Ok("SIGURG"),
- 17 => Ok("SIGSTOP"),
- 18 => Ok("SIGTSTP"),
- 19 => Ok("SIGCONT"),
- 20 => Ok("SIGCHLD"),
- 21 => Ok("SIGTTIN"),
- 22 => Ok("SIGTTOU"),
- 23 => Ok("SIGIO"),
- 24 => Ok("SIGXCPU"),
- 25 => Ok("SIGXFSZ"),
- 26 => Ok("SIGVTALRM"),
- 27 => Ok("SIGPROF"),
- 28 => Ok("SIGWINCH"),
- 29 => Ok("SIGINFO"),
- 30 => Ok("SIGUSR1"),
- 31 => Ok("SIGUSR2"),
- 32 => Ok("SIGTHR"),
- _ => Err(type_error(format!("Invalid signal : {}", s))),
- }
-}
-
-#[cfg(any(target_os = "android", target_os = "linux"))]
-pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
- match s {
- "SIGHUP" => Ok(1),
- "SIGINT" => Ok(2),
- "SIGQUIT" => Ok(3),
- "SIGILL" => Ok(4),
- "SIGTRAP" => Ok(5),
- "SIGIOT" => Ok(6),
- "SIGABRT" => Ok(6),
- "SIGBUS" => Ok(7),
- "SIGFPE" => Ok(8),
- "SIGKILL" => Ok(9),
- "SIGUSR1" => Ok(10),
- "SIGSEGV" => Ok(11),
- "SIGUSR2" => Ok(12),
- "SIGPIPE" => Ok(13),
- "SIGALRM" => Ok(14),
- "SIGTERM" => Ok(15),
- "SIGSTKFLT" => Ok(16),
- "SIGCHLD" => Ok(17),
- "SIGCONT" => Ok(18),
- "SIGSTOP" => Ok(19),
- "SIGTSTP" => Ok(20),
- "SIGTTIN" => Ok(21),
- "SIGTTOU" => Ok(22),
- "SIGURG" => Ok(23),
- "SIGXCPU" => Ok(24),
- "SIGXFSZ" => Ok(25),
- "SIGVTALRM" => Ok(26),
- "SIGPROF" => Ok(27),
- "SIGWINCH" => Ok(28),
- "SIGIO" | "SIGPOLL" => Ok(29),
- "SIGPWR" => Ok(30),
- "SIGSYS" | "SIGUNUSED" => Ok(31),
- _ => Err(type_error(format!("Invalid signal : {s}"))),
- }
-}
+signal_dict!(
+ (1, "SIGHUP"),
+ (2, "SIGINT"),
+ (3, "SIGQUIT"),
+ (4, "SIGILL"),
+ (5, "SIGTRAP"),
+ (6, "SIGABRT" | "SIGIOT"),
+ (7, "SIGEMT"),
+ (8, "SIGKILL"),
+ (10, "SIGBUS"),
+ (11, "SIGSEGV"),
+ (12, "SIGSYS"),
+ (13, "SIGPIPE"),
+ (14, "SIGALRM"),
+ (15, "SIGTERM"),
+ (16, "SIGURG"),
+ (17, "SIGSTOP"),
+ (18, "SIGTSTP"),
+ (19, "SIGCONT"),
+ (20, "SIGCHLD"),
+ (21, "SIGTTIN"),
+ (22, "SIGTTOU"),
+ (23, "SIGIO"),
+ (24, "SIGXCPU"),
+ (25, "SIGXFSZ"),
+ (26, "SIGVTALRM"),
+ (27, "SIGPROF"),
+ (28, "SIGWINCH"),
+ (29, "SIGINFO"),
+ (30, "SIGUSR1"),
+ (31, "SIGUSR2"),
+ (32, "SIGTHR")
+);
#[cfg(any(target_os = "android", target_os = "linux"))]
-pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
- match s {
- 1 => Ok("SIGHUP"),
- 2 => Ok("SIGINT"),
- 3 => Ok("SIGQUIT"),
- 4 => Ok("SIGILL"),
- 5 => Ok("SIGTRAP"),
- 6 => Ok("SIGABRT"),
- 7 => Ok("SIGBUS"),
- 8 => Ok("SIGFPE"),
- 9 => Ok("SIGKILL"),
- 10 => Ok("SIGUSR1"),
- 11 => Ok("SIGSEGV"),
- 12 => Ok("SIGUSR2"),
- 13 => Ok("SIGPIPE"),
- 14 => Ok("SIGALRM"),
- 15 => Ok("SIGTERM"),
- 16 => Ok("SIGSTKFLT"),
- 17 => Ok("SIGCHLD"),
- 18 => Ok("SIGCONT"),
- 19 => Ok("SIGSTOP"),
- 20 => Ok("SIGTSTP"),
- 21 => Ok("SIGTTIN"),
- 22 => Ok("SIGTTOU"),
- 23 => Ok("SIGURG"),
- 24 => Ok("SIGXCPU"),
- 25 => Ok("SIGXFSZ"),
- 26 => Ok("SIGVTALRM"),
- 27 => Ok("SIGPROF"),
- 28 => Ok("SIGWINCH"),
- 29 => Ok("SIGIO"),
- 30 => Ok("SIGPWR"),
- 31 => Ok("SIGSYS"),
- _ => Err(type_error(format!("Invalid signal : {s}"))),
- }
-}
-
-#[cfg(target_os = "macos")]
-pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
- match s {
- "SIGHUP" => Ok(1),
- "SIGINT" => Ok(2),
- "SIGQUIT" => Ok(3),
- "SIGILL" => Ok(4),
- "SIGTRAP" => Ok(5),
- "SIGIOT" => Ok(6),
- "SIGABRT" => Ok(6),
- "SIGEMT" => Ok(7),
- "SIGFPE" => Ok(8),
- "SIGKILL" => Ok(9),
- "SIGBUS" => Ok(10),
- "SIGSEGV" => Ok(11),
- "SIGSYS" => Ok(12),
- "SIGPIPE" => Ok(13),
- "SIGALRM" => Ok(14),
- "SIGTERM" => Ok(15),
- "SIGURG" => Ok(16),
- "SIGSTOP" => Ok(17),
- "SIGTSTP" => Ok(18),
- "SIGCONT" => Ok(19),
- "SIGCHLD" => Ok(20),
- "SIGTTIN" => Ok(21),
- "SIGTTOU" => Ok(22),
- "SIGIO" => Ok(23),
- "SIGXCPU" => Ok(24),
- "SIGXFSZ" => Ok(25),
- "SIGVTALRM" => Ok(26),
- "SIGPROF" => Ok(27),
- "SIGWINCH" => Ok(28),
- "SIGINFO" => Ok(29),
- "SIGUSR1" => Ok(30),
- "SIGUSR2" => Ok(31),
- _ => Err(type_error(format!("Invalid signal: {s}"))),
- }
-}
+signal_dict!(
+ (1, "SIGHUP"),
+ (2, "SIGINT"),
+ (3, "SIGQUIT"),
+ (4, "SIGILL"),
+ (5, "SIGTRAP"),
+ (6, "SIGABRT" | "SIGIOT"),
+ (7, "SIGBUS"),
+ (8, "SIGFPE"),
+ (9, "SIGKILL"),
+ (10, "SIGUSR1"),
+ (11, "SIGSEGV"),
+ (12, "SIGUSR2"),
+ (13, "SIGPIPE"),
+ (14, "SIGALRM"),
+ (15, "SIGTERM"),
+ (16, "SIGSTKFLT"),
+ (17, "SIGCHLD"),
+ (18, "SIGCONT"),
+ (19, "SIGSTOP"),
+ (20, "SIGTSTP"),
+ (21, "SIGTTIN"),
+ (22, "SIGTTOU"),
+ (23, "SIGURG"),
+ (24, "SIGXCPU"),
+ (25, "SIGXFSZ"),
+ (26, "SIGVTALRM"),
+ (27, "SIGPROF"),
+ (28, "SIGWINCH"),
+ (29, "SIGIO" | "SIGPOLL"),
+ (30, "SIGPWR"),
+ (31, "SIGSYS" | "SIGUNUSED")
+);
#[cfg(target_os = "macos")]
-pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
- match s {
- 1 => Ok("SIGHUP"),
- 2 => Ok("SIGINT"),
- 3 => Ok("SIGQUIT"),
- 4 => Ok("SIGILL"),
- 5 => Ok("SIGTRAP"),
- 6 => Ok("SIGABRT"),
- 7 => Ok("SIGEMT"),
- 8 => Ok("SIGFPE"),
- 9 => Ok("SIGKILL"),
- 10 => Ok("SIGBUS"),
- 11 => Ok("SIGSEGV"),
- 12 => Ok("SIGSYS"),
- 13 => Ok("SIGPIPE"),
- 14 => Ok("SIGALRM"),
- 15 => Ok("SIGTERM"),
- 16 => Ok("SIGURG"),
- 17 => Ok("SIGSTOP"),
- 18 => Ok("SIGTSTP"),
- 19 => Ok("SIGCONT"),
- 20 => Ok("SIGCHLD"),
- 21 => Ok("SIGTTIN"),
- 22 => Ok("SIGTTOU"),
- 23 => Ok("SIGIO"),
- 24 => Ok("SIGXCPU"),
- 25 => Ok("SIGXFSZ"),
- 26 => Ok("SIGVTALRM"),
- 27 => Ok("SIGPROF"),
- 28 => Ok("SIGWINCH"),
- 29 => Ok("SIGINFO"),
- 30 => Ok("SIGUSR1"),
- 31 => Ok("SIGUSR2"),
- _ => Err(type_error(format!("Invalid signal: {s}"))),
- }
-}
-
-#[cfg(any(target_os = "solaris", target_os = "illumos"))]
-pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
- match s {
- "SIGHUP" => Ok(1),
- "SIGINT" => Ok(2),
- "SIGQUIT" => Ok(3),
- "SIGILL" => Ok(4),
- "SIGTRAP" => Ok(5),
- "SIGIOT" => Ok(6),
- "SIGABRT" => Ok(6),
- "SIGEMT" => Ok(7),
- "SIGFPE" => Ok(8),
- "SIGKILL" => Ok(9),
- "SIGBUS" => Ok(10),
- "SIGSEGV" => Ok(11),
- "SIGSYS" => Ok(12),
- "SIGPIPE" => Ok(13),
- "SIGALRM" => Ok(14),
- "SIGTERM" => Ok(15),
- "SIGUSR1" => Ok(16),
- "SIGUSR2" => Ok(17),
- "SIGCLD" => Ok(18),
- "SIGCHLD" => Ok(18),
- "SIGPWR" => Ok(19),
- "SIGWINCH" => Ok(20),
- "SIGURG" => Ok(21),
- "SIGPOLL" => Ok(22),
- "SIGIO" => Ok(22),
- "SIGSTOP" => Ok(23),
- "SIGTSTP" => Ok(24),
- "SIGCONT" => Ok(25),
- "SIGTTIN" => Ok(26),
- "SIGTTOU" => Ok(27),
- "SIGVTALRM" => Ok(28),
- "SIGPROF" => Ok(29),
- "SIGXCPU" => Ok(30),
- "SIGXFSZ" => Ok(31),
- "SIGWAITING" => Ok(32),
- "SIGLWP" => Ok(33),
- "SIGFREEZE" => Ok(34),
- "SIGTHAW" => Ok(35),
- "SIGCANCEL" => Ok(36),
- "SIGLOST" => Ok(37),
- "SIGXRES" => Ok(38),
- "SIGJVM1" => Ok(39),
- "SIGJVM2" => Ok(40),
- _ => Err(type_error(format!("Invalid signal : {}", s))),
- }
-}
+signal_dict!(
+ (1, "SIGHUP"),
+ (2, "SIGINT"),
+ (3, "SIGQUIT"),
+ (4, "SIGILL"),
+ (5, "SIGTRAP"),
+ (6, "SIGABRT" | "SIGIOT"),
+ (7, "SIGEMT"),
+ (8, "SIGFPE"),
+ (9, "SIGKILL"),
+ (10, "SIGBUS"),
+ (11, "SIGSEGV"),
+ (12, "SIGSYS"),
+ (13, "SIGPIPE"),
+ (14, "SIGALRM"),
+ (15, "SIGTERM"),
+ (16, "SIGURG"),
+ (17, "SIGSTOP"),
+ (18, "SIGTSTP"),
+ (19, "SIGCONT"),
+ (20, "SIGCHLD"),
+ (21, "SIGTTIN"),
+ (22, "SIGTTOU"),
+ (23, "SIGIO"),
+ (24, "SIGXCPU"),
+ (25, "SIGXFSZ"),
+ (26, "SIGVTALRM"),
+ (27, "SIGPROF"),
+ (28, "SIGWINCH"),
+ (29, "SIGINFO"),
+ (30, "SIGUSR1"),
+ (31, "SIGUSR2")
+);
#[cfg(any(target_os = "solaris", target_os = "illumos"))]
-pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
- match s {
- 1 => Ok("SIGHUP"),
- 2 => Ok("SIGINT"),
- 3 => Ok("SIGQUIT"),
- 4 => Ok("SIGILL"),
- 5 => Ok("SIGTRAP"),
- 6 => Ok("SIGABRT"),
- 7 => Ok("SIGEMT"),
- 8 => Ok("SIGFPE"),
- 9 => Ok("SIGKILL"),
- 10 => Ok("SIGBUS"),
- 11 => Ok("SIGSEGV"),
- 12 => Ok("SIGSYS"),
- 13 => Ok("SIGPIPE"),
- 14 => Ok("SIGALRM"),
- 15 => Ok("SIGTERM"),
- 16 => Ok("SIGUSR1"),
- 17 => Ok("SIGUSR2"),
- 18 => Ok("SIGCHLD"),
- 19 => Ok("SIGPWR"),
- 20 => Ok("SIGWINCH"),
- 21 => Ok("SIGURG"),
- 22 => Ok("SIGPOLL"),
- 23 => Ok("SIGSTOP"),
- 24 => Ok("SIGTSTP"),
- 25 => Ok("SIGCONT"),
- 26 => Ok("SIGTTIN"),
- 27 => Ok("SIGTTOU"),
- 28 => Ok("SIGVTALRM"),
- 29 => Ok("SIGPROF"),
- 30 => Ok("SIGXCPU"),
- 31 => Ok("SIGXFSZ"),
- 32 => Ok("SIGWAITING"),
- 33 => Ok("SIGLWP"),
- 34 => Ok("SIGFREEZE"),
- 35 => Ok("SIGTHAW"),
- 36 => Ok("SIGCANCEL"),
- 37 => Ok("SIGLOST"),
- 38 => Ok("SIGXRES"),
- 39 => Ok("SIGJVM1"),
- 40 => Ok("SIGJVM2"),
- _ => Err(type_error(format!("Invalid signal : {}", s))),
- }
-}
-
-#[cfg(target_os = "windows")]
-pub fn signal_str_to_int(s: &str) -> Result<libc::c_int, AnyError> {
- match s {
- "SIGINT" => Ok(2),
- "SIGBREAK" => Ok(21),
- _ => Err(type_error(
- "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK).",
- )),
- }
-}
+signal_dict!(
+ (1, "SIGHUP"),
+ (2, "SIGINT"),
+ (3, "SIGQUIT"),
+ (4, "SIGILL"),
+ (5, "SIGTRAP"),
+ (6, "SIGABRT" | "SIGIOT"),
+ (7, "SIGEMT"),
+ (8, "SIGFPE"),
+ (9, "SIGKILL"),
+ (10, "SIGBUS"),
+ (11, "SIGSEGV"),
+ (12, "SIGSYS"),
+ (13, "SIGPIPE"),
+ (14, "SIGALRM"),
+ (15, "SIGTERM"),
+ (16, "SIGUSR1"),
+ (17, "SIGUSR2"),
+ (18, "SIGCHLD"),
+ (19, "SIGPWR"),
+ (20, "SIGWINCH"),
+ (21, "SIGURG"),
+ (22, "SIGPOLL"),
+ (23, "SIGSTOP"),
+ (24, "SIGTSTP"),
+ (25, "SIGCONT"),
+ (26, "SIGTTIN"),
+ (27, "SIGTTOU"),
+ (28, "SIGVTALRM"),
+ (29, "SIGPROF"),
+ (30, "SIGXCPU"),
+ (31, "SIGXFSZ"),
+ (32, "SIGWAITING"),
+ (33, "SIGLWP"),
+ (34, "SIGFREEZE"),
+ (35, "SIGTHAW"),
+ (36, "SIGCANCEL"),
+ (37, "SIGLOST"),
+ (38, "SIGXRES"),
+ (39, "SIGJVM1"),
+ (40, "SIGJVM2")
+);
#[cfg(target_os = "windows")]
-pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
- match s {
- 2 => Ok("SIGINT"),
- 21 => Ok("SIGBREAK"),
- _ => Err(type_error(
- "Windows only supports ctrl-c (SIGINT) and ctrl-break (SIGBREAK).",
- )),
- }
-}
+signal_dict!((2, "SIGINT"), (21, "SIGBREAK"));
#[cfg(unix)]
#[op2(fast)]
@@ -586,12 +399,10 @@ pub fn signal_int_to_str(s: libc::c_int) -> Result<&'static str, AnyError> {
fn op_signal_bind(
state: &mut OpState,
#[string] sig: &str,
-) -> Result<ResourceId, AnyError> {
+) -> Result<ResourceId, SignalError> {
let signo = signal_str_to_int(sig)?;
if signal_hook_registry::FORBIDDEN.contains(&signo) {
- return Err(type_error(format!(
- "Binding to signal '{sig}' is not allowed",
- )));
+ return Err(SignalError::SignalNotAllowed(sig.to_string()));
}
let signal = AsyncRefCell::new(signal(SignalKind::from_raw(signo))?);
@@ -625,7 +436,7 @@ fn op_signal_bind(
fn op_signal_bind(
state: &mut OpState,
#[string] sig: &str,
-) -> Result<ResourceId, AnyError> {
+) -> Result<ResourceId, SignalError> {
let signo = signal_str_to_int(sig)?;
let resource = SignalStreamResource {
signal: AsyncRefCell::new(match signo {
@@ -649,7 +460,7 @@ fn op_signal_bind(
async fn op_signal_poll(
state: Rc<RefCell<OpState>>,
#[smi] rid: ResourceId,
-) -> Result<bool, AnyError> {
+) -> Result<bool, deno_core::error::AnyError> {
let resource = state
.borrow_mut()
.resource_table
@@ -668,7 +479,7 @@ async fn op_signal_poll(
pub fn op_signal_unbind(
state: &mut OpState,
#[smi] rid: ResourceId,
-) -> Result<(), AnyError> {
+) -> Result<(), deno_core::error::AnyError> {
let resource = state.resource_table.take::<SignalStreamResource>(rid)?;
#[cfg(unix)]
diff --git a/runtime/ops/tty.rs b/runtime/ops/tty.rs
index 5b49e3a24..7849185fa 100644
--- a/runtime/ops/tty.rs
+++ b/runtime/ops/tty.rs
@@ -2,7 +2,6 @@
use std::io::Error;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::OpState;
use rustyline::config::Configurer;
@@ -64,6 +63,19 @@ deno_core::extension!(
},
);
+#[derive(Debug, thiserror::Error)]
+pub enum TtyError {
+ #[error(transparent)]
+ Resource(deno_core::error::AnyError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[cfg(unix)]
+ #[error(transparent)]
+ Nix(nix::Error),
+ #[error(transparent)]
+ Other(deno_core::error::AnyError),
+}
+
// ref: <https://learn.microsoft.com/en-us/windows/console/setconsolemode>
#[cfg(windows)]
const COOKED_MODE: DWORD =
@@ -90,8 +102,11 @@ fn op_set_raw(
rid: u32,
is_raw: bool,
cbreak: bool,
-) -> Result<(), AnyError> {
- let handle_or_fd = state.resource_table.get_fd(rid)?;
+) -> Result<(), TtyError> {
+ let handle_or_fd = state
+ .resource_table
+ .get_fd(rid)
+ .map_err(TtyError::Resource)?;
// From https://github.com/kkawakam/rustyline/blob/master/src/tty/windows.rs
// and https://github.com/kkawakam/rustyline/blob/master/src/tty/unix.rs
@@ -107,7 +122,7 @@ fn op_set_raw(
let handle = handle_or_fd;
if cbreak {
- return Err(deno_core::error::not_supported());
+ return Err(TtyError::Other(deno_core::error::not_supported()));
}
let mut original_mode: DWORD = 0;
@@ -115,7 +130,7 @@ fn op_set_raw(
if unsafe { consoleapi::GetConsoleMode(handle, &mut original_mode) }
== FALSE
{
- return Err(Error::last_os_error().into());
+ return Err(TtyError::Io(Error::last_os_error()));
}
let new_mode = if is_raw {
@@ -185,7 +200,7 @@ fn op_set_raw(
winapi::um::wincon::WriteConsoleInputW(handle, &record, 1, &mut 0)
} == FALSE
{
- return Err(Error::last_os_error().into());
+ return Err(TtyError::Io(Error::last_os_error()));
}
/* Wait for read thread to acknowledge the cancellation to ensure that nothing
@@ -199,7 +214,7 @@ fn op_set_raw(
// SAFETY: winapi call
if unsafe { consoleapi::SetConsoleMode(handle, new_mode) } == FALSE {
- return Err(Error::last_os_error().into());
+ return Err(TtyError::Io(Error::last_os_error()));
}
Ok(())
@@ -244,14 +259,16 @@ fn op_set_raw(
let tty_mode_store = state.borrow::<TtyModeStore>().clone();
let previous_mode = tty_mode_store.get(rid);
- let raw_fd = handle_or_fd;
+ // SAFETY: Nix crate requires value to implement the AsFd trait
+ let raw_fd = unsafe { std::os::fd::BorrowedFd::borrow_raw(handle_or_fd) };
if is_raw {
let mut raw = match previous_mode {
Some(mode) => mode,
None => {
// Save original mode.
- let original_mode = termios::tcgetattr(raw_fd)?;
+ let original_mode =
+ termios::tcgetattr(raw_fd).map_err(TtyError::Nix)?;
tty_mode_store.set(rid, original_mode.clone());
original_mode
}
@@ -273,11 +290,13 @@ 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)?;
+ termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &raw)
+ .map_err(TtyError::Nix)?;
} else {
// Try restore saved mode.
if let Some(mode) = tty_mode_store.take(rid) {
- termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode)?;
+ termios::tcsetattr(raw_fd, termios::SetArg::TCSADRAIN, &mode)
+ .map_err(TtyError::Nix)?;
}
}
@@ -289,13 +308,16 @@ fn op_set_raw(
fn op_console_size(
state: &mut OpState,
#[buffer] result: &mut [u32],
-) -> Result<(), AnyError> {
+) -> Result<(), TtyError> {
fn check_console_size(
state: &mut OpState,
result: &mut [u32],
rid: u32,
- ) -> Result<(), AnyError> {
- let fd = state.resource_table.get_fd(rid)?;
+ ) -> Result<(), TtyError> {
+ let fd = state
+ .resource_table
+ .get_fd(rid)
+ .map_err(TtyError::Resource)?;
let size = console_size_from_fd(fd)?;
result[0] = size.cols;
result[1] = size.rows;
@@ -418,7 +440,7 @@ mod tests {
pub fn op_read_line_prompt(
#[string] prompt_text: &str,
#[string] default_value: &str,
-) -> Result<Option<String>, AnyError> {
+) -> Result<Option<String>, ReadlineError> {
let mut editor = Editor::<(), rustyline::history::DefaultHistory>::new()
.expect("Failed to create editor.");
@@ -438,6 +460,6 @@ pub fn op_read_line_prompt(
Ok(None)
}
Err(ReadlineError::Eof) => Ok(None),
- Err(err) => Err(err.into()),
+ Err(err) => Err(err),
}
}
diff --git a/runtime/ops/utils.rs b/runtime/ops/utils.rs
deleted file mode 100644
index d5ce61c1f..000000000
--- a/runtime/ops/utils.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-
-use deno_core::error::custom_error;
-use deno_core::error::AnyError;
-
-/// A utility function to map OsStrings to Strings
-pub fn into_string(s: std::ffi::OsString) -> Result<String, AnyError> {
- s.into_string().map_err(|s| {
- let message = format!("File name or path {s:?} is not valid UTF-8");
- custom_error("InvalidData", message)
- })
-}
diff --git a/runtime/ops/web_worker.rs b/runtime/ops/web_worker.rs
index 0ed76ebd5..d0c3eea66 100644
--- a/runtime/ops/web_worker.rs
+++ b/runtime/ops/web_worker.rs
@@ -4,15 +4,16 @@ mod sync_fetch;
use crate::web_worker::WebWorkerInternalHandle;
use crate::web_worker::WebWorkerType;
-use deno_core::error::AnyError;
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;
use self::sync_fetch::op_worker_sync_fetch;
+pub use sync_fetch::SyncFetchError;
deno_core::extension!(
deno_web_worker,
@@ -30,17 +31,16 @@ deno_core::extension!(
fn op_worker_post_message(
state: &mut OpState,
#[serde] data: JsMessageData,
-) -> Result<(), AnyError> {
+) -> Result<(), MessagePortError> {
let handle = state.borrow::<WebWorkerInternalHandle>().clone();
- handle.port.send(state, data)?;
- Ok(())
+ handle.port.send(state, data)
}
#[op2(async(lazy), fast)]
#[serde]
async fn op_worker_recv_message(
state: Rc<RefCell<OpState>>,
-) -> Result<Option<JsMessageData>, AnyError> {
+) -> Result<Option<JsMessageData>, MessagePortError> {
let handle = {
let state = state.borrow();
state.borrow::<WebWorkerInternalHandle>().clone()
diff --git a/runtime/ops/web_worker/sync_fetch.rs b/runtime/ops/web_worker/sync_fetch.rs
index cdb151a86..d1f133d3d 100644
--- a/runtime/ops/web_worker/sync_fetch.rs
+++ b/runtime/ops/web_worker/sync_fetch.rs
@@ -4,15 +4,13 @@ use std::sync::Arc;
use crate::web_worker::WebWorkerInternalHandle;
use crate::web_worker::WebWorkerType;
-use deno_core::error::type_error;
-use deno_core::error::AnyError;
use deno_core::futures::StreamExt;
use deno_core::op2;
use deno_core::url::Url;
use deno_core::OpState;
use deno_fetch::data_url::DataUrl;
+use deno_fetch::FetchError;
use deno_web::BlobStore;
-use deno_websocket::DomExceptionNetworkError;
use http_body_util::BodyExt;
use hyper::body::Bytes;
use serde::Deserialize;
@@ -27,6 +25,32 @@ fn mime_type_essence(mime_type: &str) -> String {
essence.trim().to_ascii_lowercase()
}
+#[derive(Debug, thiserror::Error)]
+pub enum SyncFetchError {
+ #[error("Blob URLs are not supported in this context.")]
+ BlobUrlsNotSupportedInContext,
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+ #[error("Invalid script URL")]
+ InvalidScriptUrl,
+ #[error("http status error: {0}")]
+ InvalidStatusCode(http::StatusCode),
+ #[error("Classic scripts with scheme {0}: are not supported in workers")]
+ ClassicScriptSchemeUnsupportedInWorkers(String),
+ #[error("{0}")]
+ InvalidUri(#[from] http::uri::InvalidUri),
+ #[error("Invalid MIME type {0:?}.")]
+ InvalidMimeType(String),
+ #[error("Missing MIME type.")]
+ MissingMimeType,
+ #[error(transparent)]
+ Fetch(#[from] FetchError),
+ #[error(transparent)]
+ Join(#[from] tokio::task::JoinError),
+ #[error(transparent)]
+ Other(deno_core::error::AnyError),
+}
+
#[derive(Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SyncFetchScript {
@@ -40,21 +64,22 @@ pub fn op_worker_sync_fetch(
state: &mut OpState,
#[serde] scripts: Vec<String>,
loose_mime_checks: bool,
-) -> Result<Vec<SyncFetchScript>, AnyError> {
+) -> Result<Vec<SyncFetchScript>, SyncFetchError> {
let handle = state.borrow::<WebWorkerInternalHandle>().clone();
assert_eq!(handle.worker_type, WebWorkerType::Classic);
// it's not safe to share a client across tokio runtimes, so create a fresh one
// https://github.com/seanmonstar/reqwest/issues/1148#issuecomment-910868788
let options = state.borrow::<deno_fetch::Options>().clone();
- let client = deno_fetch::create_client_from_options(&options)?;
+ let client = deno_fetch::create_client_from_options(&options)
+ .map_err(FetchError::ClientCreate)?;
// TODO(andreubotella) It's not good to throw an exception related to blob
// URLs when none of the script URLs use the blob scheme.
// Also, in which contexts are blob URLs not supported?
let blob_store = state
.try_borrow::<Arc<BlobStore>>()
- .ok_or_else(|| type_error("Blob URLs are not supported in this context."))?
+ .ok_or(SyncFetchError::BlobUrlsNotSupportedInContext)?
.clone();
// TODO(andreubotella): make the below thread into a resource that can be
@@ -74,7 +99,7 @@ pub fn op_worker_sync_fetch(
let blob_store = blob_store.clone();
deno_core::unsync::spawn(async move {
let script_url = Url::parse(&script)
- .map_err(|_| type_error("Invalid script URL"))?;
+ .map_err(|_| SyncFetchError::InvalidScriptUrl)?;
let mut loose_mime_checks = loose_mime_checks;
let (body, mime_type, res_url) = match script_url.scheme() {
@@ -86,15 +111,13 @@ pub fn op_worker_sync_fetch(
);
*req.uri_mut() = script_url.as_str().parse()?;
- let resp = client.send(req).await?;
+ let resp =
+ client.send(req).await.map_err(FetchError::ClientSend)?;
if resp.status().is_client_error()
|| resp.status().is_server_error()
{
- return Err(type_error(format!(
- "http status error: {}",
- resp.status()
- )));
+ return Err(SyncFetchError::InvalidStatusCode(resp.status()));
}
// TODO(andreubotella) Properly run fetch's "extract a MIME type".
@@ -107,42 +130,45 @@ pub fn op_worker_sync_fetch(
// Always check the MIME type with HTTP(S).
loose_mime_checks = false;
- let body = resp.collect().await?.to_bytes();
+ let body = resp
+ .collect()
+ .await
+ .map_err(SyncFetchError::Other)?
+ .to_bytes();
(body, mime_type, script)
}
"data" => {
- let data_url = DataUrl::process(&script)
- .map_err(|e| type_error(format!("{e:?}")))?;
+ let data_url =
+ DataUrl::process(&script).map_err(FetchError::DataUrl)?;
let mime_type = {
let mime = data_url.mime_type();
format!("{}/{}", mime.type_, mime.subtype)
};
- let (body, _) = data_url
- .decode_to_vec()
- .map_err(|e| type_error(format!("{e:?}")))?;
+ let (body, _) =
+ data_url.decode_to_vec().map_err(FetchError::Base64)?;
(Bytes::from(body), Some(mime_type), script)
}
"blob" => {
- let blob =
- blob_store.get_object_url(script_url).ok_or_else(|| {
- type_error("Blob for the given URL not found.")
- })?;
+ let blob = blob_store
+ .get_object_url(script_url)
+ .ok_or(FetchError::BlobNotFound)?;
let mime_type = mime_type_essence(&blob.media_type);
- let body = blob.read_all().await?;
+ let body = blob.read_all().await;
(Bytes::from(body), Some(mime_type), script)
}
_ => {
- return Err(type_error(format!(
- "Classic scripts with scheme {}: are not supported in workers.",
- script_url.scheme()
- )))
+ return Err(
+ SyncFetchError::ClassicScriptSchemeUnsupportedInWorkers(
+ script_url.scheme().to_string(),
+ ),
+ )
}
};
@@ -151,18 +177,11 @@ pub fn op_worker_sync_fetch(
match mime_type.as_deref() {
Some("application/javascript" | "text/javascript") => {}
Some(mime_type) => {
- return Err(
- DomExceptionNetworkError {
- msg: format!("Invalid MIME type {mime_type:?}."),
- }
- .into(),
- )
- }
- None => {
- return Err(
- DomExceptionNetworkError::new("Missing MIME type.").into(),
- )
+ return Err(SyncFetchError::InvalidMimeType(
+ mime_type.to_string(),
+ ))
}
+ None => return Err(SyncFetchError::MissingMimeType),
}
}
diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs
index b9fd06654..521284a6a 100644
--- a/runtime/ops/worker_host.rs
+++ b/runtime/ops/worker_host.rs
@@ -10,7 +10,6 @@ use crate::web_worker::WorkerControlEvent;
use crate::web_worker::WorkerId;
use crate::web_worker::WorkerMetadata;
use crate::worker::FormatJsErrorFn;
-use deno_core::error::AnyError;
use deno_core::op2;
use deno_core::serde::Deserialize;
use deno_core::CancelFuture;
@@ -21,6 +20,7 @@ use deno_permissions::ChildPermissionsArg;
use deno_permissions::PermissionsContainer;
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;
@@ -118,6 +118,20 @@ pub struct CreateWorkerArgs {
close_on_idle: bool,
}
+#[derive(Debug, thiserror::Error)]
+pub enum CreateWorkerError {
+ #[error("Classic workers are not supported.")]
+ ClassicWorkers,
+ #[error(transparent)]
+ Permission(deno_permissions::ChildPermissionError),
+ #[error(transparent)]
+ ModuleResolution(#[from] deno_core::ModuleResolutionError),
+ #[error(transparent)]
+ MessagePort(#[from] MessagePortError),
+ #[error("{0}")]
+ Io(#[from] std::io::Error),
+}
+
/// Create worker as the host
#[op2]
#[serde]
@@ -125,7 +139,7 @@ fn op_create_worker(
state: &mut OpState,
#[serde] args: CreateWorkerArgs,
#[serde] maybe_worker_metadata: Option<JsMessageData>,
-) -> Result<WorkerId, AnyError> {
+) -> Result<WorkerId, CreateWorkerError> {
let specifier = args.specifier.clone();
let maybe_source_code = if args.has_source_code {
Some(args.source_code.clone())
@@ -136,12 +150,7 @@ fn op_create_worker(
let worker_type = args.worker_type;
if let WebWorkerType::Classic = worker_type {
if let TestingFeaturesEnabled(false) = state.borrow() {
- return Err(
- deno_webstorage::DomExceptionNotSupportedError::new(
- "Classic workers are not supported.",
- )
- .into(),
- );
+ return Err(CreateWorkerError::ClassicWorkers);
}
}
@@ -155,7 +164,9 @@ fn op_create_worker(
let parent_permissions = state.borrow_mut::<PermissionsContainer>();
let worker_permissions = if let Some(child_permissions_arg) = args.permissions
{
- parent_permissions.create_child_permissions(child_permissions_arg)?
+ parent_permissions
+ .create_child_permissions(child_permissions_arg)
+ .map_err(CreateWorkerError::Permission)?
} else {
parent_permissions.clone()
};
@@ -167,9 +178,8 @@ fn op_create_worker(
let module_specifier = deno_core::resolve_url(&specifier)?;
let worker_name = args_name.unwrap_or_default();
- let (handle_sender, handle_receiver) = std::sync::mpsc::sync_channel::<
- Result<SendableWebWorkerHandle, AnyError>,
- >(1);
+ let (handle_sender, handle_receiver) =
+ std::sync::mpsc::sync_channel::<SendableWebWorkerHandle>(1);
// Setup new thread
let thread_builder = std::thread::Builder::new().name(format!("{worker_id}"));
@@ -203,7 +213,7 @@ fn op_create_worker(
});
// Send thread safe handle from newly created worker to host thread
- handle_sender.send(Ok(external_handle)).unwrap();
+ handle_sender.send(external_handle).unwrap();
drop(handle_sender);
// At this point the only method of communication with host
@@ -219,7 +229,7 @@ fn op_create_worker(
})?;
// Receive WebWorkerHandle from newly created worker
- let worker_handle = handle_receiver.recv().unwrap()?;
+ let worker_handle = handle_receiver.recv().unwrap();
let worker_thread = WorkerThread {
worker_handle: worker_handle.into(),
@@ -292,7 +302,7 @@ fn close_channel(
async fn op_host_recv_ctrl(
state: Rc<RefCell<OpState>>,
#[serde] id: WorkerId,
-) -> Result<WorkerControlEvent, AnyError> {
+) -> WorkerControlEvent {
let (worker_handle, cancel_handle) = {
let state = state.borrow();
let workers_table = state.borrow::<WorkersTable>();
@@ -301,7 +311,7 @@ async fn op_host_recv_ctrl(
(handle.worker_handle.clone(), handle.cancel_handle.clone())
} else {
// If handle was not found it means worker has already shutdown
- return Ok(WorkerControlEvent::Close);
+ return WorkerControlEvent::Close;
}
};
@@ -310,22 +320,21 @@ async fn op_host_recv_ctrl(
.or_cancel(cancel_handle)
.await;
match maybe_event {
- Ok(Ok(Some(event))) => {
+ Ok(Some(event)) => {
// Terminal error means that worker should be removed from worker table.
if let WorkerControlEvent::TerminalError(_) = &event {
close_channel(state, id, WorkerChannel::Ctrl);
}
- Ok(event)
+ event
}
- Ok(Ok(None)) => {
+ Ok(None) => {
// If there was no event from worker it means it has already been closed.
close_channel(state, id, WorkerChannel::Ctrl);
- Ok(WorkerControlEvent::Close)
+ WorkerControlEvent::Close
}
- Ok(Err(err)) => Err(err),
Err(_) => {
// The worker was terminated.
- Ok(WorkerControlEvent::Close)
+ WorkerControlEvent::Close
}
}
}
@@ -335,7 +344,7 @@ async fn op_host_recv_ctrl(
async fn op_host_recv_message(
state: Rc<RefCell<OpState>>,
#[serde] id: WorkerId,
-) -> Result<Option<JsMessageData>, AnyError> {
+) -> Result<Option<JsMessageData>, MessagePortError> {
let (worker_handle, cancel_handle) = {
let s = state.borrow();
let workers_table = s.borrow::<WorkersTable>();
@@ -374,7 +383,7 @@ fn op_host_post_message(
state: &mut OpState,
#[serde] id: WorkerId,
#[serde] data: JsMessageData,
-) -> Result<(), AnyError> {
+) -> Result<(), MessagePortError> {
if let Some(worker_thread) = state.borrow::<WorkersTable>().get(&id) {
debug!("post message to worker {}", id);
let worker_handle = worker_thread.worker_handle.clone();
diff --git a/runtime/permissions.rs b/runtime/permissions.rs
index fa62227e0..e8460e03f 100644
--- a/runtime/permissions.rs
+++ b/runtime/permissions.rs
@@ -3,9 +3,6 @@
use std::path::Path;
use std::path::PathBuf;
-use deno_core::anyhow::bail;
-use deno_core::anyhow::Context;
-use deno_core::error::AnyError;
use deno_path_util::normalize_path;
use deno_permissions::AllowRunDescriptor;
use deno_permissions::AllowRunDescriptorParseResult;
@@ -15,9 +12,12 @@ use deno_permissions::FfiDescriptor;
use deno_permissions::ImportDescriptor;
use deno_permissions::NetDescriptor;
use deno_permissions::PathQueryDescriptor;
+use deno_permissions::PathResolveError;
use deno_permissions::ReadDescriptor;
+use deno_permissions::RunDescriptorParseError;
use deno_permissions::RunQueryDescriptor;
use deno_permissions::SysDescriptor;
+use deno_permissions::SysDescriptorParseError;
use deno_permissions::WriteDescriptor;
#[derive(Debug)]
@@ -30,9 +30,9 @@ impl RuntimePermissionDescriptorParser {
Self { fs }
}
- fn resolve_from_cwd(&self, path: &str) -> Result<PathBuf, AnyError> {
+ fn resolve_from_cwd(&self, path: &str) -> Result<PathBuf, PathResolveError> {
if path.is_empty() {
- bail!("Empty path is not allowed");
+ return Err(PathResolveError::EmptyPath);
}
let path = Path::new(path);
if path.is_absolute() {
@@ -43,12 +43,11 @@ impl RuntimePermissionDescriptorParser {
}
}
- fn resolve_cwd(&self) -> Result<PathBuf, AnyError> {
+ fn resolve_cwd(&self) -> Result<PathBuf, PathResolveError> {
self
.fs
.cwd()
- .map_err(|e| e.into_io_error())
- .context("failed resolving cwd")
+ .map_err(|e| PathResolveError::CwdResolve(e.into_io_error()))
}
}
@@ -58,37 +57,37 @@ impl deno_permissions::PermissionDescriptorParser
fn parse_read_descriptor(
&self,
text: &str,
- ) -> Result<ReadDescriptor, AnyError> {
+ ) -> Result<ReadDescriptor, PathResolveError> {
Ok(ReadDescriptor(self.resolve_from_cwd(text)?))
}
fn parse_write_descriptor(
&self,
text: &str,
- ) -> Result<WriteDescriptor, AnyError> {
+ ) -> Result<WriteDescriptor, PathResolveError> {
Ok(WriteDescriptor(self.resolve_from_cwd(text)?))
}
fn parse_net_descriptor(
&self,
text: &str,
- ) -> Result<NetDescriptor, AnyError> {
+ ) -> Result<NetDescriptor, deno_permissions::NetDescriptorParseError> {
NetDescriptor::parse(text)
}
fn parse_import_descriptor(
&self,
text: &str,
- ) -> Result<ImportDescriptor, AnyError> {
+ ) -> Result<ImportDescriptor, deno_permissions::NetDescriptorParseError> {
ImportDescriptor::parse(text)
}
fn parse_env_descriptor(
&self,
text: &str,
- ) -> Result<EnvDescriptor, AnyError> {
+ ) -> Result<EnvDescriptor, deno_permissions::EnvDescriptorParseError> {
if text.is_empty() {
- Err(AnyError::msg("Empty env not allowed"))
+ Err(deno_permissions::EnvDescriptorParseError)
} else {
Ok(EnvDescriptor::new(text))
}
@@ -97,9 +96,9 @@ impl deno_permissions::PermissionDescriptorParser
fn parse_sys_descriptor(
&self,
text: &str,
- ) -> Result<deno_permissions::SysDescriptor, AnyError> {
+ ) -> Result<SysDescriptor, SysDescriptorParseError> {
if text.is_empty() {
- Err(AnyError::msg("Empty sys not allowed"))
+ Err(SysDescriptorParseError::Empty)
} else {
Ok(SysDescriptor::parse(text.to_string())?)
}
@@ -108,21 +107,21 @@ impl deno_permissions::PermissionDescriptorParser
fn parse_allow_run_descriptor(
&self,
text: &str,
- ) -> Result<AllowRunDescriptorParseResult, AnyError> {
+ ) -> Result<AllowRunDescriptorParseResult, RunDescriptorParseError> {
Ok(AllowRunDescriptor::parse(text, &self.resolve_cwd()?)?)
}
fn parse_deny_run_descriptor(
&self,
text: &str,
- ) -> Result<DenyRunDescriptor, AnyError> {
+ ) -> Result<DenyRunDescriptor, PathResolveError> {
Ok(DenyRunDescriptor::parse(text, &self.resolve_cwd()?))
}
fn parse_ffi_descriptor(
&self,
text: &str,
- ) -> Result<deno_permissions::FfiDescriptor, AnyError> {
+ ) -> Result<FfiDescriptor, PathResolveError> {
Ok(FfiDescriptor(self.resolve_from_cwd(text)?))
}
@@ -131,7 +130,7 @@ impl deno_permissions::PermissionDescriptorParser
fn parse_path_query(
&self,
path: &str,
- ) -> Result<PathQueryDescriptor, AnyError> {
+ ) -> Result<PathQueryDescriptor, PathResolveError> {
Ok(PathQueryDescriptor {
resolved: self.resolve_from_cwd(path)?,
requested: path.to_string(),
@@ -141,11 +140,12 @@ impl deno_permissions::PermissionDescriptorParser
fn parse_run_query(
&self,
requested: &str,
- ) -> Result<RunQueryDescriptor, AnyError> {
+ ) -> Result<RunQueryDescriptor, RunDescriptorParseError> {
if requested.is_empty() {
- bail!("Empty run query is not allowed");
+ return Err(RunDescriptorParseError::EmptyRunQuery);
}
RunQueryDescriptor::parse(requested)
+ .map_err(RunDescriptorParseError::PathResolve)
}
}
diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml
index 8f0b0dd3d..e088eb8ce 100644
--- a/runtime/permissions/Cargo.toml
+++ b/runtime/permissions/Cargo.toml
@@ -2,7 +2,7 @@
[package]
name = "deno_permissions"
-version = "0.31.0"
+version = "0.37.0"
authors.workspace = true
edition.workspace = true
license.workspace = true
@@ -23,6 +23,7 @@ log.workspace = true
once_cell.workspace = true
percent-encoding = { version = "2.3.1", features = [] }
serde.workspace = true
+thiserror.workspace = true
which.workspace = true
[target.'cfg(windows)'.dependencies]
diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs
index 2904242da..71ef7d228 100644
--- a/runtime/permissions/lib.rs
+++ b/runtime/permissions/lib.rs
@@ -1,11 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::anyhow::bail;
-use deno_core::anyhow::Context;
-use deno_core::error::custom_error;
-use deno_core::error::type_error;
-use deno_core::error::uri_error;
-use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_core::serde::de;
use deno_core::serde::Deserialize;
@@ -35,11 +29,28 @@ use std::sync::Arc;
pub mod prompter;
use prompter::permission_prompt;
-use prompter::PromptResponse;
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;
+
+#[derive(Debug, thiserror::Error)]
+#[error("Requires {access}, {}", format_permission_error(.name))]
+pub struct PermissionDeniedError {
+ pub access: String,
+ pub name: &'static str,
+}
+
+fn format_permission_error(name: &'static str) -> String {
+ if is_standalone() {
+ format!("specify the required permissions during compilation using `deno compile --allow-{name}`")
+ } else {
+ format!("run again with the --allow-{name} flag")
+ }
+}
/// Fast exit from permission check routines if this permission
/// is in the "fully-granted" state.
@@ -102,7 +113,10 @@ impl From<bool> for AllowPartial {
impl PermissionState {
#[inline(always)]
- fn log_perm_access(name: &str, info: impl FnOnce() -> Option<String>) {
+ fn log_perm_access(
+ name: &'static str,
+ info: impl FnOnce() -> Option<String>,
+ ) {
// Eliminates log overhead (when logging is disabled),
// log_enabled!(Debug) check in a hot path still has overhead
// TODO(AaronO): generalize or upstream this optimization
@@ -118,53 +132,47 @@ impl PermissionState {
}
}
- fn fmt_access(name: &str, info: impl FnOnce() -> Option<String>) -> String {
+ fn fmt_access(
+ name: &'static str,
+ info: impl FnOnce() -> Option<String>,
+ ) -> String {
format!(
"{} access{}",
name,
- info()
- .map(|info| { format!(" to {info}") })
- .unwrap_or_default(),
+ info().map(|info| format!(" to {info}")).unwrap_or_default(),
)
}
- fn error(name: &str, info: impl FnOnce() -> Option<String>) -> AnyError {
- let msg = if is_standalone() {
- format!(
- "Requires {}, specify the required permissions during compilation using `deno compile --allow-{}`",
- Self::fmt_access(name, info),
- name
- )
- } else {
- format!(
- "Requires {}, run again with the --allow-{} flag",
- Self::fmt_access(name, info),
- name
- )
- };
- custom_error("NotCapable", msg)
+ fn error(
+ name: &'static str,
+ info: impl FnOnce() -> Option<String>,
+ ) -> PermissionDeniedError {
+ PermissionDeniedError {
+ access: Self::fmt_access(name, info),
+ name,
+ }
}
/// Check the permission state. bool is whether a prompt was issued.
#[inline]
fn check(
self,
- name: &str,
+ name: &'static str,
api_name: Option<&str>,
info: Option<&str>,
prompt: bool,
- ) -> (Result<(), AnyError>, bool, bool) {
+ ) -> (Result<(), PermissionDeniedError>, bool, bool) {
self.check2(name, api_name, || info.map(|s| s.to_string()), prompt)
}
#[inline]
fn check2(
self,
- name: &str,
+ name: &'static str,
api_name: Option<&str>,
info: impl Fn() -> Option<String>,
prompt: bool,
- ) -> (Result<(), AnyError>, bool, bool) {
+ ) -> (Result<(), PermissionDeniedError>, bool, bool) {
match self {
PermissionState::Granted => {
Self::log_perm_access(name, info);
@@ -244,7 +252,7 @@ impl UnitPermission {
self.state
}
- pub fn check(&mut self) -> Result<(), AnyError> {
+ pub fn check(&mut self) -> Result<(), PermissionDeniedError> {
let (result, prompted, _is_allow_all) =
self.state.check(self.name, None, None, self.prompt);
if prompted {
@@ -260,7 +268,7 @@ impl UnitPermission {
fn create_child_permissions(
&mut self,
flag: ChildUnitPermissionArg,
- ) -> Result<Self, AnyError> {
+ ) -> Result<Self, ChildPermissionError> {
let mut perm = self.clone();
match flag {
ChildUnitPermissionArg::Inherit => {
@@ -268,7 +276,7 @@ impl UnitPermission {
}
ChildUnitPermissionArg::Granted => {
if self.check().is_err() {
- return Err(escalation_error());
+ return Err(ChildPermissionError::Escalation);
}
perm.state = PermissionState::Granted;
}
@@ -325,7 +333,7 @@ pub trait QueryDescriptor: Debug {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError>;
+ ) -> Result<(), PermissionDeniedError>;
fn matches_allow(&self, other: &Self::AllowDesc) -> bool;
fn matches_deny(&self, other: &Self::DenyDesc) -> bool;
@@ -400,7 +408,7 @@ impl<TQuery: QueryDescriptor> UnaryPermission<TQuery> {
pub fn check_all_api(
&mut self,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, api_name)
}
@@ -410,7 +418,7 @@ impl<TQuery: QueryDescriptor> UnaryPermission<TQuery> {
desc: Option<&TQuery>,
assert_non_partial: bool,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
let (result, prompted, is_allow_all) = self
.query_desc(desc, AllowPartial::from(!assert_non_partial))
.check2(
@@ -597,11 +605,14 @@ impl<TQuery: QueryDescriptor> UnaryPermission<TQuery> {
}
}
- fn create_child_permissions(
+ fn create_child_permissions<E>(
&mut self,
flag: ChildUnaryPermissionArg,
- parse: impl Fn(&str) -> Result<Option<TQuery::AllowDesc>, AnyError>,
- ) -> Result<UnaryPermission<TQuery>, AnyError> {
+ parse: impl Fn(&str) -> Result<Option<TQuery::AllowDesc>, E>,
+ ) -> Result<UnaryPermission<TQuery>, ChildPermissionError>
+ where
+ ChildPermissionError: From<E>,
+ {
let mut perms = Self::default();
match flag {
@@ -610,7 +621,7 @@ impl<TQuery: QueryDescriptor> UnaryPermission<TQuery> {
}
ChildUnaryPermissionArg::Granted => {
if self.check_all_api(None).is_err() {
- return Err(escalation_error());
+ return Err(ChildPermissionError::Escalation);
}
perms.granted_global = true;
}
@@ -619,13 +630,13 @@ impl<TQuery: QueryDescriptor> UnaryPermission<TQuery> {
perms.granted_list = granted_list
.iter()
.filter_map(|i| parse(i).transpose())
- .collect::<Result<_, _>>()?;
+ .collect::<Result<_, E>>()?;
if !perms.granted_list.iter().all(|desc| {
TQuery::from_allow(desc)
.check_in_permission(self, None)
.is_ok()
}) {
- return Err(escalation_error());
+ return Err(ChildPermissionError::Escalation);
}
}
}
@@ -696,7 +707,7 @@ impl QueryDescriptor for ReadQueryDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), true, api_name)
}
@@ -759,7 +770,7 @@ impl QueryDescriptor for WriteQueryDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), true, api_name)
}
@@ -794,22 +805,37 @@ pub enum Host {
Ip(IpAddr),
}
+#[derive(Debug, thiserror::Error)]
+pub enum HostParseError {
+ #[error("invalid IPv6 address: '{0}'")]
+ InvalidIpv6(String),
+ #[error("invalid host: '{0}'")]
+ InvalidHost(String),
+ #[error("invalid empty host: '{0}'")]
+ InvalidEmptyHost(String),
+ #[error("invalid host '{host}': {error}")]
+ Fqdn {
+ #[source]
+ error: fqdn::Error,
+ host: String,
+ },
+}
+
impl Host {
- // TODO(bartlomieju): rewrite to not use `AnyError` but a specific error implementations
- fn parse(s: &str) -> Result<Self, AnyError> {
+ fn parse(s: &str) -> Result<Self, HostParseError> {
if s.starts_with('[') && s.ends_with(']') {
let ip = s[1..s.len() - 1]
.parse::<Ipv6Addr>()
- .map_err(|_| uri_error(format!("invalid IPv6 address: '{s}'")))?;
+ .map_err(|_| HostParseError::InvalidIpv6(s.to_string()))?;
return Ok(Host::Ip(IpAddr::V6(ip)));
}
let (without_trailing_dot, has_trailing_dot) =
s.strip_suffix('.').map_or((s, false), |s| (s, true));
if let Ok(ip) = without_trailing_dot.parse::<IpAddr>() {
if has_trailing_dot {
- return Err(uri_error(format!(
- "invalid host: '{without_trailing_dot}'"
- )));
+ return Err(HostParseError::InvalidHost(
+ without_trailing_dot.to_string(),
+ ));
}
Ok(Host::Ip(ip))
} else {
@@ -820,11 +846,13 @@ impl Host {
};
let fqdn = {
use std::str::FromStr;
- FQDN::from_str(&lower)
- .with_context(|| format!("invalid host: '{s}'"))?
+ FQDN::from_str(&lower).map_err(|e| HostParseError::Fqdn {
+ error: e,
+ host: s.to_string(),
+ })?
};
if fqdn.is_root() {
- return Err(uri_error(format!("invalid empty host: '{s}'")));
+ return Err(HostParseError::InvalidEmptyHost(s.to_string()));
}
Ok(Host::Fqdn(fqdn))
}
@@ -868,7 +896,7 @@ impl QueryDescriptor for NetDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), false, api_name)
}
@@ -894,39 +922,72 @@ impl QueryDescriptor for NetDescriptor {
}
}
-// TODO(bartlomieju): rewrite to not use `AnyError` but a specific error implementations
+#[derive(Debug, thiserror::Error)]
+pub enum NetDescriptorParseError {
+ #[error("invalid value '{0}': URLs are not supported, only domains and ips")]
+ Url(String),
+ #[error("invalid IPv6 address in '{hostname}': '{ip}'")]
+ InvalidIpv6 { hostname: String, ip: String },
+ #[error("invalid port in '{hostname}': '{port}'")]
+ InvalidPort { hostname: String, port: String },
+ #[error("invalid host: '{0}'")]
+ InvalidHost(String),
+ #[error("invalid empty port in '{0}'")]
+ EmptyPort(String),
+ #[error("ipv6 addresses must be enclosed in square brackets: '{0}'")]
+ Ipv6MissingSquareBrackets(String),
+ #[error("{0}")]
+ Host(#[from] HostParseError),
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum NetDescriptorFromUrlParseError {
+ #[error("Missing host in url: '{0}'")]
+ MissingHost(Url),
+ #[error("{0}")]
+ Host(#[from] HostParseError),
+}
+
impl NetDescriptor {
- pub fn parse(hostname: &str) -> Result<Self, AnyError> {
+ pub fn parse(hostname: &str) -> Result<Self, NetDescriptorParseError> {
if hostname.starts_with("http://") || hostname.starts_with("https://") {
- return Err(uri_error(format!("invalid value '{hostname}': URLs are not supported, only domains and ips")));
+ return Err(NetDescriptorParseError::Url(hostname.to_string()));
}
// If this is a IPv6 address enclosed in square brackets, parse it as such.
if hostname.starts_with('[') {
if let Some((ip, after)) = hostname.split_once(']') {
let ip = ip[1..].parse::<Ipv6Addr>().map_err(|_| {
- uri_error(format!("invalid IPv6 address in '{hostname}': '{ip}'"))
+ NetDescriptorParseError::InvalidIpv6 {
+ hostname: hostname.to_string(),
+ ip: ip.to_string(),
+ }
})?;
let port = if let Some(port) = after.strip_prefix(':') {
let port = port.parse::<u16>().map_err(|_| {
- uri_error(format!("invalid port in '{hostname}': '{port}'"))
+ NetDescriptorParseError::InvalidPort {
+ hostname: hostname.to_string(),
+ port: port.to_string(),
+ }
})?;
Some(port)
} else if after.is_empty() {
None
} else {
- return Err(uri_error(format!("invalid host: '{hostname}'")));
+ return Err(NetDescriptorParseError::InvalidHost(
+ hostname.to_string(),
+ ));
};
return Ok(NetDescriptor(Host::Ip(IpAddr::V6(ip)), port));
} else {
- return Err(uri_error(format!("invalid host: '{hostname}'")));
+ return Err(NetDescriptorParseError::InvalidHost(hostname.to_string()));
}
}
// Otherwise it is an IPv4 address or a FQDN with an optional port.
let (host, port) = match hostname.split_once(':') {
Some((_, "")) => {
- return Err(uri_error(format!("invalid empty port in '{hostname}'")));
+ return Err(NetDescriptorParseError::EmptyPort(hostname.to_string()));
}
Some((host, port)) => (host, port),
None => (hostname, ""),
@@ -941,11 +1002,14 @@ impl NetDescriptor {
// should give them a hint. There are always at least two colons in an
// IPv6 address, so this heuristic finds likely a bare IPv6 address.
if port.contains(':') {
- uri_error(format!(
- "ipv6 addresses must be enclosed in square brackets: '{hostname}'"
- ))
+ NetDescriptorParseError::Ipv6MissingSquareBrackets(
+ hostname.to_string(),
+ )
} else {
- uri_error(format!("invalid port in '{hostname}': '{port}'"))
+ NetDescriptorParseError::InvalidPort {
+ hostname: hostname.to_string(),
+ port: port.to_string(),
+ }
}
})?;
Some(port)
@@ -954,10 +1018,10 @@ impl NetDescriptor {
Ok(NetDescriptor(host, port))
}
- pub fn from_url(url: &Url) -> Result<Self, AnyError> {
- let host = url
- .host_str()
- .ok_or_else(|| type_error(format!("Missing host in url: '{}'", url)))?;
+ pub fn from_url(url: &Url) -> Result<Self, NetDescriptorFromUrlParseError> {
+ let host = url.host_str().ok_or_else(|| {
+ NetDescriptorFromUrlParseError::MissingHost(url.clone())
+ })?;
let host = Host::parse(host)?;
let port = url.port_or_known_default();
Ok(NetDescriptor(host, port))
@@ -1009,7 +1073,7 @@ impl QueryDescriptor for ImportDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), false, api_name)
}
@@ -1036,15 +1100,19 @@ impl QueryDescriptor for ImportDescriptor {
}
impl ImportDescriptor {
- pub fn parse(specifier: &str) -> Result<Self, AnyError> {
+ pub fn parse(specifier: &str) -> Result<Self, NetDescriptorParseError> {
Ok(ImportDescriptor(NetDescriptor::parse(specifier)?))
}
- pub fn from_url(url: &Url) -> Result<Self, AnyError> {
+ pub fn from_url(url: &Url) -> Result<Self, NetDescriptorFromUrlParseError> {
Ok(ImportDescriptor(NetDescriptor::from_url(url)?))
}
}
+#[derive(Debug, thiserror::Error)]
+#[error("Empty env not allowed")]
+pub struct EnvDescriptorParseError;
+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct EnvDescriptor(EnvVarName);
@@ -1082,7 +1150,7 @@ impl QueryDescriptor for EnvDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), false, api_name)
}
@@ -1129,14 +1197,25 @@ pub enum RunQueryDescriptor {
Name(String),
}
+#[derive(Debug, thiserror::Error)]
+pub enum PathResolveError {
+ #[error("failed resolving cwd: {0}")]
+ CwdResolve(#[source] std::io::Error),
+ #[error("Empty path is not allowed")]
+ EmptyPath,
+}
+
impl RunQueryDescriptor {
- pub fn parse(requested: &str) -> Result<RunQueryDescriptor, AnyError> {
+ pub fn parse(
+ requested: &str,
+ ) -> Result<RunQueryDescriptor, PathResolveError> {
if is_path(requested) {
let path = PathBuf::from(requested);
let resolved = if path.is_absolute() {
normalize_path(path)
} else {
- let cwd = std::env::current_dir().context("failed resolving cwd")?;
+ let cwd =
+ std::env::current_dir().map_err(PathResolveError::CwdResolve)?;
normalize_path(cwd.join(path))
};
Ok(RunQueryDescriptor::Path {
@@ -1208,7 +1287,7 @@ impl QueryDescriptor for RunQueryDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), false, api_name)
}
@@ -1278,6 +1357,16 @@ pub enum AllowRunDescriptorParseResult {
Descriptor(AllowRunDescriptor),
}
+#[derive(Debug, thiserror::Error)]
+pub enum RunDescriptorParseError {
+ #[error("{0}")]
+ Which(#[from] which::Error),
+ #[error("{0}")]
+ PathResolve(#[from] PathResolveError),
+ #[error("Empty run query is not allowed")]
+ EmptyRunQuery,
+}
+
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
pub struct AllowRunDescriptor(pub PathBuf);
@@ -1358,17 +1447,29 @@ fn denies_run_name(name: &str, cmd_path: &Path) -> bool {
suffix.is_empty() || suffix.starts_with('.')
}
+#[derive(Debug, thiserror::Error)]
+pub enum SysDescriptorParseError {
+ #[error("unknown system info kind \"{0}\"")]
+ InvalidKind(String), // TypeError
+ #[error("Empty sys not allowed")]
+ Empty, // Error
+}
+
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
pub struct SysDescriptor(String);
impl SysDescriptor {
- pub fn parse(kind: String) -> Result<Self, AnyError> {
+ pub fn parse(kind: String) -> Result<Self, SysDescriptorParseError> {
match kind.as_str() {
- "hostname" | "osRelease" | "osUptime" | "loadavg"
+ "hostname" | "inspector" | "osRelease" | "osUptime" | "loadavg"
| "networkInterfaces" | "systemMemoryInfo" | "uid" | "gid" | "cpus"
- | "homedir" | "getegid" | "username" | "statfs" | "getPriority"
- | "setPriority" => Ok(Self(kind)),
- _ => Err(type_error(format!("unknown system info kind \"{kind}\""))),
+ | "homedir" | "getegid" | "statfs" | "getPriority" | "setPriority"
+ | "userInfo" => Ok(Self(kind)),
+
+ // the underlying permission check changed to `userInfo` to better match the API,
+ // alias this to avoid breaking existing projects with `--allow-sys=username`
+ "username" => Ok(Self("userInfo".into())),
+ _ => Err(SysDescriptorParseError::InvalidKind(kind)),
}
}
@@ -1405,7 +1506,7 @@ impl QueryDescriptor for SysDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), false, api_name)
}
@@ -1466,7 +1567,7 @@ impl QueryDescriptor for FfiQueryDescriptor {
&self,
perm: &mut UnaryPermission<Self>,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(perm);
perm.check_desc(Some(self), true, api_name)
}
@@ -1518,7 +1619,7 @@ impl UnaryPermission<ReadQueryDescriptor> {
&mut self,
desc: &ReadQueryDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(desc), true, api_name)
}
@@ -1528,12 +1629,15 @@ impl UnaryPermission<ReadQueryDescriptor> {
&mut self,
desc: &ReadQueryDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(desc), false, api_name)
}
- pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
+ pub fn check_all(
+ &mut self,
+ api_name: Option<&str>,
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, api_name)
}
@@ -1562,7 +1666,7 @@ impl UnaryPermission<WriteQueryDescriptor> {
&mut self,
path: &WriteQueryDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(path), true, api_name)
}
@@ -1572,12 +1676,15 @@ impl UnaryPermission<WriteQueryDescriptor> {
&mut self,
path: &WriteQueryDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(path), false, api_name)
}
- pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
+ pub fn check_all(
+ &mut self,
+ api_name: Option<&str>,
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, api_name)
}
@@ -1600,12 +1707,12 @@ impl UnaryPermission<NetDescriptor> {
&mut self,
host: &NetDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(host), false, api_name)
}
- pub fn check_all(&mut self) -> Result<(), AnyError> {
+ pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, None)
}
@@ -1631,12 +1738,12 @@ impl UnaryPermission<ImportDescriptor> {
&mut self,
host: &ImportDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(host), false, api_name)
}
- pub fn check_all(&mut self) -> Result<(), AnyError> {
+ pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, None)
}
@@ -1662,12 +1769,12 @@ impl UnaryPermission<EnvDescriptor> {
&mut self,
env: &str,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(&EnvDescriptor::new(env)), false, api_name)
}
- pub fn check_all(&mut self) -> Result<(), AnyError> {
+ pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, None)
}
@@ -1690,12 +1797,12 @@ impl UnaryPermission<SysDescriptor> {
&mut self,
kind: &SysDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(kind), false, api_name)
}
- pub fn check_all(&mut self) -> Result<(), AnyError> {
+ pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, None)
}
@@ -1724,11 +1831,14 @@ impl UnaryPermission<RunQueryDescriptor> {
&mut self,
cmd: &RunQueryDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
self.check_desc(Some(cmd), false, api_name)
}
- pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> {
+ pub fn check_all(
+ &mut self,
+ api_name: Option<&str>,
+ ) -> Result<(), PermissionDeniedError> {
self.check_desc(None, false, api_name)
}
@@ -1771,7 +1881,7 @@ impl UnaryPermission<FfiQueryDescriptor> {
&mut self,
path: &FfiQueryDescriptor,
api_name: Option<&str>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(Some(path), true, api_name)
}
@@ -1779,12 +1889,12 @@ impl UnaryPermission<FfiQueryDescriptor> {
pub fn check_partial(
&mut self,
path: Option<&FfiQueryDescriptor>,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(path, false, None)
}
- pub fn check_all(&mut self) -> Result<(), AnyError> {
+ pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> {
skip_check_if_is_permission_fully_granted!(self);
self.check_desc(None, false, Some("all"))
}
@@ -1824,23 +1934,39 @@ pub struct PermissionsOptions {
pub prompt: bool,
}
+#[derive(Debug, thiserror::Error)]
+pub enum PermissionsFromOptionsError {
+ #[error("{0}")]
+ PathResolve(#[from] PathResolveError),
+ #[error("{0}")]
+ SysDescriptorParse(#[from] SysDescriptorParseError),
+ #[error("{0}")]
+ NetDescriptorParse(#[from] NetDescriptorParseError),
+ #[error("{0}")]
+ EnvDescriptorParse(#[from] EnvDescriptorParseError),
+ #[error("{0}")]
+ RunDescriptorParse(#[from] RunDescriptorParseError),
+ #[error("Empty command name not allowed in --allow-run=...")]
+ RunEmptyCommandName,
+}
+
impl Permissions {
pub fn new_unary<TQuery>(
allow_list: Option<HashSet<TQuery::AllowDesc>>,
deny_list: Option<HashSet<TQuery::DenyDesc>>,
prompt: bool,
- ) -> Result<UnaryPermission<TQuery>, AnyError>
+ ) -> UnaryPermission<TQuery>
where
TQuery: QueryDescriptor,
{
- Ok(UnaryPermission::<TQuery> {
+ UnaryPermission::<TQuery> {
granted_global: global_from_option(allow_list.as_ref()),
granted_list: allow_list.unwrap_or_default(),
flag_denied_global: global_from_option(deny_list.as_ref()),
flag_denied_list: deny_list.unwrap_or_default(),
prompt,
..Default::default()
- })
+ }
}
pub const fn new_all(allow_state: bool) -> UnitPermission {
@@ -1856,15 +1982,15 @@ impl Permissions {
pub fn from_options(
parser: &dyn PermissionDescriptorParser,
opts: &PermissionsOptions,
- ) -> Result<Self, AnyError> {
+ ) -> Result<Self, PermissionsFromOptionsError> {
fn resolve_allow_run(
parser: &dyn PermissionDescriptorParser,
allow_run: &[String],
- ) -> Result<HashSet<AllowRunDescriptor>, AnyError> {
+ ) -> Result<HashSet<AllowRunDescriptor>, PermissionsFromOptionsError> {
let mut new_allow_run = HashSet::with_capacity(allow_run.len());
for unresolved in allow_run {
if unresolved.is_empty() {
- bail!("Empty command name not allowed in --allow-run=...")
+ return Err(PermissionsFromOptionsError::RunEmptyCommandName);
}
match parser.parse_allow_run_descriptor(unresolved)? {
AllowRunDescriptorParseResult::Descriptor(descriptor) => {
@@ -1883,10 +2009,13 @@ impl Permissions {
Ok(new_allow_run)
}
- fn parse_maybe_vec<T: Eq + PartialEq + Hash>(
+ fn parse_maybe_vec<T: Eq + PartialEq + Hash, E>(
items: Option<&[String]>,
- parse: impl Fn(&str) -> Result<T, AnyError>,
- ) -> Result<Option<HashSet<T>>, AnyError> {
+ parse: impl Fn(&str) -> Result<T, E>,
+ ) -> Result<Option<HashSet<T>>, PermissionsFromOptionsError>
+ where
+ PermissionsFromOptionsError: From<E>,
+ {
match items {
Some(items) => Ok(Some(
items
@@ -1938,14 +2067,14 @@ impl Permissions {
parser.parse_read_descriptor(item)
})?,
opts.prompt,
- )?,
+ ),
write: Permissions::new_unary(
parse_maybe_vec(opts.allow_write.as_deref(), |item| {
parser.parse_write_descriptor(item)
})?,
deny_write,
opts.prompt,
- )?,
+ ),
net: Permissions::new_unary(
parse_maybe_vec(opts.allow_net.as_deref(), |item| {
parser.parse_net_descriptor(item)
@@ -1954,7 +2083,7 @@ impl Permissions {
parser.parse_net_descriptor(item)
})?,
opts.prompt,
- )?,
+ ),
env: Permissions::new_unary(
parse_maybe_vec(opts.allow_env.as_deref(), |item| {
parser.parse_env_descriptor(item)
@@ -1963,7 +2092,7 @@ impl Permissions {
parser.parse_env_descriptor(text)
})?,
opts.prompt,
- )?,
+ ),
sys: Permissions::new_unary(
parse_maybe_vec(opts.allow_sys.as_deref(), |text| {
parser.parse_sys_descriptor(text)
@@ -1972,14 +2101,14 @@ impl Permissions {
parser.parse_sys_descriptor(text)
})?,
opts.prompt,
- )?,
+ ),
run: Permissions::new_unary(
allow_run,
parse_maybe_vec(opts.deny_run.as_deref(), |text| {
parser.parse_deny_run_descriptor(text)
})?,
opts.prompt,
- )?,
+ ),
ffi: Permissions::new_unary(
parse_maybe_vec(opts.allow_ffi.as_deref(), |text| {
parser.parse_ffi_descriptor(text)
@@ -1988,14 +2117,14 @@ impl Permissions {
parser.parse_ffi_descriptor(text)
})?,
opts.prompt,
- )?,
+ ),
import: Permissions::new_unary(
parse_maybe_vec(opts.allow_import.as_deref(), |item| {
parser.parse_import_descriptor(item)
})?,
None,
opts.prompt,
- )?,
+ ),
all: Permissions::new_all(opts.allow_all),
})
}
@@ -2027,14 +2156,14 @@ impl Permissions {
fn none(prompt: bool) -> Self {
Self {
- read: Permissions::new_unary(None, None, prompt).unwrap(),
- write: Permissions::new_unary(None, None, prompt).unwrap(),
- net: Permissions::new_unary(None, None, prompt).unwrap(),
- env: Permissions::new_unary(None, None, prompt).unwrap(),
- sys: Permissions::new_unary(None, None, prompt).unwrap(),
- run: Permissions::new_unary(None, None, prompt).unwrap(),
- ffi: Permissions::new_unary(None, None, prompt).unwrap(),
- import: Permissions::new_unary(None, None, prompt).unwrap(),
+ read: Permissions::new_unary(None, None, prompt),
+ write: Permissions::new_unary(None, None, prompt),
+ net: Permissions::new_unary(None, None, prompt),
+ env: Permissions::new_unary(None, None, prompt),
+ sys: Permissions::new_unary(None, None, prompt),
+ run: Permissions::new_unary(None, None, prompt),
+ ffi: Permissions::new_unary(None, None, prompt),
+ import: Permissions::new_unary(None, None, prompt),
all: Permissions::new_all(false),
}
}
@@ -2046,6 +2175,38 @@ pub enum CheckSpecifierKind {
Dynamic,
}
+#[derive(Debug, thiserror::Error)]
+pub enum ChildPermissionError {
+ #[error("Can't escalate parent thread permissions")]
+ Escalation,
+ #[error("{0}")]
+ PathResolve(#[from] PathResolveError),
+ #[error("{0}")]
+ NetDescriptorParse(#[from] NetDescriptorParseError),
+ #[error("{0}")]
+ EnvDescriptorParse(#[from] EnvDescriptorParseError),
+ #[error("{0}")]
+ SysDescriptorParse(#[from] SysDescriptorParseError),
+ #[error("{0}")]
+ RunDescriptorParse(#[from] RunDescriptorParseError),
+}
+
+#[derive(Debug, thiserror::Error)]
+pub enum PermissionCheckError {
+ #[error(transparent)]
+ PermissionDenied(#[from] PermissionDeniedError),
+ #[error("Invalid file path.\n Specifier: {0}")]
+ InvalidFilePath(Url),
+ #[error(transparent)]
+ NetDescriptorForUrlParse(#[from] NetDescriptorFromUrlParseError),
+ #[error(transparent)]
+ SysDescriptorParse(#[from] SysDescriptorParseError),
+ #[error(transparent)]
+ PathResolve(#[from] PathResolveError),
+ #[error(transparent)]
+ HostParse(#[from] HostParseError),
+}
+
/// Wrapper struct for `Permissions` that can be shared across threads.
///
/// We need a way to have internal mutability for permissions as they might get
@@ -2078,7 +2239,7 @@ impl PermissionsContainer {
pub fn create_child_permissions(
&self,
child_permissions_arg: ChildPermissionsArg,
- ) -> Result<PermissionsContainer, AnyError> {
+ ) -> Result<PermissionsContainer, ChildPermissionError> {
fn is_granted_unary(arg: &ChildUnaryPermissionArg) -> bool {
match arg {
ChildUnaryPermissionArg::Inherit | ChildUnaryPermissionArg::Granted => {
@@ -2116,48 +2277,71 @@ impl PermissionsContainer {
// WARNING: When adding a permission here, ensure it is handled
// in the worker_perms.all block above
- worker_perms.read = inner
- .read
- .create_child_permissions(child_permissions_arg.read, |text| {
- Ok(Some(self.descriptor_parser.parse_read_descriptor(text)?))
- })?;
- worker_perms.write = inner
- .write
- .create_child_permissions(child_permissions_arg.write, |text| {
- Ok(Some(self.descriptor_parser.parse_write_descriptor(text)?))
- })?;
- worker_perms.import = inner
- .import
- .create_child_permissions(child_permissions_arg.import, |text| {
- Ok(Some(self.descriptor_parser.parse_import_descriptor(text)?))
- })?;
- worker_perms.net = inner
- .net
- .create_child_permissions(child_permissions_arg.net, |text| {
- Ok(Some(self.descriptor_parser.parse_net_descriptor(text)?))
- })?;
- worker_perms.env = inner
- .env
- .create_child_permissions(child_permissions_arg.env, |text| {
- Ok(Some(self.descriptor_parser.parse_env_descriptor(text)?))
- })?;
- worker_perms.sys = inner
- .sys
- .create_child_permissions(child_permissions_arg.sys, |text| {
- Ok(Some(self.descriptor_parser.parse_sys_descriptor(text)?))
- })?;
+ worker_perms.read = inner.read.create_child_permissions(
+ child_permissions_arg.read,
+ |text| {
+ Ok::<_, PathResolveError>(Some(
+ self.descriptor_parser.parse_read_descriptor(text)?,
+ ))
+ },
+ )?;
+ worker_perms.write = inner.write.create_child_permissions(
+ child_permissions_arg.write,
+ |text| {
+ Ok::<_, PathResolveError>(Some(
+ self.descriptor_parser.parse_write_descriptor(text)?,
+ ))
+ },
+ )?;
+ worker_perms.import = inner.import.create_child_permissions(
+ child_permissions_arg.import,
+ |text| {
+ Ok::<_, NetDescriptorParseError>(Some(
+ self.descriptor_parser.parse_import_descriptor(text)?,
+ ))
+ },
+ )?;
+ worker_perms.net = inner.net.create_child_permissions(
+ child_permissions_arg.net,
+ |text| {
+ Ok::<_, NetDescriptorParseError>(Some(
+ self.descriptor_parser.parse_net_descriptor(text)?,
+ ))
+ },
+ )?;
+ worker_perms.env = inner.env.create_child_permissions(
+ child_permissions_arg.env,
+ |text| {
+ Ok::<_, EnvDescriptorParseError>(Some(
+ self.descriptor_parser.parse_env_descriptor(text)?,
+ ))
+ },
+ )?;
+ worker_perms.sys = inner.sys.create_child_permissions(
+ child_permissions_arg.sys,
+ |text| {
+ Ok::<_, SysDescriptorParseError>(Some(
+ self.descriptor_parser.parse_sys_descriptor(text)?,
+ ))
+ },
+ )?;
worker_perms.run = inner.run.create_child_permissions(
child_permissions_arg.run,
|text| match self.descriptor_parser.parse_allow_run_descriptor(text)? {
- AllowRunDescriptorParseResult::Unresolved(_) => Ok(None),
+ AllowRunDescriptorParseResult::Unresolved(_) => {
+ Ok::<_, RunDescriptorParseError>(None)
+ }
AllowRunDescriptorParseResult::Descriptor(desc) => Ok(Some(desc)),
},
)?;
- worker_perms.ffi = inner
- .ffi
- .create_child_permissions(child_permissions_arg.ffi, |text| {
- Ok(Some(self.descriptor_parser.parse_ffi_descriptor(text)?))
- })?;
+ worker_perms.ffi = inner.ffi.create_child_permissions(
+ child_permissions_arg.ffi,
+ |text| {
+ Ok::<_, PathResolveError>(Some(
+ self.descriptor_parser.parse_ffi_descriptor(text)?,
+ ))
+ },
+ )?;
Ok(PermissionsContainer::new(
self.descriptor_parser.clone(),
@@ -2170,7 +2354,7 @@ impl PermissionsContainer {
&self,
specifier: &ModuleSpecifier,
kind: CheckSpecifierKind,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionCheckError> {
let mut inner = self.inner.lock();
match specifier.scheme() {
"file" => {
@@ -2179,17 +2363,20 @@ impl PermissionsContainer {
}
match url_to_file_path(specifier) {
- Ok(path) => inner.read.check(
- &PathQueryDescriptor {
- requested: path.to_string_lossy().into_owned(),
- resolved: path,
- }
- .into_read(),
- Some("import()"),
- ),
- Err(_) => Err(uri_error(format!(
- "Invalid file path.\n Specifier: {specifier}"
- ))),
+ Ok(path) => inner
+ .read
+ .check(
+ &PathQueryDescriptor {
+ requested: path.to_string_lossy().into_owned(),
+ resolved: path,
+ }
+ .into_read(),
+ Some("import()"),
+ )
+ .map_err(PermissionCheckError::PermissionDenied),
+ Err(_) => {
+ Err(PermissionCheckError::InvalidFilePath(specifier.clone()))
+ }
}
}
"data" => Ok(()),
@@ -2214,7 +2401,7 @@ impl PermissionsContainer {
&self,
path: &str,
api_name: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
self.check_read_with_api_name(path, Some(api_name))
}
@@ -2224,7 +2411,7 @@ impl PermissionsContainer {
&self,
path: &str,
api_name: Option<&str>,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.read;
if inner.is_allow_all() {
@@ -2242,7 +2429,7 @@ impl PermissionsContainer {
&self,
path: &'a Path,
api_name: Option<&str>,
- ) -> Result<Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.read;
if inner.is_allow_all() {
@@ -2266,7 +2453,7 @@ impl PermissionsContainer {
path: &Path,
display: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.read;
skip_check_if_is_permission_fully_granted!(inner);
@@ -2277,12 +2464,17 @@ impl PermissionsContainer {
}
.into_read(),
Some(api_name),
- )
+ )?;
+ Ok(())
}
#[inline(always)]
- pub fn check_read_all(&self, api_name: &str) -> Result<(), AnyError> {
- self.inner.lock().read.check_all(Some(api_name))
+ pub fn check_read_all(
+ &self,
+ api_name: &str,
+ ) -> Result<(), PermissionCheckError> {
+ self.inner.lock().read.check_all(Some(api_name))?;
+ Ok(())
}
#[inline(always)]
@@ -2296,7 +2488,7 @@ impl PermissionsContainer {
&self,
path: &str,
api_name: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
self.check_write_with_api_name(path, Some(api_name))
}
@@ -2306,7 +2498,7 @@ impl PermissionsContainer {
&self,
path: &str,
api_name: Option<&str>,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.write;
if inner.is_allow_all() {
@@ -2324,7 +2516,7 @@ impl PermissionsContainer {
&self,
path: &'a Path,
api_name: &str,
- ) -> Result<Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.write;
if inner.is_allow_all() {
@@ -2341,8 +2533,12 @@ impl PermissionsContainer {
}
#[inline(always)]
- pub fn check_write_all(&self, api_name: &str) -> Result<(), AnyError> {
- self.inner.lock().write.check_all(Some(api_name))
+ pub fn check_write_all(
+ &self,
+ api_name: &str,
+ ) -> Result<(), PermissionCheckError> {
+ self.inner.lock().write.check_all(Some(api_name))?;
+ Ok(())
}
/// As `check_write()`, but permission error messages will anonymize the path
@@ -2353,7 +2549,7 @@ impl PermissionsContainer {
path: &Path,
display: &str,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.write;
skip_check_if_is_permission_fully_granted!(inner);
@@ -2364,7 +2560,8 @@ impl PermissionsContainer {
}
.into_write(),
Some(api_name),
- )
+ )?;
+ Ok(())
}
#[inline(always)]
@@ -2372,7 +2569,7 @@ impl PermissionsContainer {
&mut self,
path: &str,
api_name: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.write;
if inner.is_allow_all() {
@@ -2389,13 +2586,18 @@ impl PermissionsContainer {
&mut self,
cmd: &RunQueryDescriptor,
api_name: &str,
- ) -> Result<(), AnyError> {
- self.inner.lock().run.check(cmd, Some(api_name))
+ ) -> Result<(), PermissionCheckError> {
+ self.inner.lock().run.check(cmd, Some(api_name))?;
+ Ok(())
}
#[inline(always)]
- pub fn check_run_all(&mut self, api_name: &str) -> Result<(), AnyError> {
- self.inner.lock().run.check_all(Some(api_name))
+ pub fn check_run_all(
+ &mut self,
+ api_name: &str,
+ ) -> Result<(), PermissionCheckError> {
+ self.inner.lock().run.check_all(Some(api_name))?;
+ Ok(())
}
#[inline(always)]
@@ -2404,38 +2606,50 @@ impl PermissionsContainer {
}
#[inline(always)]
- pub fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
+ pub fn check_sys(
+ &self,
+ kind: &str,
+ api_name: &str,
+ ) -> Result<(), PermissionCheckError> {
self.inner.lock().sys.check(
&self.descriptor_parser.parse_sys_descriptor(kind)?,
Some(api_name),
- )
+ )?;
+ Ok(())
}
#[inline(always)]
- pub fn check_env(&mut self, var: &str) -> Result<(), AnyError> {
- self.inner.lock().env.check(var, None)
+ pub fn check_env(&mut self, var: &str) -> Result<(), PermissionCheckError> {
+ self.inner.lock().env.check(var, None)?;
+ Ok(())
}
#[inline(always)]
- pub fn check_env_all(&mut self) -> Result<(), AnyError> {
- self.inner.lock().env.check_all()
+ pub fn check_env_all(&mut self) -> Result<(), PermissionCheckError> {
+ self.inner.lock().env.check_all()?;
+ Ok(())
}
#[inline(always)]
- pub fn check_sys_all(&mut self) -> Result<(), AnyError> {
- self.inner.lock().sys.check_all()
+ pub fn check_sys_all(&mut self) -> Result<(), PermissionCheckError> {
+ self.inner.lock().sys.check_all()?;
+ Ok(())
}
#[inline(always)]
- pub fn check_ffi_all(&mut self) -> Result<(), AnyError> {
- self.inner.lock().ffi.check_all()
+ pub fn check_ffi_all(&mut self) -> Result<(), PermissionCheckError> {
+ self.inner.lock().ffi.check_all()?;
+ Ok(())
}
/// This checks to see if the allow-all flag was passed, not whether all
/// permissions are enabled!
#[inline(always)]
- pub fn check_was_allow_all_flag_passed(&mut self) -> Result<(), AnyError> {
- self.inner.lock().all.check()
+ pub fn check_was_allow_all_flag_passed(
+ &mut self,
+ ) -> Result<(), PermissionCheckError> {
+ self.inner.lock().all.check()?;
+ Ok(())
}
/// Checks special file access, returning the failed permission type if
@@ -2547,13 +2761,14 @@ impl PermissionsContainer {
&mut self,
url: &Url,
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionCheckError> {
let mut inner = self.inner.lock();
if inner.net.is_allow_all() {
return Ok(());
}
let desc = self.descriptor_parser.parse_net_descriptor_from_url(url)?;
- inner.net.check(&desc, Some(api_name))
+ inner.net.check(&desc, Some(api_name))?;
+ Ok(())
}
#[inline(always)]
@@ -2561,17 +2776,21 @@ impl PermissionsContainer {
&mut self,
host: &(T, Option<u16>),
api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.net;
skip_check_if_is_permission_fully_granted!(inner);
let hostname = Host::parse(host.0.as_ref())?;
let descriptor = NetDescriptor(hostname, host.1);
- inner.check(&descriptor, Some(api_name))
+ inner.check(&descriptor, Some(api_name))?;
+ Ok(())
}
#[inline(always)]
- pub fn check_ffi(&mut self, path: &str) -> Result<PathBuf, AnyError> {
+ pub fn check_ffi(
+ &mut self,
+ path: &str,
+ ) -> Result<PathBuf, PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.ffi;
if inner.is_allow_all() {
@@ -2585,14 +2804,15 @@ impl PermissionsContainer {
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
#[inline(always)]
- pub fn check_ffi_partial_no_path(&mut self) -> Result<(), AnyError> {
+ pub fn check_ffi_partial_no_path(
+ &mut self,
+ ) -> Result<(), PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.ffi;
- if inner.is_allow_all() {
- Ok(())
- } else {
- inner.check_partial(None)
+ if !inner.is_allow_all() {
+ inner.check_partial(None)?;
}
+ Ok(())
}
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
@@ -2600,7 +2820,7 @@ impl PermissionsContainer {
pub fn check_ffi_partial_with_path(
&mut self,
path: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
let mut inner = self.inner.lock();
let inner = &mut inner.ffi;
if inner.is_allow_all() {
@@ -2618,7 +2838,7 @@ impl PermissionsContainer {
pub fn query_read(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
let inner = self.inner.lock();
let permission = &inner.read;
if permission.is_allow_all() {
@@ -2628,7 +2848,7 @@ impl PermissionsContainer {
permission.query(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_read(),
)
})
@@ -2642,7 +2862,7 @@ impl PermissionsContainer {
pub fn query_write(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
let inner = self.inner.lock();
let permission = &inner.write;
if permission.is_allow_all() {
@@ -2652,7 +2872,7 @@ impl PermissionsContainer {
permission.query(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_write(),
)
})
@@ -2666,7 +2886,7 @@ impl PermissionsContainer {
pub fn query_net(
&self,
host: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, NetDescriptorParseError> {
let inner = self.inner.lock();
let permission = &inner.net;
if permission.is_allow_all() {
@@ -2697,7 +2917,7 @@ impl PermissionsContainer {
pub fn query_sys(
&self,
kind: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, SysDescriptorParseError> {
let inner = self.inner.lock();
let permission = &inner.sys;
if permission.is_allow_all() {
@@ -2717,7 +2937,7 @@ impl PermissionsContainer {
pub fn query_run(
&self,
cmd: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, RunDescriptorParseError> {
let inner = self.inner.lock();
let permission = &inner.run;
if permission.is_allow_all() {
@@ -2737,7 +2957,7 @@ impl PermissionsContainer {
pub fn query_ffi(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
let inner = self.inner.lock();
let permission = &inner.ffi;
if permission.is_allow_all() {
@@ -2747,7 +2967,7 @@ impl PermissionsContainer {
permission.query(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_ffi(),
)
})
@@ -2763,12 +2983,12 @@ impl PermissionsContainer {
pub fn revoke_read(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
Ok(
self.inner.lock().read.revoke(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_read(),
)
})
@@ -2782,12 +3002,12 @@ impl PermissionsContainer {
pub fn revoke_write(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
Ok(
self.inner.lock().write.revoke(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_write(),
)
})
@@ -2801,7 +3021,7 @@ impl PermissionsContainer {
pub fn revoke_net(
&self,
host: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, NetDescriptorParseError> {
Ok(
self.inner.lock().net.revoke(
match host {
@@ -2822,7 +3042,7 @@ impl PermissionsContainer {
pub fn revoke_sys(
&self,
kind: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, SysDescriptorParseError> {
Ok(
self.inner.lock().sys.revoke(
kind
@@ -2837,7 +3057,7 @@ impl PermissionsContainer {
pub fn revoke_run(
&self,
cmd: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, RunDescriptorParseError> {
Ok(
self.inner.lock().run.revoke(
cmd
@@ -2852,12 +3072,12 @@ impl PermissionsContainer {
pub fn revoke_ffi(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
Ok(
self.inner.lock().ffi.revoke(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_ffi(),
)
})
@@ -2873,12 +3093,12 @@ impl PermissionsContainer {
pub fn request_read(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
Ok(
self.inner.lock().read.request(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_read(),
)
})
@@ -2892,12 +3112,12 @@ impl PermissionsContainer {
pub fn request_write(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
Ok(
self.inner.lock().write.request(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_write(),
)
})
@@ -2911,7 +3131,7 @@ impl PermissionsContainer {
pub fn request_net(
&self,
host: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, NetDescriptorParseError> {
Ok(
self.inner.lock().net.request(
match host {
@@ -2932,7 +3152,7 @@ impl PermissionsContainer {
pub fn request_sys(
&self,
kind: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, SysDescriptorParseError> {
Ok(
self.inner.lock().sys.request(
kind
@@ -2947,7 +3167,7 @@ impl PermissionsContainer {
pub fn request_run(
&self,
cmd: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, RunDescriptorParseError> {
Ok(
self.inner.lock().run.request(
cmd
@@ -2962,12 +3182,12 @@ impl PermissionsContainer {
pub fn request_ffi(
&self,
path: Option<&str>,
- ) -> Result<PermissionState, AnyError> {
+ ) -> Result<PermissionState, PathResolveError> {
Ok(
self.inner.lock().ffi.request(
path
.map(|path| {
- Result::<_, AnyError>::Ok(
+ Ok::<_, PathResolveError>(
self.descriptor_parser.parse_path_query(path)?.into_ffi(),
)
})
@@ -3003,10 +3223,6 @@ fn global_from_option<T>(flag: Option<&HashSet<T>>) -> bool {
matches!(flag, Some(v) if v.is_empty())
}
-fn escalation_error() -> AnyError {
- custom_error("NotCapable", "Can't escalate parent thread permissions")
-}
-
#[derive(Debug, Eq, PartialEq)]
pub enum ChildUnitPermissionArg {
Inherit,
@@ -3267,65 +3483,73 @@ pub trait PermissionDescriptorParser: Debug + Send + Sync {
fn parse_read_descriptor(
&self,
text: &str,
- ) -> Result<ReadDescriptor, AnyError>;
+ ) -> Result<ReadDescriptor, PathResolveError>;
fn parse_write_descriptor(
&self,
text: &str,
- ) -> Result<WriteDescriptor, AnyError>;
+ ) -> Result<WriteDescriptor, PathResolveError>;
- fn parse_net_descriptor(&self, text: &str)
- -> Result<NetDescriptor, AnyError>;
+ fn parse_net_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<NetDescriptor, NetDescriptorParseError>;
fn parse_net_descriptor_from_url(
&self,
url: &Url,
- ) -> Result<NetDescriptor, AnyError> {
+ ) -> Result<NetDescriptor, NetDescriptorFromUrlParseError> {
NetDescriptor::from_url(url)
}
fn parse_import_descriptor(
&self,
text: &str,
- ) -> Result<ImportDescriptor, AnyError>;
+ ) -> Result<ImportDescriptor, NetDescriptorParseError>;
fn parse_import_descriptor_from_url(
&self,
url: &Url,
- ) -> Result<ImportDescriptor, AnyError> {
+ ) -> Result<ImportDescriptor, NetDescriptorFromUrlParseError> {
ImportDescriptor::from_url(url)
}
- fn parse_env_descriptor(&self, text: &str)
- -> Result<EnvDescriptor, AnyError>;
+ fn parse_env_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<EnvDescriptor, EnvDescriptorParseError>;
- fn parse_sys_descriptor(&self, text: &str)
- -> Result<SysDescriptor, AnyError>;
+ fn parse_sys_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<SysDescriptor, SysDescriptorParseError>;
fn parse_allow_run_descriptor(
&self,
text: &str,
- ) -> Result<AllowRunDescriptorParseResult, AnyError>;
+ ) -> Result<AllowRunDescriptorParseResult, RunDescriptorParseError>;
fn parse_deny_run_descriptor(
&self,
text: &str,
- ) -> Result<DenyRunDescriptor, AnyError>;
+ ) -> Result<DenyRunDescriptor, PathResolveError>;
- fn parse_ffi_descriptor(&self, text: &str)
- -> Result<FfiDescriptor, AnyError>;
+ fn parse_ffi_descriptor(
+ &self,
+ text: &str,
+ ) -> Result<FfiDescriptor, PathResolveError>;
// queries
fn parse_path_query(
&self,
path: &str,
- ) -> Result<PathQueryDescriptor, AnyError>;
+ ) -> Result<PathQueryDescriptor, PathResolveError>;
fn parse_run_query(
&self,
requested: &str,
- ) -> Result<RunQueryDescriptor, AnyError>;
+ ) -> Result<RunQueryDescriptor, RunDescriptorParseError>;
}
static IS_STANDALONE: AtomicFlag = AtomicFlag::lowered();
@@ -3368,49 +3592,49 @@ mod tests {
fn parse_read_descriptor(
&self,
text: &str,
- ) -> Result<ReadDescriptor, AnyError> {
+ ) -> Result<ReadDescriptor, PathResolveError> {
Ok(ReadDescriptor(self.join_path_with_root(text)))
}
fn parse_write_descriptor(
&self,
text: &str,
- ) -> Result<WriteDescriptor, AnyError> {
+ ) -> Result<WriteDescriptor, PathResolveError> {
Ok(WriteDescriptor(self.join_path_with_root(text)))
}
fn parse_net_descriptor(
&self,
text: &str,
- ) -> Result<NetDescriptor, AnyError> {
+ ) -> Result<NetDescriptor, NetDescriptorParseError> {
NetDescriptor::parse(text)
}
fn parse_import_descriptor(
&self,
text: &str,
- ) -> Result<ImportDescriptor, AnyError> {
+ ) -> Result<ImportDescriptor, NetDescriptorParseError> {
ImportDescriptor::parse(text)
}
fn parse_env_descriptor(
&self,
text: &str,
- ) -> Result<EnvDescriptor, AnyError> {
+ ) -> Result<EnvDescriptor, EnvDescriptorParseError> {
Ok(EnvDescriptor::new(text))
}
fn parse_sys_descriptor(
&self,
text: &str,
- ) -> Result<SysDescriptor, AnyError> {
+ ) -> Result<SysDescriptor, SysDescriptorParseError> {
SysDescriptor::parse(text.to_string())
}
fn parse_allow_run_descriptor(
&self,
text: &str,
- ) -> Result<AllowRunDescriptorParseResult, AnyError> {
+ ) -> Result<AllowRunDescriptorParseResult, RunDescriptorParseError> {
Ok(AllowRunDescriptorParseResult::Descriptor(
AllowRunDescriptor(self.join_path_with_root(text)),
))
@@ -3419,7 +3643,7 @@ mod tests {
fn parse_deny_run_descriptor(
&self,
text: &str,
- ) -> Result<DenyRunDescriptor, AnyError> {
+ ) -> Result<DenyRunDescriptor, PathResolveError> {
if text.contains("/") {
Ok(DenyRunDescriptor::Path(self.join_path_with_root(text)))
} else {
@@ -3430,14 +3654,14 @@ mod tests {
fn parse_ffi_descriptor(
&self,
text: &str,
- ) -> Result<FfiDescriptor, AnyError> {
+ ) -> Result<FfiDescriptor, PathResolveError> {
Ok(FfiDescriptor(self.join_path_with_root(text)))
}
fn parse_path_query(
&self,
path: &str,
- ) -> Result<PathQueryDescriptor, AnyError> {
+ ) -> Result<PathQueryDescriptor, PathResolveError> {
Ok(PathQueryDescriptor {
resolved: self.join_path_with_root(path),
requested: path.to_string(),
@@ -3447,8 +3671,8 @@ mod tests {
fn parse_run_query(
&self,
requested: &str,
- ) -> Result<RunQueryDescriptor, AnyError> {
- RunQueryDescriptor::parse(requested)
+ ) -> Result<RunQueryDescriptor, RunDescriptorParseError> {
+ RunQueryDescriptor::parse(requested).map_err(Into::into)
}
}
@@ -4329,7 +4553,6 @@ mod tests {
None,
false,
)
- .unwrap()
};
prompt_value.set(true);
@@ -4556,13 +4779,12 @@ mod tests {
.lock()
.clone(),
Permissions {
- env: Permissions::new_unary(Some(HashSet::new()), None, false).unwrap(),
+ env: Permissions::new_unary(Some(HashSet::new()), None, false),
net: Permissions::new_unary(
Some(HashSet::from([NetDescriptor::parse("foo").unwrap()])),
None,
false
- )
- .unwrap(),
+ ),
..Permissions::none_without_prompt()
}
);
diff --git a/runtime/permissions/prompter.rs b/runtime/permissions/prompter.rs
index e48e0af10..168a845a2 100644
--- a/runtime/permissions/prompter.rs
+++ b/runtime/permissions/prompter.rs
@@ -1,6 +1,5 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
-use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex;
use deno_terminal::colors;
use once_cell::sync::Lazy;
@@ -80,6 +79,10 @@ pub fn set_prompt_callbacks(
*MAYBE_AFTER_PROMPT_CALLBACK.lock() = Some(after_callback);
}
+pub fn set_prompter(prompter: Box<dyn PermissionPrompter>) {
+ *PERMISSION_PROMPTER.lock() = prompter;
+}
+
pub type PromptCallback = Box<dyn FnMut() + Send + Sync>;
pub trait PermissionPrompter: Send + Sync {
@@ -97,8 +100,7 @@ pub struct TtyPrompter;
fn clear_stdin(
_stdin_lock: &mut StdinLock,
_stderr_lock: &mut StderrLock,
-) -> Result<(), AnyError> {
- use deno_core::anyhow::bail;
+) -> Result<(), std::io::Error> {
use std::mem::MaybeUninit;
const STDIN_FD: i32 = 0;
@@ -113,7 +115,10 @@ fn clear_stdin(
loop {
let r = libc::tcflush(STDIN_FD, libc::TCIFLUSH);
if r != 0 {
- bail!("clear_stdin failed (tcflush)");
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "clear_stdin failed (tcflush)",
+ ));
}
// Initialize timeout for select to be 100ms
@@ -133,7 +138,10 @@ fn clear_stdin(
// Check if select returned an error
if r < 0 {
- bail!("clear_stdin failed (select)");
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "clear_stdin failed (select)",
+ ));
}
// Check if select returned due to timeout (stdin is quiescent)
@@ -152,8 +160,7 @@ fn clear_stdin(
fn clear_stdin(
stdin_lock: &mut StdinLock,
stderr_lock: &mut StderrLock,
-) -> Result<(), AnyError> {
- use deno_core::anyhow::bail;
+) -> Result<(), std::io::Error> {
use winapi::shared::minwindef::TRUE;
use winapi::shared::minwindef::UINT;
use winapi::shared::minwindef::WORD;
@@ -190,18 +197,23 @@ fn clear_stdin(
return Ok(());
- unsafe fn flush_input_buffer(stdin: HANDLE) -> Result<(), AnyError> {
+ unsafe fn flush_input_buffer(stdin: HANDLE) -> Result<(), std::io::Error> {
let success = FlushConsoleInputBuffer(stdin);
if success != TRUE {
- bail!(
- "Could not flush the console input buffer: {}",
- std::io::Error::last_os_error()
- )
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ format!(
+ "Could not flush the console input buffer: {}",
+ std::io::Error::last_os_error()
+ ),
+ ));
}
Ok(())
}
- unsafe fn emulate_enter_key_press(stdin: HANDLE) -> Result<(), AnyError> {
+ unsafe fn emulate_enter_key_press(
+ stdin: HANDLE,
+ ) -> Result<(), std::io::Error> {
// https://github.com/libuv/libuv/blob/a39009a5a9252a566ca0704d02df8dabc4ce328f/src/win/tty.c#L1121-L1131
let mut input_record: INPUT_RECORD = std::mem::zeroed();
input_record.EventType = KEY_EVENT;
@@ -216,34 +228,43 @@ fn clear_stdin(
let success =
WriteConsoleInputW(stdin, &input_record, 1, &mut record_written);
if success != TRUE {
- bail!(
- "Could not emulate enter key press: {}",
- std::io::Error::last_os_error()
- );
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ format!(
+ "Could not emulate enter key press: {}",
+ std::io::Error::last_os_error()
+ ),
+ ));
}
Ok(())
}
- unsafe fn is_input_buffer_empty(stdin: HANDLE) -> Result<bool, AnyError> {
+ unsafe fn is_input_buffer_empty(
+ stdin: HANDLE,
+ ) -> Result<bool, std::io::Error> {
let mut buffer = Vec::with_capacity(1);
let mut events_read = 0;
let success =
PeekConsoleInputW(stdin, buffer.as_mut_ptr(), 1, &mut events_read);
if success != TRUE {
- bail!(
- "Could not peek the console input buffer: {}",
- std::io::Error::last_os_error()
- )
+ return Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ format!(
+ "Could not peek the console input buffer: {}",
+ std::io::Error::last_os_error()
+ ),
+ ));
}
Ok(events_read == 0)
}
- fn move_cursor_up(stderr_lock: &mut StderrLock) -> Result<(), AnyError> {
- write!(stderr_lock, "\x1B[1A")?;
- Ok(())
+ fn move_cursor_up(
+ stderr_lock: &mut StderrLock,
+ ) -> Result<(), std::io::Error> {
+ write!(stderr_lock, "\x1B[1A")
}
- fn read_stdin_line(stdin_lock: &mut StdinLock) -> Result<(), AnyError> {
+ fn read_stdin_line(stdin_lock: &mut StdinLock) -> Result<(), std::io::Error> {
let mut input = String::new();
stdin_lock.read_line(&mut input)?;
Ok(())
@@ -265,7 +286,7 @@ fn get_stdin_metadata() -> std::io::Result<std::fs::Metadata> {
unsafe {
let stdin = std::fs::File::from_raw_fd(0);
let metadata = stdin.metadata().unwrap();
- stdin.into_raw_fd();
+ let _ = stdin.into_raw_fd();
Ok(metadata)
}
}
@@ -366,7 +387,7 @@ impl PermissionPrompter for TtyPrompter {
let mut input = String::new();
let result = stdin_lock.read_line(&mut input);
- let input = input.trim_end_matches(|c| c == '\r' || c == '\n');
+ let input = input.trim_end_matches(['\r', '\n']);
if result.is_err() || input.len() != 1 {
break PromptResponse::Deny;
};
@@ -476,8 +497,4 @@ pub mod tests {
STUB_PROMPT_VALUE.store(value, Ordering::SeqCst);
}
}
-
- pub fn set_prompter(prompter: Box<dyn PermissionPrompter>) {
- *PERMISSION_PROMPTER.lock() = prompter;
- }
}
diff --git a/runtime/shared.rs b/runtime/shared.rs
index 02dfd1871..b1f383b03 100644
--- a/runtime/shared.rs
+++ b/runtime/shared.rs
@@ -47,6 +47,7 @@ extension!(runtime,
"40_signals.js",
"40_tty.js",
"41_prompt.js",
+ "telemetry.ts",
"90_deno_ns.js",
"98_global_scope_shared.js",
"98_global_scope_window.js",
@@ -98,6 +99,7 @@ pub fn maybe_transpile_source(
imports_not_used_as_values: deno_ast::ImportsNotUsedAsValues::Remove,
..Default::default()
},
+ &deno_ast::TranspileModuleOptions::default(),
&deno_ast::EmitOptions {
source_map: if cfg!(debug_assertions) {
SourceMapOption::Separate
@@ -109,9 +111,9 @@ pub fn maybe_transpile_source(
)?
.into_source();
- let maybe_source_map: Option<SourceMapData> =
- transpiled_source.source_map.map(|sm| sm.into());
- let source_text = String::from_utf8(transpiled_source.source)?;
-
+ let maybe_source_map: Option<SourceMapData> = transpiled_source
+ .source_map
+ .map(|sm| sm.into_bytes().into());
+ let source_text = transpiled_source.text;
Ok((source_text.into(), maybe_source_map))
}
diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs
index 041132f97..3bf515131 100644
--- a/runtime/snapshot.rs
+++ b/runtime/snapshot.rs
@@ -5,12 +5,12 @@ use crate::ops::bootstrap::SnapshotOptions;
use crate::shared::maybe_transpile_source;
use crate::shared::runtime;
use deno_cache::SqliteBackedCache;
-use deno_core::error::AnyError;
use deno_core::snapshot::*;
use deno_core::v8;
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;
@@ -26,7 +26,7 @@ impl deno_websocket::WebSocketPermissions for Permissions {
&mut self,
_url: &deno_core::url::Url,
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
}
@@ -42,7 +42,7 @@ impl deno_fetch::FetchPermissions for Permissions {
&mut self,
_url: &deno_core::url::Url,
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -50,28 +50,26 @@ impl deno_fetch::FetchPermissions for Permissions {
&mut self,
_p: &'a Path,
_api_name: &str,
- ) -> Result<Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
unreachable!("snapshotting!")
}
}
impl deno_ffi::FfiPermissions for Permissions {
- fn check_partial_no_path(
- &mut self,
- ) -> Result<(), deno_core::error::AnyError> {
+ fn check_partial_no_path(&mut self) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
fn check_partial_with_path(
&mut self,
_path: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
}
impl deno_napi::NapiPermissions for Permissions {
- fn check(&mut self, _path: &str) -> std::result::Result<PathBuf, AnyError> {
+ fn check(&mut self, _path: &str) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
}
@@ -81,20 +79,27 @@ impl deno_node::NodePermissions for Permissions {
&mut self,
_url: &deno_core::url::Url,
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<(), PermissionCheckError> {
+ unreachable!("snapshotting!")
+ }
+ fn check_net(
+ &mut self,
+ _host: (&str, Option<u16>),
+ _api_name: &str,
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
fn check_read_path<'a>(
&mut self,
_path: &'a Path,
- ) -> Result<Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
unreachable!("snapshotting!")
}
fn check_read_with_api_name(
&mut self,
_p: &str,
_api_name: Option<&str>,
- ) -> Result<PathBuf, deno_core::error::AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
fn query_read_all(&mut self) -> bool {
@@ -104,14 +109,14 @@ impl deno_node::NodePermissions for Permissions {
&mut self,
_p: &str,
_api_name: Option<&str>,
- ) -> Result<PathBuf, deno_core::error::AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
fn check_sys(
&mut self,
_kind: &str,
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
}
@@ -121,7 +126,7 @@ impl deno_net::NetPermissions for Permissions {
&mut self,
_host: &(T, Option<u16>),
_api_name: &str,
- ) -> Result<(), deno_core::error::AnyError> {
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -129,7 +134,7 @@ impl deno_net::NetPermissions for Permissions {
&mut self,
_p: &str,
_api_name: &str,
- ) -> Result<PathBuf, deno_core::error::AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -137,7 +142,7 @@ impl deno_net::NetPermissions for Permissions {
&mut self,
_p: &str,
_api_name: &str,
- ) -> Result<PathBuf, deno_core::error::AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -145,7 +150,7 @@ impl deno_net::NetPermissions for Permissions {
&mut self,
_p: &'a Path,
_api_name: &str,
- ) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
unreachable!("snapshotting!")
}
}
@@ -158,7 +163,7 @@ impl deno_fs::FsPermissions for Permissions {
_write: bool,
_path: &'a Path,
_api_name: &str,
- ) -> Result<std::borrow::Cow<'a, Path>, FsError> {
+ ) -> Result<Cow<'a, Path>, FsError> {
unreachable!("snapshotting!")
}
@@ -166,11 +171,14 @@ impl deno_fs::FsPermissions for Permissions {
&mut self,
_path: &str,
_api_name: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
- fn check_read_all(&mut self, _api_name: &str) -> Result<(), AnyError> {
+ fn check_read_all(
+ &mut self,
+ _api_name: &str,
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -179,7 +187,7 @@ impl deno_fs::FsPermissions for Permissions {
_path: &Path,
_display: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -187,7 +195,7 @@ impl deno_fs::FsPermissions for Permissions {
&mut self,
_path: &str,
_api_name: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -195,11 +203,14 @@ impl deno_fs::FsPermissions for Permissions {
&mut self,
_path: &str,
_api_name: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
- fn check_write_all(&mut self, _api_name: &str) -> Result<(), AnyError> {
+ fn check_write_all(
+ &mut self,
+ _api_name: &str,
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -208,7 +219,7 @@ impl deno_fs::FsPermissions for Permissions {
_path: &Path,
_display: &str,
_api_name: &str,
- ) -> Result<(), AnyError> {
+ ) -> Result<(), PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -216,7 +227,7 @@ impl deno_fs::FsPermissions for Permissions {
&mut self,
_path: &'a Path,
_api_name: &str,
- ) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -224,7 +235,7 @@ impl deno_fs::FsPermissions for Permissions {
&mut self,
_path: &'a Path,
_api_name: &str,
- ) -> Result<std::borrow::Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
unreachable!("snapshotting!")
}
}
@@ -234,7 +245,7 @@ impl deno_kv::sqlite::SqliteDbHandlerPermissions for Permissions {
&mut self,
_path: &str,
_api_name: &str,
- ) -> Result<PathBuf, AnyError> {
+ ) -> Result<PathBuf, PermissionCheckError> {
unreachable!("snapshotting!")
}
@@ -242,7 +253,7 @@ impl deno_kv::sqlite::SqliteDbHandlerPermissions for Permissions {
&mut self,
_path: &'a Path,
_api_name: &str,
- ) -> Result<Cow<'a, Path>, AnyError> {
+ ) -> Result<Cow<'a, Path>, PermissionCheckError> {
unreachable!("snapshotting!")
}
}
@@ -289,7 +300,9 @@ pub fn create_runtime_snapshot(
deno_cron::local::LocalCronHandler::new(),
),
deno_napi::deno_napi::init_ops_and_esm::<Permissions>(),
- deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
+ deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
+ deno_http::Options::default(),
+ ),
deno_io::deno_io::init_ops_and_esm(Default::default()),
deno_fs::deno_fs::init_ops_and_esm::<Permissions>(fs.clone()),
deno_node::deno_node::init_ops_and_esm::<Permissions>(None, fs.clone()),
@@ -301,6 +314,7 @@ pub fn create_runtime_snapshot(
),
ops::fs_events::deno_fs_events::init_ops(),
ops::os::deno_os::init_ops(Default::default()),
+ ops::otel::deno_otel::init_ops(),
ops::permissions::deno_permissions::init_ops(),
ops::process::deno_process::init_ops(None),
ops::signal::deno_signal::init_ops(),
diff --git a/runtime/ops/os/sys_info.rs b/runtime/sys_info.rs
index cffc90e9d..cffc90e9d 100644
--- a/runtime/ops/os/sys_info.rs
+++ b/runtime/sys_info.rs
diff --git a/runtime/tokio_util.rs b/runtime/tokio_util.rs
index 0d81f6e23..aa0282ece 100644
--- a/runtime/tokio_util.rs
+++ b/runtime/tokio_util.rs
@@ -43,7 +43,15 @@ pub fn create_basic_runtime() -> tokio::runtime::Runtime {
// parallel for deno fmt.
// The default value is 512, which is an unhelpfully large thread pool. We
// don't ever want to have more than a couple dozen threads.
- .max_blocking_threads(32)
+ .max_blocking_threads(if cfg!(windows) {
+ // on windows, tokio uses blocking tasks for child process IO, make sure
+ // we have enough available threads for other tasks to run
+ 4 * std::thread::available_parallelism()
+ .map(|n| n.get())
+ .unwrap_or(8)
+ } else {
+ 32
+ })
.build()
.unwrap()
}
diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs
index f560ce17e..a282d11b9 100644
--- a/runtime/web_worker.rs
+++ b/runtime/web_worker.rs
@@ -166,7 +166,10 @@ pub struct WebWorkerInternalHandle {
impl WebWorkerInternalHandle {
/// Post WorkerEvent to parent as a worker
- pub fn post_event(&self, event: WorkerControlEvent) -> Result<(), AnyError> {
+ pub fn post_event(
+ &self,
+ event: WorkerControlEvent,
+ ) -> Result<(), mpsc::TrySendError<WorkerControlEvent>> {
let mut sender = self.sender.clone();
// If the channel is closed,
// the worker must have terminated but the termination message has not yet been received.
@@ -176,8 +179,7 @@ impl WebWorkerInternalHandle {
self.has_terminated.store(true, Ordering::SeqCst);
return Ok(());
}
- sender.try_send(event)?;
- Ok(())
+ sender.try_send(event)
}
/// Check if this worker is terminated or being terminated
@@ -263,11 +265,9 @@ impl WebWorkerHandle {
/// Get the WorkerEvent with lock
/// Return error if more than one listener tries to get event
#[allow(clippy::await_holding_refcell_ref)] // TODO(ry) remove!
- pub async fn get_control_event(
- &self,
- ) -> Result<Option<WorkerControlEvent>, AnyError> {
+ pub async fn get_control_event(&self) -> Option<WorkerControlEvent> {
let mut receiver = self.receiver.borrow_mut();
- Ok(receiver.next().await)
+ receiver.next().await
}
/// Terminate the worker
@@ -361,6 +361,8 @@ pub struct WebWorkerOptions {
pub extensions: Vec<Extension>,
pub startup_snapshot: Option<&'static [u8]>,
pub unsafely_ignore_certificate_errors: Option<Vec<String>>,
+ /// Optional isolate creation parameters, such as heap limits.
+ pub create_params: Option<v8::CreateParams>,
pub seed: Option<u64>,
pub create_web_worker_cb: Arc<ops::worker_host::CreateWebWorkerCb>,
pub format_js_error_fn: Option<Arc<FormatJsErrorFn>>,
@@ -393,6 +395,13 @@ pub struct WebWorker {
maybe_worker_metadata: Option<WorkerMetadata>,
}
+impl Drop for WebWorker {
+ fn drop(&mut self) {
+ // clean up the package.json thread local cache
+ node_resolver::PackageJsonThreadLocalCache::clear();
+ }
+}
+
impl WebWorker {
pub fn bootstrap_from_options(
services: WebWorkerServiceOptions,
@@ -488,7 +497,9 @@ impl WebWorker {
),
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
- deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
+ deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
+ deno_http::Options::default(),
+ ),
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
services.fs.clone(),
@@ -505,6 +516,7 @@ impl WebWorker {
),
ops::fs_events::deno_fs_events::init_ops_and_esm(),
ops::os::deno_os_worker::init_ops_and_esm(),
+ ops::otel::deno_otel::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,
@@ -555,6 +567,7 @@ impl WebWorker {
let mut js_runtime = JsRuntime::new(RuntimeOptions {
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,
@@ -562,7 +575,7 @@ impl WebWorker {
extension_transpiler: Some(Rc::new(|specifier, source| {
maybe_transpile_source(specifier, source)
})),
- inspector: services.maybe_inspector_server.is_some(),
+ inspector: true,
feature_checker: Some(services.feature_checker),
op_metrics_factory_fn,
import_meta_resolve_callback: Some(Box::new(
@@ -579,18 +592,18 @@ impl WebWorker {
js_runtime.op_state().borrow_mut().put(op_summary_metrics);
}
+ // Put inspector handle into the op state so we can put a breakpoint when
+ // executing a CJS entrypoint.
+ let op_state = js_runtime.op_state();
+ let inspector = js_runtime.inspector();
+ op_state.borrow_mut().put(inspector);
+
if let Some(server) = services.maybe_inspector_server {
server.register_inspector(
options.main_module.to_string(),
&mut js_runtime,
false,
);
-
- // Put inspector handle into the op state so we can put a breakpoint when
- // executing a CJS entrypoint.
- let op_state = js_runtime.op_state();
- let inspector = js_runtime.inspector();
- op_state.borrow_mut().put(inspector);
}
let (internal_handle, external_handle) = {
@@ -821,13 +834,12 @@ impl WebWorker {
// TODO(mmastrac): we don't want to test this w/classic workers because
// WPT triggers a failure here. This is only exposed via --enable-testing-features-do-not-use.
- #[allow(clippy::print_stderr)]
if self.worker_type == WebWorkerType::Module {
panic!(
"coding error: either js is polling or the worker is terminated"
);
} else {
- eprintln!("classic worker terminated unexpectedly");
+ log::error!("classic worker terminated unexpectedly");
Poll::Ready(Ok(()))
}
}
@@ -895,7 +907,6 @@ impl WebWorker {
}
}
-#[allow(clippy::print_stderr)]
fn print_worker_error(
error: &AnyError,
name: &str,
@@ -908,7 +919,7 @@ fn print_worker_error(
},
None => error.to_string(),
};
- eprintln!(
+ log::error!(
"{}: Uncaught (in worker \"{}\") {}",
colors::red_bold("error"),
name,
diff --git a/runtime/worker.rs b/runtime/worker.rs
index 477d3b880..909147df9 100644
--- a/runtime/worker.rs
+++ b/runtime/worker.rs
@@ -20,6 +20,8 @@ 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;
use deno_core::LocalInspectorSession;
use deno_core::ModuleCodeString;
@@ -141,6 +143,7 @@ pub struct WorkerServiceOptions {
pub npm_process_state_provider: Option<NpmProcessStateProviderRc>,
pub permissions: PermissionsContainer,
pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
+ pub fetch_dns_resolver: deno_fetch::dns::Resolver,
/// The store to use for transferring SharedArrayBuffers between isolates.
/// If multiple isolates should have the possibility of sharing
@@ -361,6 +364,7 @@ impl MainWorker {
.unsafely_ignore_certificate_errors
.clone(),
file_fetch_handler: Rc::new(deno_fetch::FsFetchHandler),
+ resolver: services.fetch_dns_resolver,
..Default::default()
},
),
@@ -403,7 +407,9 @@ impl MainWorker {
),
deno_cron::deno_cron::init_ops_and_esm(LocalCronHandler::new()),
deno_napi::deno_napi::init_ops_and_esm::<PermissionsContainer>(),
- deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(),
+ deno_http::deno_http::init_ops_and_esm::<DefaultHttpPropertyExtractor>(
+ deno_http::Options::default(),
+ ),
deno_io::deno_io::init_ops_and_esm(Some(options.stdio)),
deno_fs::deno_fs::init_ops_and_esm::<PermissionsContainer>(
services.fs.clone(),
@@ -420,6 +426,7 @@ impl MainWorker {
),
ops::fs_events::deno_fs_events::init_ops_and_esm(),
ops::os::deno_os::init_ops_and_esm(exit_code.clone()),
+ ops::otel::deno_otel::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,
@@ -486,7 +493,7 @@ impl MainWorker {
extension_transpiler: Some(Rc::new(|specifier, source| {
maybe_transpile_source(specifier, source)
})),
- inspector: options.maybe_inspector_server.is_some(),
+ inspector: true,
is_main: true,
feature_checker: Some(services.feature_checker.clone()),
op_metrics_factory_fn,
@@ -544,6 +551,12 @@ impl MainWorker {
js_runtime.op_state().borrow_mut().put(op_summary_metrics);
}
+ // Put inspector handle into the op state so we can put a breakpoint when
+ // executing a CJS entrypoint.
+ let op_state = js_runtime.op_state();
+ let inspector = js_runtime.inspector();
+ op_state.borrow_mut().put(inspector);
+
if let Some(server) = options.maybe_inspector_server.clone() {
server.register_inspector(
main_module.to_string(),
@@ -551,13 +564,8 @@ impl MainWorker {
options.should_break_on_first_statement
|| options.should_wait_for_inspector_session,
);
-
- // Put inspector handle into the op state so we can put a breakpoint when
- // executing a CJS entrypoint.
- let op_state = js_runtime.op_state();
- let inspector = js_runtime.inspector();
- op_state.borrow_mut().put(inspector);
}
+
let (
bootstrap_fn_global,
dispatch_load_event_fn_global,
@@ -792,7 +800,11 @@ impl MainWorker {
/// was not configured to create inspector.
pub fn create_inspector_session(&mut self) -> LocalInspectorSession {
self.js_runtime.maybe_init_inspector();
- self.js_runtime.inspector().borrow().create_local_session()
+ self.js_runtime.inspector().borrow().create_local_session(
+ InspectorSessionOptions {
+ kind: InspectorSessionKind::Blocking,
+ },
+ )
}
pub async fn run_event_loop(
diff --git a/runtime/worker_bootstrap.rs b/runtime/worker_bootstrap.rs
index 3f3c25c5e..3f5c245a0 100644
--- a/runtime/worker_bootstrap.rs
+++ b/runtime/worker_bootstrap.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use crate::ops::otel::OtelConfig;
use deno_core::v8;
use deno_core::ModuleSpecifier;
use serde::Serialize;
@@ -118,6 +119,8 @@ pub struct BootstrapOptions {
// Used by `deno serve`
pub serve_port: Option<u16>,
pub serve_host: Option<String>,
+ // OpenTelemetry output options. If `None`, OpenTelemetry is disabled.
+ pub otel_config: Option<OtelConfig>,
}
impl Default for BootstrapOptions {
@@ -152,6 +155,7 @@ impl Default for BootstrapOptions {
mode: WorkerExecutionMode::None,
serve_port: Default::default(),
serve_host: Default::default(),
+ otel_config: None,
}
}
}
@@ -193,6 +197,8 @@ struct BootstrapV8<'a>(
Option<bool>,
// serve worker count
Option<usize>,
+ // OTEL config
+ Box<[u8]>,
);
impl BootstrapOptions {
@@ -219,6 +225,11 @@ impl BootstrapOptions {
self.serve_host.as_deref(),
serve_is_main,
serve_worker_count,
+ if let Some(otel_config) = self.otel_config.as_ref() {
+ Box::new([otel_config.console as u8, otel_config.deterministic as u8])
+ } else {
+ Box::new([])
+ },
);
bootstrap.serialize(ser).unwrap()