mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -05:00
Merge remote-tracking branch 'upstream/main' into open-flag-on-serve
This commit is contained in:
commit
4f005ca82a
67 changed files with 2033 additions and 2131 deletions
|
@ -1941,6 +1941,7 @@ On the first invocation with deno will download the proper binary and cache it i
|
|||
])
|
||||
.help_heading(COMPILE_HEADING),
|
||||
)
|
||||
.arg(no_code_cache_arg())
|
||||
.arg(
|
||||
Arg::new("no-terminal")
|
||||
.long("no-terminal")
|
||||
|
@ -4433,6 +4434,8 @@ fn compile_parse(
|
|||
};
|
||||
ext_arg_parse(flags, matches);
|
||||
|
||||
flags.code_cache_enabled = !matches.get_flag("no-code-cache");
|
||||
|
||||
flags.subcommand = DenoSubcommand::Compile(CompileFlags {
|
||||
source_file,
|
||||
output,
|
||||
|
@ -10044,6 +10047,7 @@ mod tests {
|
|||
include: vec![]
|
||||
}),
|
||||
type_check_mode: TypeCheckMode::Local,
|
||||
code_cache_enabled: true,
|
||||
..Flags::default()
|
||||
}
|
||||
);
|
||||
|
@ -10052,7 +10056,7 @@ mod tests {
|
|||
#[test]
|
||||
fn compile_with_flags() {
|
||||
#[rustfmt::skip]
|
||||
let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--icon", "favicon.ico", "--output", "colors", "--env=.example.env", "https://examples.deno.land/color-logging.ts", "foo", "bar", "-p", "8080"]);
|
||||
let r = flags_from_vec(svec!["deno", "compile", "--import-map", "import_map.json", "--no-code-cache", "--no-remote", "--config", "tsconfig.json", "--no-check", "--unsafely-ignore-certificate-errors", "--reload", "--lock", "lock.json", "--cert", "example.crt", "--cached-only", "--location", "https:foo", "--allow-read", "--allow-net", "--v8-flags=--help", "--seed", "1", "--no-terminal", "--icon", "favicon.ico", "--output", "colors", "--env=.example.env", "https://examples.deno.land/color-logging.ts", "foo", "bar", "-p", "8080"]);
|
||||
assert_eq!(
|
||||
r.unwrap(),
|
||||
Flags {
|
||||
|
@ -10068,6 +10072,7 @@ mod tests {
|
|||
}),
|
||||
import_map_path: Some("import_map.json".to_string()),
|
||||
no_remote: true,
|
||||
code_cache_enabled: false,
|
||||
config_flag: ConfigFlag::Path("tsconfig.json".to_owned()),
|
||||
type_check_mode: TypeCheckMode::None,
|
||||
reload: true,
|
||||
|
|
18
cli/build.rs
18
cli/build.rs
|
@ -400,6 +400,24 @@ fn main() {
|
|||
println!("cargo:rustc-env=TARGET={}", env::var("TARGET").unwrap());
|
||||
println!("cargo:rustc-env=PROFILE={}", env::var("PROFILE").unwrap());
|
||||
|
||||
if cfg!(windows) {
|
||||
// these dls load slowly, so delay loading them
|
||||
let dlls = [
|
||||
// webgpu
|
||||
"d3dcompiler_47",
|
||||
"OPENGL32",
|
||||
// network related functions
|
||||
"iphlpapi",
|
||||
];
|
||||
for dll in dlls {
|
||||
println!("cargo:rustc-link-arg-bin=deno=/delayload:{dll}.dll");
|
||||
println!("cargo:rustc-link-arg-bin=denort=/delayload:{dll}.dll");
|
||||
}
|
||||
// enable delay loading
|
||||
println!("cargo:rustc-link-arg-bin=deno=delayimp.lib");
|
||||
println!("cargo:rustc-link-arg-bin=denort=delayimp.lib");
|
||||
}
|
||||
|
||||
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
|
|
10
cli/cache/code_cache.rs
vendored
10
cli/cache/code_cache.rs
vendored
|
@ -1,10 +1,14 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_runtime::code_cache;
|
||||
use deno_runtime::deno_webstorage::rusqlite::params;
|
||||
|
||||
use crate::worker::CliCodeCache;
|
||||
|
||||
use super::cache_db::CacheDB;
|
||||
use super::cache_db::CacheDBConfiguration;
|
||||
use super::cache_db::CacheDBHash;
|
||||
|
@ -82,6 +86,12 @@ impl CodeCache {
|
|||
}
|
||||
}
|
||||
|
||||
impl CliCodeCache for CodeCache {
|
||||
fn as_code_cache(self: Arc<Self>) -> Arc<dyn code_cache::CodeCache> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl code_cache::CodeCache for CodeCache {
|
||||
fn get_sync(
|
||||
&self,
|
||||
|
|
|
@ -1396,9 +1396,11 @@ impl Inner {
|
|||
.fmt_config_for_specifier(&specifier)
|
||||
.options
|
||||
.clone();
|
||||
let config_data = self.config.tree.data_for_specifier(&specifier);
|
||||
if !config_data.is_some_and(|d| d.maybe_deno_json().is_some()) {
|
||||
fmt_options.use_tabs = Some(!params.options.insert_spaces);
|
||||
fmt_options.indent_width = Some(params.options.tab_size as u8);
|
||||
let config_data = self.config.tree.data_for_specifier(&specifier);
|
||||
}
|
||||
let unstable_options = UnstableFmtOptions {
|
||||
component: config_data
|
||||
.map(|d| d.unstable.contains("fmt-component"))
|
||||
|
|
|
@ -64,6 +64,7 @@ use crate::args::NpmInstallDepsProvider;
|
|||
use crate::args::PermissionFlags;
|
||||
use crate::args::UnstableConfig;
|
||||
use crate::cache::DenoDir;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::emit::Emitter;
|
||||
use crate::file_fetcher::FileFetcher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
|
@ -174,6 +175,7 @@ pub struct SerializedWorkspaceResolver {
|
|||
pub struct Metadata {
|
||||
pub argv: Vec<String>,
|
||||
pub seed: Option<u64>,
|
||||
pub code_cache_key: Option<u64>,
|
||||
pub permissions: PermissionFlags,
|
||||
pub location: Option<Url>,
|
||||
pub v8_flags: Vec<String>,
|
||||
|
@ -604,10 +606,21 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
VfsBuilder::new(root_path.clone())?
|
||||
};
|
||||
let mut remote_modules_store = RemoteModulesStoreBuilder::default();
|
||||
let mut code_cache_key_hasher = if cli_options.code_cache_enabled() {
|
||||
Some(FastInsecureHasher::new_deno_versioned())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
for module in graph.modules() {
|
||||
if module.specifier().scheme() == "data" {
|
||||
continue; // don't store data urls as an entry as they're in the code
|
||||
}
|
||||
if let Some(hasher) = &mut code_cache_key_hasher {
|
||||
if let Some(source) = module.source() {
|
||||
hasher.write(module.specifier().as_str().as_bytes());
|
||||
hasher.write(source.as_bytes());
|
||||
}
|
||||
}
|
||||
let (maybe_source, media_type) = match module {
|
||||
deno_graph::Module::Js(m) => {
|
||||
let source = if m.media_type.is_emittable() {
|
||||
|
@ -675,6 +688,7 @@ impl<'a> DenoCompileBinaryWriter<'a> {
|
|||
let metadata = Metadata {
|
||||
argv: compile_flags.args.clone(),
|
||||
seed: cli_options.seed(),
|
||||
code_cache_key: code_cache_key_hasher.map(|h| h.finish()),
|
||||
location: cli_options.location_flag().clone(),
|
||||
permissions: cli_options.permission_flags().clone(),
|
||||
v8_flags: cli_options.v8_flags().clone(),
|
||||
|
|
514
cli/standalone/code_cache.rs
Normal file
514
cli/standalone/code_cache.rs
Normal file
|
@ -0,0 +1,514 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::HashMap;
|
||||
use std::io::BufReader;
|
||||
use std::io::BufWriter;
|
||||
use std::io::Read;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::bail;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_core::unsync::sync::AtomicFlag;
|
||||
use deno_runtime::code_cache::CodeCache;
|
||||
use deno_runtime::code_cache::CodeCacheType;
|
||||
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::util::path::get_atomic_file_path;
|
||||
use crate::worker::CliCodeCache;
|
||||
|
||||
enum CodeCacheStrategy {
|
||||
FirstRun(FirstRunCodeCacheStrategy),
|
||||
SubsequentRun(SubsequentRunCodeCacheStrategy),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct DenoCompileCodeCacheEntry {
|
||||
pub source_hash: u64,
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct DenoCompileCodeCache {
|
||||
strategy: CodeCacheStrategy,
|
||||
}
|
||||
|
||||
impl DenoCompileCodeCache {
|
||||
pub fn new(file_path: PathBuf, cache_key: u64) -> Self {
|
||||
// attempt to deserialize the cache data
|
||||
match deserialize(&file_path, cache_key) {
|
||||
Ok(data) => {
|
||||
log::debug!("Loaded {} code cache entries", data.len());
|
||||
Self {
|
||||
strategy: CodeCacheStrategy::SubsequentRun(
|
||||
SubsequentRunCodeCacheStrategy {
|
||||
is_finished: AtomicFlag::lowered(),
|
||||
data: Mutex::new(data),
|
||||
},
|
||||
),
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::debug!("Failed to deserialize code cache: {:#}", err);
|
||||
Self {
|
||||
strategy: CodeCacheStrategy::FirstRun(FirstRunCodeCacheStrategy {
|
||||
cache_key,
|
||||
file_path,
|
||||
is_finished: AtomicFlag::lowered(),
|
||||
data: Mutex::new(FirstRunCodeCacheData {
|
||||
cache: HashMap::new(),
|
||||
add_count: 0,
|
||||
}),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CodeCache for DenoCompileCodeCache {
|
||||
fn get_sync(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
code_cache_type: CodeCacheType,
|
||||
source_hash: u64,
|
||||
) -> Option<Vec<u8>> {
|
||||
match &self.strategy {
|
||||
CodeCacheStrategy::FirstRun(strategy) => {
|
||||
if !strategy.is_finished.is_raised() {
|
||||
// we keep track of how many times the cache is requested
|
||||
// then serialize the cache when we get that number of
|
||||
// "set" calls
|
||||
strategy.data.lock().add_count += 1;
|
||||
}
|
||||
None
|
||||
}
|
||||
CodeCacheStrategy::SubsequentRun(strategy) => {
|
||||
if strategy.is_finished.is_raised() {
|
||||
return None;
|
||||
}
|
||||
strategy.take_from_cache(specifier, code_cache_type, source_hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_sync(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
code_cache_type: CodeCacheType,
|
||||
source_hash: u64,
|
||||
bytes: &[u8],
|
||||
) {
|
||||
match &self.strategy {
|
||||
CodeCacheStrategy::FirstRun(strategy) => {
|
||||
if strategy.is_finished.is_raised() {
|
||||
return;
|
||||
}
|
||||
|
||||
let data_to_serialize = {
|
||||
let mut data = strategy.data.lock();
|
||||
data.cache.insert(
|
||||
(specifier.to_string(), code_cache_type),
|
||||
DenoCompileCodeCacheEntry {
|
||||
source_hash,
|
||||
data: bytes.to_vec(),
|
||||
},
|
||||
);
|
||||
if data.add_count != 0 {
|
||||
data.add_count -= 1;
|
||||
}
|
||||
if data.add_count == 0 {
|
||||
// don't allow using the cache anymore
|
||||
strategy.is_finished.raise();
|
||||
if data.cache.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(std::mem::take(&mut data.cache))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(cache_data) = &data_to_serialize {
|
||||
strategy.write_cache_data(cache_data);
|
||||
}
|
||||
}
|
||||
CodeCacheStrategy::SubsequentRun(_) => {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CliCodeCache for DenoCompileCodeCache {
|
||||
fn enabled(&self) -> bool {
|
||||
match &self.strategy {
|
||||
CodeCacheStrategy::FirstRun(strategy) => {
|
||||
!strategy.is_finished.is_raised()
|
||||
}
|
||||
CodeCacheStrategy::SubsequentRun(strategy) => {
|
||||
!strategy.is_finished.is_raised()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn as_code_cache(self: Arc<Self>) -> Arc<dyn CodeCache> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
type CodeCacheKey = (String, CodeCacheType);
|
||||
|
||||
struct FirstRunCodeCacheData {
|
||||
cache: HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||
add_count: usize,
|
||||
}
|
||||
|
||||
struct FirstRunCodeCacheStrategy {
|
||||
cache_key: u64,
|
||||
file_path: PathBuf,
|
||||
is_finished: AtomicFlag,
|
||||
data: Mutex<FirstRunCodeCacheData>,
|
||||
}
|
||||
|
||||
impl FirstRunCodeCacheStrategy {
|
||||
fn write_cache_data(
|
||||
&self,
|
||||
cache_data: &HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||
) {
|
||||
let count = cache_data.len();
|
||||
let temp_file = get_atomic_file_path(&self.file_path);
|
||||
match serialize(&temp_file, self.cache_key, cache_data) {
|
||||
Ok(()) => {
|
||||
if let Err(err) = std::fs::rename(&temp_file, &self.file_path) {
|
||||
log::debug!("Failed to rename code cache: {}", err);
|
||||
} else {
|
||||
log::debug!("Serialized {} code cache entries", count);
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
let _ = std::fs::remove_file(&temp_file);
|
||||
log::debug!("Failed to serialize code cache: {}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SubsequentRunCodeCacheStrategy {
|
||||
is_finished: AtomicFlag,
|
||||
data: Mutex<HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>>,
|
||||
}
|
||||
|
||||
impl SubsequentRunCodeCacheStrategy {
|
||||
fn take_from_cache(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
code_cache_type: CodeCacheType,
|
||||
source_hash: u64,
|
||||
) -> Option<Vec<u8>> {
|
||||
let mut data = self.data.lock();
|
||||
// todo(dsherret): how to avoid the clone here?
|
||||
let entry = data.remove(&(specifier.to_string(), code_cache_type))?;
|
||||
if entry.source_hash != source_hash {
|
||||
return None;
|
||||
}
|
||||
if data.is_empty() {
|
||||
self.is_finished.raise();
|
||||
}
|
||||
Some(entry.data)
|
||||
}
|
||||
}
|
||||
|
||||
/// File format:
|
||||
/// - <header>
|
||||
/// - <cache key>
|
||||
/// - <u32: number of entries>
|
||||
/// - <[entry length]> - u64 * number of entries
|
||||
/// - <[entry]>
|
||||
/// - <[u8]: entry data>
|
||||
/// - <String: specifier>
|
||||
/// - <u8>: code cache type
|
||||
/// - <u32: specifier length>
|
||||
/// - <u64: source hash>
|
||||
/// - <u64: entry data hash>
|
||||
fn serialize(
|
||||
file_path: &Path,
|
||||
cache_key: u64,
|
||||
cache: &HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||
) -> Result<(), AnyError> {
|
||||
let cache_file = std::fs::OpenOptions::new()
|
||||
.create(true)
|
||||
.truncate(true)
|
||||
.write(true)
|
||||
.open(file_path)?;
|
||||
let mut writer = BufWriter::new(cache_file);
|
||||
serialize_with_writer(&mut writer, cache_key, cache)
|
||||
}
|
||||
|
||||
fn serialize_with_writer<T: Write>(
|
||||
writer: &mut BufWriter<T>,
|
||||
cache_key: u64,
|
||||
cache: &HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>,
|
||||
) -> Result<(), AnyError> {
|
||||
// header
|
||||
writer.write_all(&cache_key.to_le_bytes())?;
|
||||
writer.write_all(&(cache.len() as u32).to_le_bytes())?;
|
||||
// lengths of each entry
|
||||
for ((specifier, _), entry) in cache {
|
||||
let len: u64 =
|
||||
entry.data.len() as u64 + specifier.len() as u64 + 1 + 4 + 8 + 8;
|
||||
writer.write_all(&len.to_le_bytes())?;
|
||||
}
|
||||
// entries
|
||||
for ((specifier, code_cache_type), entry) in cache {
|
||||
writer.write_all(&entry.data)?;
|
||||
writer.write_all(&[match code_cache_type {
|
||||
CodeCacheType::EsModule => 0,
|
||||
CodeCacheType::Script => 1,
|
||||
}])?;
|
||||
writer.write_all(specifier.as_bytes())?;
|
||||
writer.write_all(&(specifier.len() as u32).to_le_bytes())?;
|
||||
writer.write_all(&entry.source_hash.to_le_bytes())?;
|
||||
let hash: u64 = FastInsecureHasher::new_without_deno_version()
|
||||
.write(&entry.data)
|
||||
.finish();
|
||||
writer.write_all(&hash.to_le_bytes())?;
|
||||
}
|
||||
|
||||
writer.flush()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deserialize(
|
||||
file_path: &Path,
|
||||
expected_cache_key: u64,
|
||||
) -> Result<HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>, AnyError> {
|
||||
let cache_file = std::fs::File::open(file_path)?;
|
||||
let mut reader = BufReader::new(cache_file);
|
||||
deserialize_with_reader(&mut reader, expected_cache_key)
|
||||
}
|
||||
|
||||
fn deserialize_with_reader<T: Read>(
|
||||
reader: &mut BufReader<T>,
|
||||
expected_cache_key: u64,
|
||||
) -> Result<HashMap<CodeCacheKey, DenoCompileCodeCacheEntry>, AnyError> {
|
||||
// it's very important to use this below so that a corrupt cache file
|
||||
// doesn't cause a memory allocation error
|
||||
fn new_vec_sized<T: Clone>(
|
||||
capacity: usize,
|
||||
default_value: T,
|
||||
) -> Result<Vec<T>, AnyError> {
|
||||
let mut vec = Vec::new();
|
||||
vec.try_reserve(capacity)?;
|
||||
vec.resize(capacity, default_value);
|
||||
Ok(vec)
|
||||
}
|
||||
|
||||
fn try_subtract(a: usize, b: usize) -> Result<usize, AnyError> {
|
||||
if a < b {
|
||||
bail!("Integer underflow");
|
||||
}
|
||||
Ok(a - b)
|
||||
}
|
||||
|
||||
let mut header_bytes = vec![0; 8 + 4];
|
||||
reader.read_exact(&mut header_bytes)?;
|
||||
let actual_cache_key = u64::from_le_bytes(header_bytes[..8].try_into()?);
|
||||
if actual_cache_key != expected_cache_key {
|
||||
// cache bust
|
||||
bail!("Cache key mismatch");
|
||||
}
|
||||
let len = u32::from_le_bytes(header_bytes[8..].try_into()?) as usize;
|
||||
// read the lengths for each entry found in the file
|
||||
let entry_len_bytes_capacity = len * 8;
|
||||
let mut entry_len_bytes = new_vec_sized(entry_len_bytes_capacity, 0)?;
|
||||
reader.read_exact(&mut entry_len_bytes)?;
|
||||
let mut lengths = Vec::new();
|
||||
lengths.try_reserve(len)?;
|
||||
for i in 0..len {
|
||||
let pos = i * 8;
|
||||
lengths.push(
|
||||
u64::from_le_bytes(entry_len_bytes[pos..pos + 8].try_into()?) as usize,
|
||||
);
|
||||
}
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.try_reserve(len)?;
|
||||
for len in lengths {
|
||||
let mut buffer = new_vec_sized(len, 0)?;
|
||||
reader.read_exact(&mut buffer)?;
|
||||
let entry_data_hash_start_pos = try_subtract(buffer.len(), 8)?;
|
||||
let expected_entry_data_hash =
|
||||
u64::from_le_bytes(buffer[entry_data_hash_start_pos..].try_into()?);
|
||||
let source_hash_start_pos = try_subtract(entry_data_hash_start_pos, 8)?;
|
||||
let source_hash = u64::from_le_bytes(
|
||||
buffer[source_hash_start_pos..entry_data_hash_start_pos].try_into()?,
|
||||
);
|
||||
let specifier_end_pos = try_subtract(source_hash_start_pos, 4)?;
|
||||
let specifier_len = u32::from_le_bytes(
|
||||
buffer[specifier_end_pos..source_hash_start_pos].try_into()?,
|
||||
) as usize;
|
||||
let specifier_start_pos = try_subtract(specifier_end_pos, specifier_len)?;
|
||||
let specifier = String::from_utf8(
|
||||
buffer[specifier_start_pos..specifier_end_pos].to_vec(),
|
||||
)?;
|
||||
let code_cache_type_pos = try_subtract(specifier_start_pos, 1)?;
|
||||
let code_cache_type = match buffer[code_cache_type_pos] {
|
||||
0 => CodeCacheType::EsModule,
|
||||
1 => CodeCacheType::Script,
|
||||
_ => bail!("Invalid code cache type"),
|
||||
};
|
||||
buffer.truncate(code_cache_type_pos);
|
||||
let actual_entry_data_hash: u64 =
|
||||
FastInsecureHasher::new_without_deno_version()
|
||||
.write(&buffer)
|
||||
.finish();
|
||||
if expected_entry_data_hash != actual_entry_data_hash {
|
||||
bail!("Hash mismatch.")
|
||||
}
|
||||
map.insert(
|
||||
(specifier, code_cache_type),
|
||||
DenoCompileCodeCacheEntry {
|
||||
source_hash,
|
||||
data: buffer,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Ok(map)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use test_util::TempDir;
|
||||
|
||||
use super::*;
|
||||
use std::fs::File;
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize() {
|
||||
let cache_key = 123456;
|
||||
let cache = {
|
||||
let mut cache = HashMap::new();
|
||||
cache.insert(
|
||||
("specifier1".to_string(), CodeCacheType::EsModule),
|
||||
DenoCompileCodeCacheEntry {
|
||||
source_hash: 1,
|
||||
data: vec![1, 2, 3],
|
||||
},
|
||||
);
|
||||
cache.insert(
|
||||
("specifier2".to_string(), CodeCacheType::EsModule),
|
||||
DenoCompileCodeCacheEntry {
|
||||
source_hash: 2,
|
||||
data: vec![4, 5, 6],
|
||||
},
|
||||
);
|
||||
cache.insert(
|
||||
("specifier2".to_string(), CodeCacheType::Script),
|
||||
DenoCompileCodeCacheEntry {
|
||||
source_hash: 2,
|
||||
data: vec![6, 5, 1],
|
||||
},
|
||||
);
|
||||
cache
|
||||
};
|
||||
let mut buffer = Vec::new();
|
||||
serialize_with_writer(&mut BufWriter::new(&mut buffer), cache_key, &cache)
|
||||
.unwrap();
|
||||
let deserialized =
|
||||
deserialize_with_reader(&mut BufReader::new(&buffer[..]), cache_key)
|
||||
.unwrap();
|
||||
assert_eq!(cache, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_empty() {
|
||||
let cache_key = 1234;
|
||||
let cache = HashMap::new();
|
||||
let mut buffer = Vec::new();
|
||||
serialize_with_writer(&mut BufWriter::new(&mut buffer), cache_key, &cache)
|
||||
.unwrap();
|
||||
let deserialized =
|
||||
deserialize_with_reader(&mut BufReader::new(&buffer[..]), cache_key)
|
||||
.unwrap();
|
||||
assert_eq!(cache, deserialized);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn serialize_deserialize_corrupt() {
|
||||
let buffer = "corrupttestingtestingtesting".as_bytes().to_vec();
|
||||
let err = deserialize_with_reader(&mut BufReader::new(&buffer[..]), 1234)
|
||||
.unwrap_err();
|
||||
assert_eq!(err.to_string(), "Cache key mismatch");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn code_cache() {
|
||||
let temp_dir = TempDir::new();
|
||||
let file_path = temp_dir.path().join("cache.bin").to_path_buf();
|
||||
let url1 = ModuleSpecifier::parse("https://deno.land/example1.js").unwrap();
|
||||
let url2 = ModuleSpecifier::parse("https://deno.land/example2.js").unwrap();
|
||||
// first run
|
||||
{
|
||||
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 1234);
|
||||
assert!(code_cache
|
||||
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||
.is_none());
|
||||
assert!(code_cache
|
||||
.get_sync(&url2, CodeCacheType::EsModule, 1)
|
||||
.is_none());
|
||||
assert!(code_cache.enabled());
|
||||
code_cache.set_sync(url1.clone(), CodeCacheType::EsModule, 0, &[1, 2, 3]);
|
||||
assert!(code_cache.enabled());
|
||||
assert!(!file_path.exists());
|
||||
code_cache.set_sync(url2.clone(), CodeCacheType::EsModule, 1, &[2, 1, 3]);
|
||||
assert!(file_path.exists()); // now the new code cache exists
|
||||
assert!(!code_cache.enabled()); // no longer enabled
|
||||
}
|
||||
// second run
|
||||
{
|
||||
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 1234);
|
||||
assert!(code_cache.enabled());
|
||||
let result1 = code_cache
|
||||
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||
.unwrap();
|
||||
assert!(code_cache.enabled());
|
||||
let result2 = code_cache
|
||||
.get_sync(&url2, CodeCacheType::EsModule, 1)
|
||||
.unwrap();
|
||||
assert!(!code_cache.enabled()); // no longer enabled
|
||||
assert_eq!(result1, vec![1, 2, 3]);
|
||||
assert_eq!(result2, vec![2, 1, 3]);
|
||||
}
|
||||
|
||||
// new cache key first run
|
||||
{
|
||||
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 54321);
|
||||
assert!(code_cache
|
||||
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||
.is_none());
|
||||
assert!(code_cache
|
||||
.get_sync(&url2, CodeCacheType::EsModule, 1)
|
||||
.is_none());
|
||||
code_cache.set_sync(url1.clone(), CodeCacheType::EsModule, 0, &[2, 2, 3]);
|
||||
code_cache.set_sync(url2.clone(), CodeCacheType::EsModule, 1, &[3, 2, 3]);
|
||||
}
|
||||
// new cache key second run
|
||||
{
|
||||
let code_cache = DenoCompileCodeCache::new(file_path.clone(), 54321);
|
||||
let result1 = code_cache
|
||||
.get_sync(&url1, CodeCacheType::EsModule, 0)
|
||||
.unwrap();
|
||||
assert_eq!(result1, vec![2, 2, 3]);
|
||||
assert!(code_cache
|
||||
.get_sync(&url2, CodeCacheType::EsModule, 5) // different hash will cause none
|
||||
.is_none());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
use binary::StandaloneData;
|
||||
use binary::StandaloneModules;
|
||||
use code_cache::DenoCompileCodeCache;
|
||||
use deno_ast::MediaType;
|
||||
use deno_cache_dir::npm::NpmCacheDir;
|
||||
use deno_config::workspace::MappedResolution;
|
||||
|
@ -17,6 +18,7 @@ use deno_core::anyhow::Context;
|
|||
use deno_core::error::generic_error;
|
||||
use deno_core::error::type_error;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::future::LocalBoxFuture;
|
||||
use deno_core::futures::FutureExt;
|
||||
use deno_core::v8_set_flags;
|
||||
use deno_core::FastString;
|
||||
|
@ -27,6 +29,7 @@ use deno_core::ModuleSpecifier;
|
|||
use deno_core::ModuleType;
|
||||
use deno_core::RequestedModuleType;
|
||||
use deno_core::ResolutionKind;
|
||||
use deno_core::SourceCodeCacheInfo;
|
||||
use deno_npm::npm_rc::ResolvedNpmRc;
|
||||
use deno_package_json::PackageJsonDepValue;
|
||||
use deno_resolver::npm::NpmReqResolverOptions;
|
||||
|
@ -64,6 +67,7 @@ use crate::args::StorageKeyResolver;
|
|||
use crate::cache::Caches;
|
||||
use crate::cache::DenoCacheEnvFsAdapter;
|
||||
use crate::cache::DenoDirProvider;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::cache::NodeAnalysisCache;
|
||||
use crate::cache::RealDenoCacheEnv;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
|
@ -86,12 +90,14 @@ use crate::resolver::NpmModuleLoader;
|
|||
use crate::util::progress_bar::ProgressBar;
|
||||
use crate::util::progress_bar::ProgressBarStyle;
|
||||
use crate::util::v8::construct_v8_flags;
|
||||
use crate::worker::CliCodeCache;
|
||||
use crate::worker::CliMainWorkerFactory;
|
||||
use crate::worker::CliMainWorkerOptions;
|
||||
use crate::worker::CreateModuleLoaderResult;
|
||||
use crate::worker::ModuleLoaderFactory;
|
||||
|
||||
pub mod binary;
|
||||
mod code_cache;
|
||||
mod file_system;
|
||||
mod serialization;
|
||||
mod virtual_fs;
|
||||
|
@ -113,6 +119,35 @@ struct SharedModuleLoaderState {
|
|||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
workspace_resolver: WorkspaceResolver,
|
||||
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||
}
|
||||
|
||||
impl SharedModuleLoaderState {
|
||||
fn get_code_cache(
|
||||
&self,
|
||||
specifier: &ModuleSpecifier,
|
||||
source: &[u8],
|
||||
) -> Option<SourceCodeCacheInfo> {
|
||||
let Some(code_cache) = &self.code_cache else {
|
||||
return None;
|
||||
};
|
||||
if !code_cache.enabled() {
|
||||
return None;
|
||||
}
|
||||
// deno version is already included in the root cache key
|
||||
let hash = FastInsecureHasher::new_without_deno_version()
|
||||
.write_hashable(source)
|
||||
.finish();
|
||||
let data = code_cache.get_sync(
|
||||
specifier,
|
||||
deno_runtime::code_cache::CodeCacheType::EsModule,
|
||||
hash,
|
||||
);
|
||||
Some(SourceCodeCacheInfo {
|
||||
hash,
|
||||
data: data.map(Cow::Owned),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -329,14 +364,19 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
}
|
||||
|
||||
if self.shared.node_resolver.in_npm_package(original_specifier) {
|
||||
let npm_module_loader = self.shared.npm_module_loader.clone();
|
||||
let shared = self.shared.clone();
|
||||
let original_specifier = original_specifier.clone();
|
||||
let maybe_referrer = maybe_referrer.cloned();
|
||||
return deno_core::ModuleLoadResponse::Async(
|
||||
async move {
|
||||
let code_source = npm_module_loader
|
||||
let code_source = shared
|
||||
.npm_module_loader
|
||||
.load(&original_specifier, maybe_referrer.as_ref())
|
||||
.await?;
|
||||
let code_cache_entry = shared.get_code_cache(
|
||||
&code_source.found_url,
|
||||
code_source.code.as_bytes(),
|
||||
);
|
||||
Ok(deno_core::ModuleSource::new_with_redirect(
|
||||
match code_source.media_type {
|
||||
MediaType::Json => ModuleType::Json,
|
||||
|
@ -345,7 +385,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
code_source.code,
|
||||
&original_specifier,
|
||||
&code_source.found_url,
|
||||
None,
|
||||
code_cache_entry,
|
||||
))
|
||||
}
|
||||
.boxed_local(),
|
||||
|
@ -398,25 +438,30 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
ModuleSourceCode::String(FastString::from_static(source))
|
||||
}
|
||||
};
|
||||
let code_cache_entry = shared
|
||||
.get_code_cache(&module_specifier, module_source.as_bytes());
|
||||
Ok(deno_core::ModuleSource::new_with_redirect(
|
||||
module_type,
|
||||
module_source,
|
||||
&original_specifier,
|
||||
&module_specifier,
|
||||
None,
|
||||
code_cache_entry,
|
||||
))
|
||||
}
|
||||
.boxed_local(),
|
||||
)
|
||||
} else {
|
||||
let module_source = module_source.into_for_v8();
|
||||
let code_cache_entry = self
|
||||
.shared
|
||||
.get_code_cache(module_specifier, module_source.as_bytes());
|
||||
deno_core::ModuleLoadResponse::Sync(Ok(
|
||||
deno_core::ModuleSource::new_with_redirect(
|
||||
module_type,
|
||||
module_source,
|
||||
original_specifier,
|
||||
module_specifier,
|
||||
None,
|
||||
code_cache_entry,
|
||||
),
|
||||
))
|
||||
}
|
||||
|
@ -429,6 +474,23 @@ impl ModuleLoader for EmbeddedModuleLoader {
|
|||
))),
|
||||
}
|
||||
}
|
||||
|
||||
fn code_cache_ready(
|
||||
&self,
|
||||
specifier: ModuleSpecifier,
|
||||
source_hash: u64,
|
||||
code_cache_data: &[u8],
|
||||
) -> LocalBoxFuture<'static, ()> {
|
||||
if let Some(code_cache) = &self.shared.code_cache {
|
||||
code_cache.set_sync(
|
||||
specifier,
|
||||
deno_runtime::code_cache::CodeCacheType::EsModule,
|
||||
source_hash,
|
||||
code_cache_data,
|
||||
);
|
||||
}
|
||||
std::future::ready(()).boxed_local()
|
||||
}
|
||||
}
|
||||
|
||||
impl NodeRequireLoader for EmbeddedModuleLoader {
|
||||
|
@ -739,6 +801,19 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
metadata.workspace_resolver.pkg_json_resolution,
|
||||
)
|
||||
};
|
||||
let code_cache = match metadata.code_cache_key {
|
||||
Some(code_cache_key) => Some(Arc::new(DenoCompileCodeCache::new(
|
||||
root_path.with_file_name(format!(
|
||||
"{}.cache",
|
||||
root_path.file_name().unwrap().to_string_lossy()
|
||||
)),
|
||||
code_cache_key,
|
||||
)) as Arc<dyn CliCodeCache>),
|
||||
None => {
|
||||
log::debug!("Code cache disabled.");
|
||||
None
|
||||
}
|
||||
};
|
||||
let module_loader_factory = StandaloneModuleLoaderFactory {
|
||||
shared: Arc::new(SharedModuleLoaderState {
|
||||
cjs_tracker: cjs_tracker.clone(),
|
||||
|
@ -751,6 +826,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
fs.clone(),
|
||||
node_code_translator,
|
||||
)),
|
||||
code_cache: code_cache.clone(),
|
||||
npm_resolver: npm_resolver.clone(),
|
||||
workspace_resolver,
|
||||
npm_req_resolver,
|
||||
|
@ -792,8 +868,7 @@ pub async fn run(data: StandaloneData) -> Result<i32, AnyError> {
|
|||
});
|
||||
let worker_factory = CliMainWorkerFactory::new(
|
||||
Arc::new(BlobStore::default()),
|
||||
// Code cache is not supported for standalone binary yet.
|
||||
None,
|
||||
code_cache,
|
||||
feature_checker,
|
||||
fs,
|
||||
None,
|
||||
|
|
|
@ -228,6 +228,7 @@ fn collect_fmt_files(
|
|||
})
|
||||
.ignore_git_folder()
|
||||
.ignore_node_modules()
|
||||
.use_gitignore()
|
||||
.set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned))
|
||||
.collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files)
|
||||
}
|
||||
|
|
|
@ -436,6 +436,7 @@ fn collect_lint_files(
|
|||
})
|
||||
.ignore_git_folder()
|
||||
.ignore_node_modules()
|
||||
.use_gitignore()
|
||||
.set_vendor_folder(cli_options.vendor_dir_path().map(ToOwned::to_owned))
|
||||
.collect_file_patterns(&deno_config::fs::RealDenoConfigFs, files)
|
||||
}
|
||||
|
|
123
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
123
cli/tsc/dts/lib.deno.unstable.d.ts
vendored
|
@ -1252,80 +1252,53 @@ declare namespace Deno {
|
|||
}
|
||||
|
||||
/**
|
||||
* **UNSTABLE**: New API, yet to be vetted.
|
||||
*
|
||||
* APIs for working with the OpenTelemetry observability framework. Deno can
|
||||
* export traces, metrics, and logs to OpenTelemetry compatible backends via
|
||||
* the OTLP protocol.
|
||||
*
|
||||
* Deno automatically instruments the runtime with OpenTelemetry traces and
|
||||
* metrics. This data is exported via OTLP to OpenTelemetry compatible
|
||||
* backends. User logs from the `console` API are exported as OpenTelemetry
|
||||
* logs via OTLP.
|
||||
*
|
||||
* User code can also create custom traces, metrics, and logs using the
|
||||
* OpenTelemetry API. This is done using the official OpenTelemetry package
|
||||
* for JavaScript:
|
||||
* [`npm:@opentelemetry/api`](https://opentelemetry.io/docs/languages/js/).
|
||||
* Deno integrates with this package to provide trace context propagation
|
||||
* between native Deno APIs (like `Deno.serve` or `fetch`) and custom user
|
||||
* code. Deno also provides APIs that allow exporting custom telemetry data
|
||||
* via the same OTLP channel used by the Deno runtime. This is done using the
|
||||
* [`jsr:@deno/otel`](https://jsr.io/@deno/otel) package.
|
||||
*
|
||||
* @example Using OpenTelemetry API to create custom traces
|
||||
* ```ts,ignore
|
||||
* import { trace } from "npm:@opentelemetry/api@1";
|
||||
* import "jsr:@deno/otel@0.0.2/register";
|
||||
*
|
||||
* const tracer = trace.getTracer("example-tracer");
|
||||
*
|
||||
* async function doWork() {
|
||||
* return tracer.startActiveSpan("doWork", async (span) => {
|
||||
* span.setAttribute("key", "value");
|
||||
* await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
* span.end();
|
||||
* });
|
||||
* }
|
||||
*
|
||||
* Deno.serve(async (req) => {
|
||||
* await doWork();
|
||||
* const resp = await fetch("https://example.com");
|
||||
* return resp;
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @category Telemetry
|
||||
* @experimental
|
||||
*/
|
||||
export namespace tracing {
|
||||
/**
|
||||
* Whether tracing is enabled.
|
||||
* @category Telemetry
|
||||
* @experimental
|
||||
*/
|
||||
export const enabled: boolean;
|
||||
|
||||
/**
|
||||
* Allowed attribute type.
|
||||
* @category Telemetry
|
||||
* @experimental
|
||||
*/
|
||||
export type AttributeValue = string | number | boolean | bigint;
|
||||
|
||||
/**
|
||||
* A tracing span.
|
||||
* @category Telemetry
|
||||
* @experimental
|
||||
*/
|
||||
export class Span implements Disposable {
|
||||
readonly traceId: string;
|
||||
readonly spanId: string;
|
||||
readonly parentSpanId: string;
|
||||
readonly kind: string;
|
||||
readonly name: string;
|
||||
readonly startTime: number;
|
||||
readonly endTime: number;
|
||||
readonly status: null | { code: 1 } | { code: 2; message: string };
|
||||
readonly attributes: Record<string, AttributeValue>;
|
||||
readonly traceFlags: number;
|
||||
|
||||
/**
|
||||
* Construct a new Span and enter it as the "current" span.
|
||||
*/
|
||||
constructor(
|
||||
name: string,
|
||||
kind?: "internal" | "server" | "client" | "producer" | "consumer",
|
||||
);
|
||||
|
||||
/**
|
||||
* Set an attribute on this span.
|
||||
*/
|
||||
setAttribute(
|
||||
name: string,
|
||||
value: AttributeValue,
|
||||
): void;
|
||||
|
||||
/**
|
||||
* Enter this span as the "current" span.
|
||||
*/
|
||||
enter(): void;
|
||||
|
||||
/**
|
||||
* Exit this span as the "current" span and restore the previous one.
|
||||
*/
|
||||
exit(): void;
|
||||
|
||||
/**
|
||||
* End this span, and exit it as the "current" span.
|
||||
*/
|
||||
end(): void;
|
||||
|
||||
[Symbol.dispose](): void;
|
||||
|
||||
/**
|
||||
* Get the "current" span, if one exists.
|
||||
*/
|
||||
static current(): Span | undefined | null;
|
||||
}
|
||||
|
||||
export namespace telemetry {
|
||||
/**
|
||||
* A SpanExporter compatible with OpenTelemetry.js
|
||||
* https://open-telemetry.github.io/opentelemetry-js/interfaces/_opentelemetry_sdk_trace_base.SpanExporter.html
|
||||
|
@ -1345,14 +1318,6 @@ declare namespace Deno {
|
|||
export {}; // only export exports
|
||||
}
|
||||
|
||||
/**
|
||||
* @category Telemetry
|
||||
* @experimental
|
||||
*/
|
||||
export namespace metrics {
|
||||
export {}; // only export exports
|
||||
}
|
||||
|
||||
export {}; // only export exports
|
||||
}
|
||||
|
||||
|
|
|
@ -83,6 +83,15 @@ pub trait HmrRunner: Send + Sync {
|
|||
async fn run(&mut self) -> Result<(), AnyError>;
|
||||
}
|
||||
|
||||
pub trait CliCodeCache: code_cache::CodeCache {
|
||||
/// Gets if the code cache is still enabled.
|
||||
fn enabled(&self) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn as_code_cache(self: Arc<Self>) -> Arc<dyn code_cache::CodeCache>;
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub trait CoverageCollector: Send + Sync {
|
||||
async fn start_collecting(&mut self) -> Result<(), AnyError>;
|
||||
|
@ -127,7 +136,7 @@ pub struct CliMainWorkerOptions {
|
|||
struct SharedWorkerState {
|
||||
blob_store: Arc<BlobStore>,
|
||||
broadcast_channel: InMemoryBroadcastChannel,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||
compiled_wasm_module_store: CompiledWasmModuleStore,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
|
@ -393,7 +402,7 @@ impl CliMainWorkerFactory {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
blob_store: Arc<BlobStore>,
|
||||
code_cache: Option<Arc<dyn code_cache::CodeCache>>,
|
||||
code_cache: Option<Arc<dyn CliCodeCache>>,
|
||||
feature_checker: Arc<FeatureChecker>,
|
||||
fs: Arc<dyn deno_fs::FileSystem>,
|
||||
maybe_file_watcher_communicator: Option<Arc<WatcherCommunicator>>,
|
||||
|
@ -554,7 +563,7 @@ impl CliMainWorkerFactory {
|
|||
),
|
||||
feature_checker,
|
||||
permissions,
|
||||
v8_code_cache: shared.code_cache.clone(),
|
||||
v8_code_cache: shared.code_cache.clone().map(|c| c.as_code_cache()),
|
||||
};
|
||||
|
||||
let options = WorkerOptions {
|
||||
|
|
3
ext/console/internal.d.ts
vendored
3
ext/console/internal.d.ts
vendored
|
@ -9,4 +9,7 @@ declare module "ext:deno_console/01_console.js" {
|
|||
keys: (keyof TObject)[];
|
||||
evaluate: boolean;
|
||||
}): Record<string, unknown>;
|
||||
|
||||
class Console {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ use http_body_util::BodyExt;
|
|||
use hyper::body::Frame;
|
||||
use hyper_util::client::legacy::connect::HttpConnector;
|
||||
use hyper_util::client::legacy::connect::HttpInfo;
|
||||
use hyper_util::client::legacy::Builder as HyperClientBuilder;
|
||||
use hyper_util::rt::TokioExecutor;
|
||||
use hyper_util::rt::TokioTimer;
|
||||
use serde::Deserialize;
|
||||
|
@ -85,6 +86,16 @@ pub struct Options {
|
|||
pub user_agent: String,
|
||||
pub root_cert_store_provider: Option<Arc<dyn RootCertStoreProvider>>,
|
||||
pub proxy: Option<Proxy>,
|
||||
/// A callback to customize HTTP client configuration.
|
||||
///
|
||||
/// The settings applied with this hook may be overridden by the options
|
||||
/// provided through `Deno.createHttpClient()` API. For instance, if the hook
|
||||
/// calls [`hyper_util::client::legacy::Builder::pool_max_idle_per_host`] with
|
||||
/// a value of 99, and a user calls `Deno.createHttpClient({ poolMaxIdlePerHost: 42 })`,
|
||||
/// the value that will take effect is 42.
|
||||
///
|
||||
/// For more info on what can be configured, see [`hyper_util::client::legacy::Builder`].
|
||||
pub client_builder_hook: Option<fn(HyperClientBuilder) -> HyperClientBuilder>,
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub request_builder_hook: Option<
|
||||
fn(&mut http::Request<ReqBody>) -> Result<(), deno_core::error::AnyError>,
|
||||
|
@ -112,6 +123,7 @@ impl Default for Options {
|
|||
user_agent: "".to_string(),
|
||||
root_cert_store_provider: None,
|
||||
proxy: None,
|
||||
client_builder_hook: None,
|
||||
request_builder_hook: None,
|
||||
unsafely_ignore_certificate_errors: None,
|
||||
client_cert_chain_and_key: TlsKeys::Null,
|
||||
|
@ -271,6 +283,7 @@ pub fn create_client_from_options(
|
|||
pool_idle_timeout: None,
|
||||
http1: true,
|
||||
http2: true,
|
||||
client_builder_hook: options.client_builder_hook,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
@ -908,6 +921,7 @@ where
|
|||
),
|
||||
http1: args.http1,
|
||||
http2: args.http2,
|
||||
client_builder_hook: options.client_builder_hook,
|
||||
},
|
||||
)?;
|
||||
|
||||
|
@ -929,6 +943,7 @@ pub struct CreateHttpClientOptions {
|
|||
pub pool_idle_timeout: Option<Option<u64>>,
|
||||
pub http1: bool,
|
||||
pub http2: bool,
|
||||
pub client_builder_hook: Option<fn(HyperClientBuilder) -> HyperClientBuilder>,
|
||||
}
|
||||
|
||||
impl Default for CreateHttpClientOptions {
|
||||
|
@ -944,6 +959,7 @@ impl Default for CreateHttpClientOptions {
|
|||
pool_idle_timeout: None,
|
||||
http1: true,
|
||||
http2: true,
|
||||
client_builder_hook: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -999,11 +1015,14 @@ pub fn create_http_client(
|
|||
HttpClientCreateError::InvalidUserAgent(user_agent.to_string())
|
||||
})?;
|
||||
|
||||
let mut builder =
|
||||
hyper_util::client::legacy::Builder::new(TokioExecutor::new());
|
||||
let mut builder = HyperClientBuilder::new(TokioExecutor::new());
|
||||
builder.timer(TokioTimer::new());
|
||||
builder.pool_timer(TokioTimer::new());
|
||||
|
||||
if let Some(client_builder_hook) = options.client_builder_hook {
|
||||
builder = client_builder_hook(builder);
|
||||
}
|
||||
|
||||
let mut proxies = proxy::from_env();
|
||||
if let Some(proxy) = options.proxy {
|
||||
let mut intercept = proxy::Intercept::all(&proxy.url)
|
||||
|
|
|
@ -126,6 +126,7 @@ async fn rust_test_client_with_resolver(
|
|||
dns_resolver: resolver,
|
||||
http1: true,
|
||||
http2: true,
|
||||
client_builder_hook: None,
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
|
|
@ -18,6 +18,7 @@ use crate::service::HttpServerState;
|
|||
use crate::service::SignallingRc;
|
||||
use crate::websocket_upgrade::WebSocketUpgrade;
|
||||
use crate::LocalExecutor;
|
||||
use crate::Options;
|
||||
use cache_control::CacheControl;
|
||||
use deno_core::external;
|
||||
use deno_core::futures::future::poll_fn;
|
||||
|
@ -821,10 +822,16 @@ fn serve_http11_unconditional(
|
|||
io: impl HttpServeStream,
|
||||
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
||||
cancel: Rc<CancelHandle>,
|
||||
http1_builder_hook: Option<fn(http1::Builder) -> http1::Builder>,
|
||||
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
|
||||
let conn = http1::Builder::new()
|
||||
.keep_alive(true)
|
||||
.writev(*USE_WRITEV)
|
||||
let mut builder = http1::Builder::new();
|
||||
builder.keep_alive(true).writev(*USE_WRITEV);
|
||||
|
||||
if let Some(http1_builder_hook) = http1_builder_hook {
|
||||
builder = http1_builder_hook(builder);
|
||||
}
|
||||
|
||||
let conn = builder
|
||||
.serve_connection(TokioIo::new(io), svc)
|
||||
.with_upgrades();
|
||||
|
||||
|
@ -843,9 +850,17 @@ fn serve_http2_unconditional(
|
|||
io: impl HttpServeStream,
|
||||
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
||||
cancel: Rc<CancelHandle>,
|
||||
http2_builder_hook: Option<
|
||||
fn(http2::Builder<LocalExecutor>) -> http2::Builder<LocalExecutor>,
|
||||
>,
|
||||
) -> impl Future<Output = Result<(), hyper::Error>> + 'static {
|
||||
let conn =
|
||||
http2::Builder::new(LocalExecutor).serve_connection(TokioIo::new(io), svc);
|
||||
let mut builder = http2::Builder::new(LocalExecutor);
|
||||
|
||||
if let Some(http2_builder_hook) = http2_builder_hook {
|
||||
builder = http2_builder_hook(builder);
|
||||
}
|
||||
|
||||
let conn = builder.serve_connection(TokioIo::new(io), svc);
|
||||
async {
|
||||
match conn.or_abort(cancel).await {
|
||||
Err(mut conn) => {
|
||||
|
@ -861,15 +876,16 @@ async fn serve_http2_autodetect(
|
|||
io: impl HttpServeStream,
|
||||
svc: impl HttpService<Incoming, ResBody = HttpRecordResponse> + 'static,
|
||||
cancel: Rc<CancelHandle>,
|
||||
options: Options,
|
||||
) -> Result<(), HttpNextError> {
|
||||
let prefix = NetworkStreamPrefixCheck::new(io, HTTP2_PREFIX);
|
||||
let (matches, io) = prefix.match_prefix().await?;
|
||||
if matches {
|
||||
serve_http2_unconditional(io, svc, cancel)
|
||||
serve_http2_unconditional(io, svc, cancel, options.http2_builder_hook)
|
||||
.await
|
||||
.map_err(HttpNextError::Hyper)
|
||||
} else {
|
||||
serve_http11_unconditional(io, svc, cancel)
|
||||
serve_http11_unconditional(io, svc, cancel, options.http1_builder_hook)
|
||||
.await
|
||||
.map_err(HttpNextError::Hyper)
|
||||
}
|
||||
|
@ -880,6 +896,7 @@ fn serve_https(
|
|||
request_info: HttpConnectionProperties,
|
||||
lifetime: HttpLifetime,
|
||||
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
||||
options: Options,
|
||||
) -> JoinHandle<Result<(), HttpNextError>> {
|
||||
let HttpLifetime {
|
||||
server_state,
|
||||
|
@ -891,21 +908,31 @@ fn serve_https(
|
|||
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
|
||||
});
|
||||
spawn(
|
||||
async {
|
||||
async move {
|
||||
let handshake = io.handshake().await?;
|
||||
// If the client specifically negotiates a protocol, we will use it. If not, we'll auto-detect
|
||||
// based on the prefix bytes
|
||||
let handshake = handshake.alpn;
|
||||
if Some(TLS_ALPN_HTTP_2) == handshake.as_deref() {
|
||||
serve_http2_unconditional(io, svc, listen_cancel_handle)
|
||||
serve_http2_unconditional(
|
||||
io,
|
||||
svc,
|
||||
listen_cancel_handle,
|
||||
options.http2_builder_hook,
|
||||
)
|
||||
.await
|
||||
.map_err(HttpNextError::Hyper)
|
||||
} else if Some(TLS_ALPN_HTTP_11) == handshake.as_deref() {
|
||||
serve_http11_unconditional(io, svc, listen_cancel_handle)
|
||||
serve_http11_unconditional(
|
||||
io,
|
||||
svc,
|
||||
listen_cancel_handle,
|
||||
options.http1_builder_hook,
|
||||
)
|
||||
.await
|
||||
.map_err(HttpNextError::Hyper)
|
||||
} else {
|
||||
serve_http2_autodetect(io, svc, listen_cancel_handle).await
|
||||
serve_http2_autodetect(io, svc, listen_cancel_handle, options).await
|
||||
}
|
||||
}
|
||||
.try_or_cancel(connection_cancel_handle),
|
||||
|
@ -917,6 +944,7 @@ fn serve_http(
|
|||
request_info: HttpConnectionProperties,
|
||||
lifetime: HttpLifetime,
|
||||
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
||||
options: Options,
|
||||
) -> JoinHandle<Result<(), HttpNextError>> {
|
||||
let HttpLifetime {
|
||||
server_state,
|
||||
|
@ -928,7 +956,7 @@ fn serve_http(
|
|||
handle_request(req, request_info.clone(), server_state.clone(), tx.clone())
|
||||
});
|
||||
spawn(
|
||||
serve_http2_autodetect(io, svc, listen_cancel_handle)
|
||||
serve_http2_autodetect(io, svc, listen_cancel_handle, options)
|
||||
.try_or_cancel(connection_cancel_handle),
|
||||
)
|
||||
}
|
||||
|
@ -938,6 +966,7 @@ fn serve_http_on<HTTP>(
|
|||
listen_properties: &HttpListenProperties,
|
||||
lifetime: HttpLifetime,
|
||||
tx: tokio::sync::mpsc::Sender<Rc<HttpRecord>>,
|
||||
options: Options,
|
||||
) -> JoinHandle<Result<(), HttpNextError>>
|
||||
where
|
||||
HTTP: HttpPropertyExtractor,
|
||||
|
@ -949,14 +978,14 @@ where
|
|||
|
||||
match network_stream {
|
||||
NetworkStream::Tcp(conn) => {
|
||||
serve_http(conn, connection_properties, lifetime, tx)
|
||||
serve_http(conn, connection_properties, lifetime, tx, options)
|
||||
}
|
||||
NetworkStream::Tls(conn) => {
|
||||
serve_https(conn, connection_properties, lifetime, tx)
|
||||
serve_https(conn, connection_properties, lifetime, tx, options)
|
||||
}
|
||||
#[cfg(unix)]
|
||||
NetworkStream::Unix(conn) => {
|
||||
serve_http(conn, connection_properties, lifetime, tx)
|
||||
serve_http(conn, connection_properties, lifetime, tx, options)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1045,6 +1074,11 @@ where
|
|||
|
||||
let lifetime = resource.lifetime();
|
||||
|
||||
let options = {
|
||||
let state = state.borrow();
|
||||
*state.borrow::<Options>()
|
||||
};
|
||||
|
||||
let listen_properties_clone: HttpListenProperties = listen_properties.clone();
|
||||
let handle = spawn(async move {
|
||||
loop {
|
||||
|
@ -1057,6 +1091,7 @@ where
|
|||
&listen_properties_clone,
|
||||
lifetime.clone(),
|
||||
tx.clone(),
|
||||
options,
|
||||
);
|
||||
}
|
||||
#[allow(unreachable_code)]
|
||||
|
@ -1093,11 +1128,17 @@ where
|
|||
let (tx, rx) = tokio::sync::mpsc::channel(10);
|
||||
let resource: Rc<HttpJoinHandle> = Rc::new(HttpJoinHandle::new(rx));
|
||||
|
||||
let options = {
|
||||
let state = state.borrow();
|
||||
*state.borrow::<Options>()
|
||||
};
|
||||
|
||||
let handle = serve_http_on::<HTTP>(
|
||||
connection,
|
||||
&listen_properties,
|
||||
resource.lifetime(),
|
||||
tx,
|
||||
options,
|
||||
);
|
||||
|
||||
// Set the handle after we start the future
|
||||
|
|
|
@ -39,6 +39,8 @@ use deno_net::raw::NetworkStream;
|
|||
use deno_websocket::ws_create_server_stream;
|
||||
use flate2::write::GzEncoder;
|
||||
use flate2::Compression;
|
||||
use hyper::server::conn::http1;
|
||||
use hyper::server::conn::http2;
|
||||
use hyper_util::rt::TokioIo;
|
||||
use hyper_v014::body::Bytes;
|
||||
use hyper_v014::body::HttpBody;
|
||||
|
@ -96,6 +98,25 @@ pub use request_properties::HttpRequestProperties;
|
|||
pub use service::UpgradeUnavailableError;
|
||||
pub use websocket_upgrade::WebSocketUpgradeError;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct Options {
|
||||
/// By passing a hook function, the caller can customize various configuration
|
||||
/// options for the HTTP/2 server.
|
||||
/// See [`http2::Builder`] for what parameters can be customized.
|
||||
///
|
||||
/// If `None`, the default configuration provided by hyper will be used. Note
|
||||
/// that the default configuration is subject to change in future versions.
|
||||
pub http2_builder_hook:
|
||||
Option<fn(http2::Builder<LocalExecutor>) -> http2::Builder<LocalExecutor>>,
|
||||
/// By passing a hook function, the caller can customize various configuration
|
||||
/// options for the HTTP/1 server.
|
||||
/// See [`http1::Builder`] for what parameters can be customized.
|
||||
///
|
||||
/// If `None`, the default configuration provided by hyper will be used. Note
|
||||
/// that the default configuration is subject to change in future versions.
|
||||
pub http1_builder_hook: Option<fn(http1::Builder) -> http1::Builder>,
|
||||
}
|
||||
|
||||
deno_core::extension!(
|
||||
deno_http,
|
||||
deps = [deno_web, deno_net, deno_fetch, deno_websocket],
|
||||
|
@ -135,6 +156,12 @@ deno_core::extension!(
|
|||
http_next::op_http_cancel,
|
||||
],
|
||||
esm = ["00_serve.ts", "01_http.js", "02_websocket.ts"],
|
||||
options = {
|
||||
options: Options,
|
||||
},
|
||||
state = |state, options| {
|
||||
state.put::<Options>(options.options);
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -1117,7 +1144,7 @@ async fn op_http_upgrade_websocket(
|
|||
|
||||
// Needed so hyper can use non Send futures
|
||||
#[derive(Clone)]
|
||||
struct LocalExecutor;
|
||||
pub struct LocalExecutor;
|
||||
|
||||
impl<Fut> hyper_v014::rt::Executor<Fut> for LocalExecutor
|
||||
where
|
||||
|
|
|
@ -210,6 +210,7 @@ impl<P: RemoteDbHandlerPermissions + 'static> DatabaseHandler
|
|||
pool_idle_timeout: None,
|
||||
http1: false,
|
||||
http2: true,
|
||||
client_builder_hook: None,
|
||||
},
|
||||
)?;
|
||||
let fetch_client = FetchClient(client);
|
||||
|
|
|
@ -30,6 +30,7 @@ import {
|
|||
import { Buffer } from "node:buffer";
|
||||
import { KeyFormat, KeyType } from "ext:deno_node/internal/crypto/types.ts";
|
||||
import process from "node:process";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
import {
|
||||
op_node_generate_dh_group_key,
|
||||
|
@ -570,7 +571,15 @@ export function generateKeyPair(
|
|||
privateKey: any,
|
||||
) => void,
|
||||
) {
|
||||
createJob(kAsync, type, options).then((pair) => {
|
||||
_generateKeyPair(type, options)
|
||||
.then(
|
||||
(res) => callback(null, res.publicKey, res.privateKey),
|
||||
(err) => callback(err, null, null),
|
||||
);
|
||||
}
|
||||
|
||||
function _generateKeyPair(type: string, options: unknown) {
|
||||
return createJob(kAsync, type, options).then((pair) => {
|
||||
const privateKeyHandle = op_node_get_private_key_from_pair(pair);
|
||||
const publicKeyHandle = op_node_get_public_key_from_pair(pair);
|
||||
|
||||
|
@ -589,12 +598,15 @@ export function generateKeyPair(
|
|||
}
|
||||
}
|
||||
|
||||
callback(null, publicKey, privateKey);
|
||||
}).catch((err) => {
|
||||
callback(err, null, null);
|
||||
return { publicKey, privateKey };
|
||||
});
|
||||
}
|
||||
|
||||
Object.defineProperty(generateKeyPair, promisify.custom, {
|
||||
enumerable: false,
|
||||
value: _generateKeyPair,
|
||||
});
|
||||
|
||||
export interface KeyPairKeyObjectResult {
|
||||
publicKey: KeyObject;
|
||||
privateKey: KeyObject;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -29,7 +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.js";
|
||||
import * as telemetry from "ext:runtime/telemetry.ts";
|
||||
|
||||
const denoNs = {
|
||||
Process: process.Process,
|
||||
|
@ -185,8 +185,7 @@ denoNsUnstableById[unstableIds.webgpu] = {
|
|||
// denoNsUnstableById[unstableIds.workerOptions] = { __proto__: null }
|
||||
|
||||
denoNsUnstableById[unstableIds.otel] = {
|
||||
tracing: telemetry.tracing,
|
||||
metrics: telemetry.metrics,
|
||||
telemetry: telemetry.telemetry,
|
||||
};
|
||||
|
||||
export { denoNs, denoNsUnstableById, unstableIds };
|
||||
|
|
|
@ -86,7 +86,7 @@ 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.js";
|
||||
import { bootstrap as bootstrapOtel } from "ext:runtime/telemetry.ts";
|
||||
|
||||
// deno-lint-ignore prefer-primordials
|
||||
if (Symbol.metadata) {
|
||||
|
|
|
@ -1,409 +0,0 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { core, primordials } from "ext:core/mod.js";
|
||||
import {
|
||||
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_start,
|
||||
} from "ext:core/ops";
|
||||
import { Console } from "ext:deno_console/01_console.js";
|
||||
import { performance } from "ext:deno_web/15_performance.js";
|
||||
|
||||
const {
|
||||
SymbolDispose,
|
||||
MathRandom,
|
||||
Array,
|
||||
ObjectEntries,
|
||||
SafeMap,
|
||||
ReflectApply,
|
||||
SymbolFor,
|
||||
Error,
|
||||
NumberPrototypeToString,
|
||||
StringPrototypePadStart,
|
||||
} = primordials;
|
||||
const { AsyncVariable, setAsyncContext } = core;
|
||||
|
||||
const CURRENT = new AsyncVariable();
|
||||
let TRACING_ENABLED = false;
|
||||
let DETERMINISTIC = false;
|
||||
|
||||
const SPAN_ID_BYTES = 8;
|
||||
const TRACE_ID_BYTES = 16;
|
||||
|
||||
const TRACE_FLAG_SAMPLED = 1 << 0;
|
||||
|
||||
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;
|
||||
})();
|
||||
|
||||
let counter = 1;
|
||||
|
||||
const INVALID_SPAN_ID = "0000000000000000";
|
||||
const INVALID_TRACE_ID = "00000000000000000000000000000000";
|
||||
|
||||
function generateId(bytes) {
|
||||
if (DETERMINISTIC) {
|
||||
return StringPrototypePadStart(
|
||||
NumberPrototypeToString(counter++, 16),
|
||||
bytes * 2,
|
||||
"0",
|
||||
);
|
||||
}
|
||||
let out = "";
|
||||
for (let i = 0; i < bytes / 4; i += 1) {
|
||||
const r32 = (MathRandom() * 2 ** 32) >>> 0;
|
||||
out += hexSliceLookupTable[(r32 >> 24) & 0xff];
|
||||
out += hexSliceLookupTable[(r32 >> 16) & 0xff];
|
||||
out += hexSliceLookupTable[(r32 >> 8) & 0xff];
|
||||
out += hexSliceLookupTable[r32 & 0xff];
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function submit(span) {
|
||||
if (!(span.traceFlags & TRACE_FLAG_SAMPLED)) return;
|
||||
|
||||
op_otel_span_start(
|
||||
span.traceId,
|
||||
span.spanId,
|
||||
span.parentSpanId ?? "",
|
||||
span.kind,
|
||||
span.name,
|
||||
span.startTime,
|
||||
span.endTime,
|
||||
);
|
||||
|
||||
if (span.status !== null && span.status.code !== 0) {
|
||||
op_otel_span_continue(span.code, span.message ?? "");
|
||||
}
|
||||
|
||||
const attributes = ObjectEntries(span.attributes);
|
||||
let i = 0;
|
||||
while (i < attributes.length) {
|
||||
if (i + 2 < attributes.length) {
|
||||
op_otel_span_attribute3(
|
||||
attributes.length,
|
||||
attributes[i][0],
|
||||
attributes[i][1],
|
||||
attributes[i + 1][0],
|
||||
attributes[i + 1][1],
|
||||
attributes[i + 2][0],
|
||||
attributes[i + 2][1],
|
||||
);
|
||||
i += 3;
|
||||
} else if (i + 1 < attributes.length) {
|
||||
op_otel_span_attribute2(
|
||||
attributes.length,
|
||||
attributes[i][0],
|
||||
attributes[i][1],
|
||||
attributes[i + 1][0],
|
||||
attributes[i + 1][1],
|
||||
);
|
||||
i += 2;
|
||||
} else {
|
||||
op_otel_span_attribute(
|
||||
attributes.length,
|
||||
attributes[i][0],
|
||||
attributes[i][1],
|
||||
);
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
|
||||
op_otel_span_flush();
|
||||
}
|
||||
|
||||
const now = () => (performance.timeOrigin + performance.now()) / 1000;
|
||||
|
||||
const NO_ASYNC_CONTEXT = {};
|
||||
|
||||
class Span {
|
||||
traceId;
|
||||
spanId;
|
||||
parentSpanId;
|
||||
kind;
|
||||
name;
|
||||
startTime;
|
||||
endTime;
|
||||
status = null;
|
||||
attributes = { __proto__: null };
|
||||
traceFlags = TRACE_FLAG_SAMPLED;
|
||||
|
||||
enabled = TRACING_ENABLED;
|
||||
#asyncContext = NO_ASYNC_CONTEXT;
|
||||
|
||||
constructor(name, kind = "internal") {
|
||||
if (!this.enabled) {
|
||||
this.traceId = INVALID_TRACE_ID;
|
||||
this.spanId = INVALID_SPAN_ID;
|
||||
this.parentSpanId = INVALID_SPAN_ID;
|
||||
return;
|
||||
}
|
||||
|
||||
this.startTime = now();
|
||||
|
||||
this.spanId = generateId(SPAN_ID_BYTES);
|
||||
|
||||
let traceId;
|
||||
let parentSpanId;
|
||||
const parent = Span.current();
|
||||
if (parent) {
|
||||
if (parent.spanId !== undefined) {
|
||||
parentSpanId = parent.spanId;
|
||||
traceId = parent.traceId;
|
||||
} else {
|
||||
const context = parent.spanContext();
|
||||
parentSpanId = context.spanId;
|
||||
traceId = context.traceId;
|
||||
}
|
||||
}
|
||||
if (
|
||||
traceId && traceId !== INVALID_TRACE_ID && parentSpanId &&
|
||||
parentSpanId !== INVALID_SPAN_ID
|
||||
) {
|
||||
this.traceId = traceId;
|
||||
this.parentSpanId = parentSpanId;
|
||||
} else {
|
||||
this.traceId = generateId(TRACE_ID_BYTES);
|
||||
this.parentSpanId = INVALID_SPAN_ID;
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
|
||||
switch (kind) {
|
||||
case "internal":
|
||||
this.kind = 0;
|
||||
break;
|
||||
case "server":
|
||||
this.kind = 1;
|
||||
break;
|
||||
case "client":
|
||||
this.kind = 2;
|
||||
break;
|
||||
case "producer":
|
||||
this.kind = 3;
|
||||
break;
|
||||
case "consumer":
|
||||
this.kind = 4;
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Invalid span kind: ${kind}`);
|
||||
}
|
||||
|
||||
this.enter();
|
||||
}
|
||||
|
||||
// helper function to match otel js api
|
||||
spanContext() {
|
||||
return {
|
||||
traceId: this.traceId,
|
||||
spanId: this.spanId,
|
||||
traceFlags: this.traceFlags,
|
||||
};
|
||||
}
|
||||
|
||||
setAttribute(name, value) {
|
||||
if (!this.enabled) return;
|
||||
this.attributes[name] = value;
|
||||
}
|
||||
|
||||
enter() {
|
||||
if (!this.enabled) return;
|
||||
const context = (CURRENT.get() || ROOT_CONTEXT).setValue(SPAN_KEY, this);
|
||||
this.#asyncContext = CURRENT.enter(context);
|
||||
}
|
||||
|
||||
exit() {
|
||||
if (!this.enabled || this.#asyncContext === NO_ASYNC_CONTEXT) return;
|
||||
setAsyncContext(this.#asyncContext);
|
||||
this.#asyncContext = NO_ASYNC_CONTEXT;
|
||||
}
|
||||
|
||||
end() {
|
||||
if (!this.enabled || this.endTime !== undefined) return;
|
||||
this.exit();
|
||||
this.endTime = now();
|
||||
submit(this);
|
||||
}
|
||||
|
||||
[SymbolDispose]() {
|
||||
this.end();
|
||||
}
|
||||
|
||||
static current() {
|
||||
return CURRENT.get()?.getValue(SPAN_KEY);
|
||||
}
|
||||
}
|
||||
|
||||
function hrToSecs(hr) {
|
||||
return ((hr[0] * 1e3 + hr[1] / 1e6) / 1000);
|
||||
}
|
||||
|
||||
// Exporter compatible with opentelemetry js library
|
||||
class SpanExporter {
|
||||
export(spans, resultCallback) {
|
||||
try {
|
||||
for (let i = 0; i < spans.length; i += 1) {
|
||||
const span = spans[i];
|
||||
const context = span.spanContext();
|
||||
submit({
|
||||
spanId: context.spanId,
|
||||
traceId: context.traceId,
|
||||
traceFlags: context.traceFlags,
|
||||
name: span.name,
|
||||
kind: span.kind,
|
||||
parentSpanId: span.parentSpanId,
|
||||
startTime: hrToSecs(span.startTime),
|
||||
endTime: hrToSecs(span.endTime),
|
||||
status: span.status,
|
||||
attributes: span.attributes,
|
||||
});
|
||||
}
|
||||
resultCallback({ code: 0 });
|
||||
} catch (error) {
|
||||
resultCallback({ code: 1, error });
|
||||
}
|
||||
}
|
||||
|
||||
async shutdown() {}
|
||||
|
||||
async forceFlush() {}
|
||||
}
|
||||
|
||||
// SPAN_KEY matches symbol in otel-js library
|
||||
const SPAN_KEY = SymbolFor("OpenTelemetry Context Key SPAN");
|
||||
|
||||
// Context tracker compatible with otel-js api
|
||||
class Context {
|
||||
#data = new SafeMap();
|
||||
|
||||
constructor(data) {
|
||||
this.#data = data ? new SafeMap(data) : new SafeMap();
|
||||
}
|
||||
|
||||
getValue(key) {
|
||||
return this.#data.get(key);
|
||||
}
|
||||
|
||||
setValue(key, value) {
|
||||
const c = new Context(this.#data);
|
||||
c.#data.set(key, value);
|
||||
return c;
|
||||
}
|
||||
|
||||
deleteValue(key) {
|
||||
const c = new Context(this.#data);
|
||||
c.#data.delete(key);
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
const ROOT_CONTEXT = new Context();
|
||||
|
||||
// Context manager for opentelemetry js library
|
||||
class ContextManager {
|
||||
active() {
|
||||
return CURRENT.get() ?? ROOT_CONTEXT;
|
||||
}
|
||||
|
||||
with(context, fn, thisArg, ...args) {
|
||||
const ctx = CURRENT.enter(context);
|
||||
try {
|
||||
return ReflectApply(fn, thisArg, args);
|
||||
} finally {
|
||||
setAsyncContext(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
bind(context, f) {
|
||||
return (...args) => {
|
||||
const ctx = CURRENT.enter(context);
|
||||
try {
|
||||
return ReflectApply(f, thisArg, args);
|
||||
} finally {
|
||||
setAsyncContext(ctx);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
enable() {
|
||||
return this;
|
||||
}
|
||||
|
||||
disable() {
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
function otelLog(message, level) {
|
||||
let traceId = "";
|
||||
let spanId = "";
|
||||
let traceFlags = 0;
|
||||
const span = Span.current();
|
||||
if (span) {
|
||||
if (span.spanId !== undefined) {
|
||||
spanId = span.spanId;
|
||||
traceId = span.traceId;
|
||||
traceFlags = span.traceFlags;
|
||||
} else {
|
||||
const context = span.spanContext();
|
||||
spanId = context.spanId;
|
||||
traceId = context.traceId;
|
||||
traceFlags = context.traceFlags;
|
||||
}
|
||||
}
|
||||
return op_otel_log(message, level, traceId, spanId, traceFlags);
|
||||
}
|
||||
|
||||
const otelConsoleConfig = {
|
||||
ignore: 0,
|
||||
capture: 1,
|
||||
replace: 2,
|
||||
};
|
||||
|
||||
export function bootstrap(config) {
|
||||
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 tracing = {
|
||||
get enabled() {
|
||||
return TRACING_ENABLED;
|
||||
},
|
||||
Span,
|
||||
SpanExporter,
|
||||
ContextManager,
|
||||
};
|
||||
|
||||
// TODO(devsnek): implement metrics
|
||||
export const metrics = {};
|
720
runtime/js/telemetry.ts
Normal file
720
runtime/js/telemetry.ts
Normal file
|
@ -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 };
|
|
@ -1,8 +1,8 @@
|
|||
// 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::anyhow::{self};
|
||||
use deno_core::futures::channel::mpsc;
|
||||
use deno_core::futures::channel::mpsc::UnboundedSender;
|
||||
use deno_core::futures::future::BoxFuture;
|
||||
|
@ -23,7 +23,6 @@ use opentelemetry::trace::SpanKind;
|
|||
use opentelemetry::trace::Status as SpanStatus;
|
||||
use opentelemetry::trace::TraceFlags;
|
||||
use opentelemetry::trace::TraceId;
|
||||
use opentelemetry::InstrumentationScope;
|
||||
use opentelemetry::Key;
|
||||
use opentelemetry::KeyValue;
|
||||
use opentelemetry::StringValue;
|
||||
|
@ -63,11 +62,15 @@ 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,
|
||||
],
|
||||
);
|
||||
|
@ -303,6 +306,10 @@ mod hyper_client {
|
|||
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.
|
||||
|
@ -390,6 +397,14 @@ pub fn init(config: OtelConfig) -> anyhow::Result<()> {
|
|||
.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(())
|
||||
}
|
||||
|
||||
|
@ -458,16 +473,160 @@ pub fn handle_log(record: &log::Record) {
|
|||
|
||||
log_processor.emit(
|
||||
&mut log_record,
|
||||
&InstrumentationScope::builder("deno").build(),
|
||||
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,
|
||||
#[string] trace_id: &str,
|
||||
#[string] span_id: &str,
|
||||
trace_id: v8::Local<'_, v8::Value>,
|
||||
span_id: v8::Local<'_, v8::Value>,
|
||||
#[smi] trace_flags: u8,
|
||||
) {
|
||||
let Some((_, log_processor)) = OTEL_PROCESSORS.get() else {
|
||||
|
@ -483,15 +642,16 @@ fn op_otel_log(
|
|||
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 let (Ok(trace_id), Ok(span_id)) =
|
||||
(TraceId::from_hex(trace_id), SpanId::from_hex(span_id))
|
||||
{
|
||||
if trace_id != TraceId::INVALID && span_id != SpanId::INVALID {
|
||||
log_record.set_trace_context(
|
||||
trace_id,
|
||||
span_id,
|
||||
|
@ -501,7 +661,7 @@ fn op_otel_log(
|
|||
|
||||
log_processor.emit(
|
||||
&mut log_record,
|
||||
&InstrumentationScope::builder("deno").build(),
|
||||
BUILT_IN_INSTRUMENTATION_SCOPE.get().unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -527,40 +687,23 @@ fn op_otel_span_start<'s>(
|
|||
span_processor.on_end(temporary_span.0);
|
||||
};
|
||||
|
||||
let trace_id = {
|
||||
let x = v8::ValueView::new(scope, trace_id.try_cast()?);
|
||||
match x.data() {
|
||||
v8::ValueViewData::OneByte(bytes) => {
|
||||
TraceId::from_hex(&String::from_utf8_lossy(bytes))?
|
||||
}
|
||||
_ => return Err(anyhow!("invalid trace_id")),
|
||||
}
|
||||
let Some(InstrumentationScope(instrumentation_scope)) =
|
||||
state.try_borrow::<InstrumentationScope>()
|
||||
else {
|
||||
return Err(anyhow!("instrumentation scope not available"));
|
||||
};
|
||||
|
||||
let span_id = {
|
||||
let x = v8::ValueView::new(scope, span_id.try_cast()?);
|
||||
match x.data() {
|
||||
v8::ValueViewData::OneByte(bytes) => {
|
||||
SpanId::from_hex(&String::from_utf8_lossy(bytes))?
|
||||
let trace_id = parse_trace_id(scope, trace_id);
|
||||
if trace_id == TraceId::INVALID {
|
||||
return Err(anyhow!("invalid trace_id"));
|
||||
}
|
||||
_ => return Err(anyhow!("invalid span_id")),
|
||||
}
|
||||
};
|
||||
|
||||
let parent_span_id = {
|
||||
let x = v8::ValueView::new(scope, parent_span_id.try_cast()?);
|
||||
match x.data() {
|
||||
v8::ValueViewData::OneByte(bytes) => {
|
||||
let s = String::from_utf8_lossy(bytes);
|
||||
if s.is_empty() {
|
||||
SpanId::INVALID
|
||||
} else {
|
||||
SpanId::from_hex(&s)?
|
||||
let span_id = parse_span_id(scope, span_id);
|
||||
if span_id == SpanId::INVALID {
|
||||
return Err(anyhow!("invalid span_id"));
|
||||
}
|
||||
}
|
||||
_ => return Err(anyhow!("invalid parent_span_id")),
|
||||
}
|
||||
};
|
||||
|
||||
let parent_span_id = parse_span_id(scope, parent_span_id);
|
||||
|
||||
let name = {
|
||||
let x = v8::ValueView::new(scope, name.try_cast()?);
|
||||
|
@ -601,7 +744,7 @@ fn op_otel_span_start<'s>(
|
|||
events: Default::default(),
|
||||
links: Default::default(),
|
||||
status: SpanStatus::Unset,
|
||||
instrumentation_scope: InstrumentationScope::builder("deno").build(),
|
||||
instrumentation_scope: instrumentation_scope.clone(),
|
||||
});
|
||||
state.put(temporary_span);
|
||||
|
||||
|
@ -626,52 +769,6 @@ fn op_otel_span_continue(
|
|||
}
|
||||
}
|
||||
|
||||
macro_rules! attr {
|
||||
($scope:ident, $temporary_span:ident, $name:ident, $value:ident) => {
|
||||
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) {
|
||||
$temporary_span
|
||||
.0
|
||||
.attributes
|
||||
.push(KeyValue::new(name, value));
|
||||
} else {
|
||||
$temporary_span.0.dropped_attributes_count += 1;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[op2(fast)]
|
||||
fn op_otel_span_attribute<'s>(
|
||||
scope: &mut v8::HandleScope<'s>,
|
||||
|
@ -684,7 +781,7 @@ fn op_otel_span_attribute<'s>(
|
|||
temporary_span.0.attributes.reserve_exact(
|
||||
(capacity as usize) - temporary_span.0.attributes.capacity(),
|
||||
);
|
||||
attr!(scope, temporary_span, key, value);
|
||||
attr!(scope, temporary_span.0.attributes => temporary_span.0.dropped_attributes_count, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -702,8 +799,8 @@ fn op_otel_span_attribute2<'s>(
|
|||
temporary_span.0.attributes.reserve_exact(
|
||||
(capacity as usize) - temporary_span.0.attributes.capacity(),
|
||||
);
|
||||
attr!(scope, temporary_span, key1, value1);
|
||||
attr!(scope, temporary_span, key2, value2);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -724,9 +821,23 @@ fn op_otel_span_attribute3<'s>(
|
|||
temporary_span.0.attributes.reserve_exact(
|
||||
(capacity as usize) - temporary_span.0.attributes.capacity(),
|
||||
);
|
||||
attr!(scope, temporary_span, key1, value1);
|
||||
attr!(scope, temporary_span, key2, value2);
|
||||
attr!(scope, temporary_span, key3, value3);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ extension!(runtime,
|
|||
"40_signals.js",
|
||||
"40_tty.js",
|
||||
"41_prompt.js",
|
||||
"telemetry.js",
|
||||
"telemetry.ts",
|
||||
"90_deno_ns.js",
|
||||
"98_global_scope_shared.js",
|
||||
"98_global_scope_window.js",
|
||||
|
|
|
@ -300,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()),
|
||||
|
|
|
@ -497,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(),
|
||||
|
|
|
@ -407,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(),
|
||||
|
|
|
@ -11680,7 +11680,7 @@ fn lsp_format_with_config() {
|
|||
},
|
||||
"options": {
|
||||
"tabSize": 2,
|
||||
"insertSpaces": false
|
||||
"insertSpaces": true,
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
"x.txt"
|
||||
],
|
||||
"internet": [
|
||||
"test-dns-any.js",
|
||||
"test-dns-ipv4.js",
|
||||
"test-dns-ipv6.js",
|
||||
"test-dns.js"
|
||||
// TODO(bartlomieju): temporarily disabled as these tests were hitting nodejs.org
|
||||
// server. We should have a setup that hits a local server instead.
|
||||
// "test-dns-any.js",
|
||||
// "test-dns-ipv4.js",
|
||||
// "test-dns-ipv6.js",
|
||||
// "test-dns.js"
|
||||
],
|
||||
"parallel": [
|
||||
"test-assert.js",
|
||||
|
@ -149,15 +151,15 @@
|
|||
],
|
||||
"fixtures/keys": ["agent1-cert.pem", "agent1-key.pem", "ca1-cert.pem"],
|
||||
"internet": [
|
||||
"test-dns-any.js",
|
||||
// "test-dns-any.js",
|
||||
"test-dns-idna2008.js",
|
||||
"test-dns-ipv4.js",
|
||||
"test-dns-ipv6.js",
|
||||
// "test-dns-ipv4.js",
|
||||
// "test-dns-ipv6.js",
|
||||
"test-dns-lookup.js",
|
||||
"test-dns-promises-resolve.js",
|
||||
"test-dns-regress-6244.js",
|
||||
"test-dns-setserver-in-callback-of-resolve4.js",
|
||||
"test-dns.js",
|
||||
// "test-dns.js",
|
||||
"test-http-https-default-ports.js"
|
||||
],
|
||||
"parallel": [
|
||||
|
|
|
@ -105,9 +105,13 @@ NOTE: This file should not be manually edited. Please edit `tests/node_compat/co
|
|||
- [internet/test-dgram-multicast-set-interface-lo.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dgram-multicast-set-interface-lo.js)
|
||||
- [internet/test-dgram-multicast-ssm-multi-process.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dgram-multicast-ssm-multi-process.js)
|
||||
- [internet/test-dgram-multicast-ssmv6-multi-process.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dgram-multicast-ssmv6-multi-process.js)
|
||||
- [internet/test-dns-any.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dns-any.js)
|
||||
- [internet/test-dns-cares-domains.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dns-cares-domains.js)
|
||||
- [internet/test-dns-getDefaultResultOrder.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dns-getDefaultResultOrder.js)
|
||||
- [internet/test-dns-ipv4.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dns-ipv4.js)
|
||||
- [internet/test-dns-ipv6.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dns-ipv6.js)
|
||||
- [internet/test-dns-txt-sigsegv.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dns-txt-sigsegv.js)
|
||||
- [internet/test-dns.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-dns.js)
|
||||
- [internet/test-http-dns-fail.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-http-dns-fail.js)
|
||||
- [internet/test-http2-issue-32922.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-http2-issue-32922.js)
|
||||
- [internet/test-https-autoselectfamily-slow-timeout.js](https://github.com/nodejs/node/tree/v18.12.1/test/internet/test-https-autoselectfamily-slow-timeout.js)
|
||||
|
|
|
@ -1,194 +0,0 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 16.13.0
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
// TODO(cmorten): enable remaining tests once functionality is implemented.
|
||||
|
||||
'use strict';
|
||||
|
||||
const common = require('../common');
|
||||
|
||||
const assert = require('assert');
|
||||
const dns = require('dns');
|
||||
const net = require('net');
|
||||
|
||||
let running = false;
|
||||
const queue = [];
|
||||
|
||||
const dnsPromises = dns.promises;
|
||||
const isIPv4 = net.isIPv4;
|
||||
const isIPv6 = net.isIPv6;
|
||||
|
||||
dns.setServers([ '8.8.8.8', '8.8.4.4' ]);
|
||||
|
||||
function checkWrap(req) {
|
||||
assert.ok(typeof req === 'object');
|
||||
}
|
||||
|
||||
const checkers = {
|
||||
checkA(r) {
|
||||
assert.ok(isIPv4(r.address));
|
||||
// assert.strictEqual(typeof r.ttl, 'number');
|
||||
assert.strictEqual(r.type, 'A');
|
||||
},
|
||||
checkAAAA(r) {
|
||||
assert.ok(isIPv6(r.address));
|
||||
// assert.strictEqual(typeof r.ttl, 'number');
|
||||
assert.strictEqual(r.type, 'AAAA');
|
||||
},
|
||||
checkCNAME(r) {
|
||||
assert.ok(r.value);
|
||||
assert.strictEqual(typeof r.value, 'string');
|
||||
assert.strictEqual(r.type, 'CNAME');
|
||||
},
|
||||
checkMX(r) {
|
||||
assert.strictEqual(typeof r.exchange, 'string');
|
||||
assert.strictEqual(typeof r.priority, 'number');
|
||||
assert.strictEqual(r.type, 'MX');
|
||||
},
|
||||
checkNAPTR(r) {
|
||||
assert.strictEqual(typeof r.flags, 'string');
|
||||
assert.strictEqual(typeof r.service, 'string');
|
||||
assert.strictEqual(typeof r.regexp, 'string');
|
||||
assert.strictEqual(typeof r.replacement, 'string');
|
||||
assert.strictEqual(typeof r.order, 'number');
|
||||
assert.strictEqual(typeof r.preference, 'number');
|
||||
assert.strictEqual(r.type, 'NAPTR');
|
||||
},
|
||||
checkNS(r) {
|
||||
assert.strictEqual(typeof r.value, 'string');
|
||||
assert.strictEqual(r.type, 'NS');
|
||||
},
|
||||
checkPTR(r) {
|
||||
assert.strictEqual(typeof r.value, 'string');
|
||||
assert.strictEqual(r.type, 'PTR');
|
||||
},
|
||||
checkTXT(r) {
|
||||
assert.ok(Array.isArray(r.entries));
|
||||
assert.ok(r.entries.length > 0);
|
||||
assert.strictEqual(r.type, 'TXT');
|
||||
},
|
||||
checkSOA(r) {
|
||||
assert.strictEqual(typeof r.nsname, 'string');
|
||||
assert.strictEqual(typeof r.hostmaster, 'string');
|
||||
assert.strictEqual(typeof r.serial, 'number');
|
||||
assert.strictEqual(typeof r.refresh, 'number');
|
||||
assert.strictEqual(typeof r.retry, 'number');
|
||||
assert.strictEqual(typeof r.expire, 'number');
|
||||
assert.strictEqual(typeof r.minttl, 'number');
|
||||
assert.strictEqual(r.type, 'SOA');
|
||||
},
|
||||
checkSRV(r) {
|
||||
assert.strictEqual(typeof r.name, 'string');
|
||||
assert.strictEqual(typeof r.port, 'number');
|
||||
assert.strictEqual(typeof r.priority, 'number');
|
||||
assert.strictEqual(typeof r.weight, 'number');
|
||||
assert.strictEqual(r.type, 'SRV');
|
||||
}
|
||||
};
|
||||
|
||||
function TEST(f) {
|
||||
function next() {
|
||||
const f = queue.shift();
|
||||
if (f) {
|
||||
running = true;
|
||||
f(done);
|
||||
}
|
||||
}
|
||||
|
||||
function done() {
|
||||
running = false;
|
||||
process.nextTick(next);
|
||||
}
|
||||
|
||||
queue.push(f);
|
||||
|
||||
if (!running) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
function processResult(res) {
|
||||
assert.ok(Array.isArray(res));
|
||||
assert.ok(res.length > 0);
|
||||
|
||||
const types = {};
|
||||
res.forEach((obj) => {
|
||||
types[obj.type] = true;
|
||||
checkers[`check${obj.type}`](obj);
|
||||
});
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
TEST(async function test_sip2sip_for_naptr(done) {
|
||||
function validateResult(res) {
|
||||
const types = processResult(res);
|
||||
assert.ok(
|
||||
types.A && types.NS && types.NAPTR && types.SOA,
|
||||
`Missing record type, found ${Object.keys(types)}`
|
||||
);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolve('sip2sip.info', 'ANY'));
|
||||
|
||||
const req = dns.resolve(
|
||||
'sip2sip.info',
|
||||
'ANY',
|
||||
common.mustSucceed((ret) => {
|
||||
validateResult(ret);
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_google_for_cname_and_srv(done) {
|
||||
function validateResult(res) {
|
||||
const types = processResult(res);
|
||||
assert.ok(types.SRV);
|
||||
}
|
||||
|
||||
// TODO(kt3k): Temporarily use _caldav._tcp.google.com instead of
|
||||
// _jabber._tcp.google.com, which currently doesn't respond
|
||||
// validateResult(await dnsPromises.resolve('_jabber._tcp.google.com', 'ANY'));
|
||||
validateResult(await dnsPromises.resolve('_caldav._tcp.google.com', 'ANY'));
|
||||
|
||||
|
||||
// TODO(kt3k): Temporarily use _caldav._tcp.google.com instead of
|
||||
// _jabber._tcp.google.com, which currently doesn't respond
|
||||
const req = dns.resolve(
|
||||
// '_jabber._tcp.google.com',
|
||||
'_caldav._tcp.google.com',
|
||||
'ANY',
|
||||
common.mustSucceed((ret) => {
|
||||
validateResult(ret);
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TODO(bartlomieju): this test started failing on CI on Dec 28th, 2023 returning
|
||||
// ENOTFOUND. It's unclear what's going on, since `dig -x 8.8.8.8.in-addr.arpa`
|
||||
// TEST(async function test_ptr(done) {
|
||||
// function validateResult(res) {
|
||||
// const types = processResult(res);
|
||||
// assert.ok(types.PTR);
|
||||
// }
|
||||
|
||||
// validateResult(await dnsPromises.resolve('8.8.8.8.in-addr.arpa', 'ANY'));
|
||||
|
||||
// const req = dns.resolve(
|
||||
// '8.8.8.8.in-addr.arpa',
|
||||
// 'ANY',
|
||||
// common.mustSucceed((ret) => {
|
||||
// validateResult(ret);
|
||||
// done();
|
||||
// }));
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
|
@ -1,257 +0,0 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 16.13.0
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: enable remaining tests once functionality is implemented.
|
||||
|
||||
const common = require('../common');
|
||||
const { addresses } = require('../common/internet');
|
||||
const assert = require('assert');
|
||||
const dns = require('dns');
|
||||
const net = require('net');
|
||||
// const util = require('util');
|
||||
const isIPv4 = net.isIPv4;
|
||||
|
||||
const dnsPromises = dns.promises;
|
||||
let running = false;
|
||||
const queue = [];
|
||||
|
||||
function TEST(f) {
|
||||
function next() {
|
||||
const f = queue.shift();
|
||||
if (f) {
|
||||
running = true;
|
||||
console.log(f.name);
|
||||
f(done);
|
||||
}
|
||||
}
|
||||
|
||||
function done() {
|
||||
running = false;
|
||||
process.nextTick(next);
|
||||
}
|
||||
|
||||
queue.push(f);
|
||||
|
||||
if (!running) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
function checkWrap(req) {
|
||||
assert.ok(typeof req === 'object');
|
||||
}
|
||||
|
||||
TEST(async function test_resolve4(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(res.length > 0);
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
assert.ok(isIPv4(res[i]));
|
||||
}
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolve4(addresses.INET4_HOST));
|
||||
|
||||
const req = dns.resolve4(
|
||||
addresses.INET4_HOST,
|
||||
common.mustSucceed((ips) => {
|
||||
validateResult(ips);
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TEST(async function test_reverse_ipv4(done) {
|
||||
// function validateResult(res) {
|
||||
// assert.ok(res.length > 0);
|
||||
|
||||
// for (let i = 0; i < res.length; i++) {
|
||||
// assert.ok(res[i]);
|
||||
// assert.ok(typeof res[i] === 'string');
|
||||
// }
|
||||
// }
|
||||
|
||||
// validateResult(await dnsPromises.reverse(addresses.INET4_IP));
|
||||
|
||||
// const req = dns.reverse(
|
||||
// addresses.INET4_IP,
|
||||
// common.mustSucceed((domains) => {
|
||||
// validateResult(domains);
|
||||
// done();
|
||||
// }));
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_lookup_ipv4_explicit(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(net.isIPv4(res.address));
|
||||
assert.strictEqual(res.family, 4);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, 4));
|
||||
|
||||
const req = dns.lookup(
|
||||
addresses.INET4_HOST, 4,
|
||||
common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_ipv4_implicit(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(net.isIPv4(res.address));
|
||||
assert.strictEqual(res.family, 4);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET4_HOST));
|
||||
|
||||
const req = dns.lookup(
|
||||
addresses.INET4_HOST,
|
||||
common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_ipv4_explicit_object(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(net.isIPv4(res.address));
|
||||
assert.strictEqual(res.family, 4);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, { family: 4 }));
|
||||
|
||||
const req = dns.lookup(addresses.INET4_HOST, {
|
||||
family: 4
|
||||
}, common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_ipv4_hint_addrconfig(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(net.isIPv4(res.address));
|
||||
assert.strictEqual(res.family, 4);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, {
|
||||
hints: dns.ADDRCONFIG
|
||||
}));
|
||||
|
||||
const req = dns.lookup(addresses.INET4_HOST, {
|
||||
hints: dns.ADDRCONFIG
|
||||
}, common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_ip_ipv4(done) {
|
||||
function validateResult(res) {
|
||||
assert.strictEqual(res.address, '127.0.0.1');
|
||||
assert.strictEqual(res.family, 4);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup('127.0.0.1'));
|
||||
|
||||
const req = dns.lookup('127.0.0.1',
|
||||
common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_localhost_ipv4(done) {
|
||||
function validateResult(res) {
|
||||
assert.strictEqual(res.address, '127.0.0.1');
|
||||
assert.strictEqual(res.family, 4);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup('localhost', 4));
|
||||
|
||||
const req = dns.lookup('localhost', 4,
|
||||
common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_all_ipv4(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(Array.isArray(res));
|
||||
assert.ok(res.length > 0);
|
||||
|
||||
res.forEach((ip) => {
|
||||
assert.ok(isIPv4(ip.address));
|
||||
assert.strictEqual(ip.family, 4);
|
||||
});
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET4_HOST, {
|
||||
all: true,
|
||||
family: 4
|
||||
}));
|
||||
|
||||
const req = dns.lookup(
|
||||
addresses.INET4_HOST,
|
||||
{ all: true, family: 4 },
|
||||
common.mustSucceed((ips) => {
|
||||
validateResult(ips);
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TEST(async function test_lookupservice_ip_ipv4(done) {
|
||||
// function validateResult(res) {
|
||||
// assert.strictEqual(typeof res.hostname, 'string');
|
||||
// assert(res.hostname);
|
||||
// assert(['http', 'www', '80'].includes(res.service));
|
||||
// }
|
||||
|
||||
// validateResult(await dnsPromises.lookupService('127.0.0.1', 80));
|
||||
|
||||
// const req = dns.lookupService(
|
||||
// '127.0.0.1', 80,
|
||||
// common.mustSucceed((hostname, service) => {
|
||||
// validateResult({ hostname, service });
|
||||
// done();
|
||||
// })
|
||||
// );
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
// TEST(function test_lookupservice_ip_ipv4_promise(done) {
|
||||
// util.promisify(dns.lookupService)('127.0.0.1', 80)
|
||||
// .then(common.mustCall(({ hostname, service }) => {
|
||||
// assert.strictEqual(typeof hostname, 'string');
|
||||
// assert(hostname.length > 0);
|
||||
// assert(['http', 'www', '80'].includes(service));
|
||||
// done();
|
||||
// }));
|
||||
// });
|
|
@ -1,250 +0,0 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 16.13.0
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO: enable remaining tests once functionality is implemented.
|
||||
|
||||
const common = require('../common');
|
||||
const { addresses } = require('../common/internet');
|
||||
if (!common.hasIPv6)
|
||||
common.skip('this test, no IPv6 support');
|
||||
|
||||
const assert = require('assert');
|
||||
const dns = require('dns');
|
||||
const net = require('net');
|
||||
const dnsPromises = dns.promises;
|
||||
const isIPv6 = net.isIPv6;
|
||||
|
||||
let running = false;
|
||||
const queue = [];
|
||||
|
||||
function TEST(f) {
|
||||
function next() {
|
||||
const f = queue.shift();
|
||||
if (f) {
|
||||
running = true;
|
||||
console.log(f.name);
|
||||
f(done);
|
||||
}
|
||||
}
|
||||
|
||||
function done() {
|
||||
running = false;
|
||||
process.nextTick(next);
|
||||
}
|
||||
|
||||
queue.push(f);
|
||||
|
||||
if (!running) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
function checkWrap(req) {
|
||||
assert.ok(typeof req === 'object');
|
||||
}
|
||||
|
||||
TEST(async function test_resolve6(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(res.length > 0);
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
assert.ok(isIPv6(res[i]));
|
||||
}
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolve6(addresses.INET6_HOST));
|
||||
|
||||
const req = dns.resolve6(
|
||||
addresses.INET6_HOST,
|
||||
common.mustSucceed((ips) => {
|
||||
validateResult(ips);
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TEST(async function test_reverse_ipv6(done) {
|
||||
// function validateResult(res) {
|
||||
// assert.ok(res.length > 0);
|
||||
|
||||
// for (let i = 0; i < res.length; i++) {
|
||||
// assert.ok(typeof res[i] === 'string');
|
||||
// }
|
||||
// }
|
||||
|
||||
// validateResult(await dnsPromises.reverse(addresses.INET6_IP));
|
||||
|
||||
// const req = dns.reverse(
|
||||
// addresses.INET6_IP,
|
||||
// common.mustSucceed((domains) => {
|
||||
// validateResult(domains);
|
||||
// done();
|
||||
// }));
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_lookup_ipv6_explicit(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(isIPv6(res.address));
|
||||
assert.strictEqual(res.family, 6);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET6_HOST, 6));
|
||||
|
||||
const req = dns.lookup(
|
||||
addresses.INET6_HOST,
|
||||
6,
|
||||
common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// This ends up just being too problematic to test
|
||||
// TEST(function test_lookup_ipv6_implicit(done) {
|
||||
// var req = dns.lookup(addresses.INET6_HOST, function(err, ip, family) {
|
||||
// assert.ifError(err);
|
||||
// assert.ok(net.isIPv6(ip));
|
||||
// assert.strictEqual(family, 6);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_lookup_ipv6_explicit_object(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(isIPv6(res.address));
|
||||
assert.strictEqual(res.family, 6);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET6_HOST, { family: 6 }));
|
||||
|
||||
const req = dns.lookup(addresses.INET6_HOST, {
|
||||
family: 6
|
||||
}, common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(function test_lookup_ipv6_hint(done) {
|
||||
const req = dns.lookup(addresses.INET6_HOST, {
|
||||
family: 6,
|
||||
hints: dns.V4MAPPED
|
||||
}, common.mustCall((err, ip, family) => {
|
||||
if (err) {
|
||||
// FreeBSD does not support V4MAPPED
|
||||
if (common.isFreeBSD) {
|
||||
assert(err instanceof Error);
|
||||
assert.strictEqual(err.code, 'EAI_BADFLAGS');
|
||||
assert.strictEqual(err.hostname, addresses.INET_HOST);
|
||||
assert.match(err.message, /getaddrinfo EAI_BADFLAGS/);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
assert.ifError(err);
|
||||
}
|
||||
|
||||
assert.ok(isIPv6(ip));
|
||||
assert.strictEqual(family, 6);
|
||||
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_ip_ipv6(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(isIPv6(res.address));
|
||||
assert.strictEqual(res.family, 6);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup('::1'));
|
||||
|
||||
const req = dns.lookup(
|
||||
'::1',
|
||||
common.mustSucceed((ip, family) => {
|
||||
validateResult({ address: ip, family });
|
||||
done();
|
||||
}));
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
TEST(async function test_lookup_all_ipv6(done) {
|
||||
function validateResult(res) {
|
||||
assert.ok(Array.isArray(res));
|
||||
assert.ok(res.length > 0);
|
||||
|
||||
res.forEach((ip) => {
|
||||
assert.ok(isIPv6(ip.address),
|
||||
`Invalid IPv6: ${ip.address.toString()}`);
|
||||
assert.strictEqual(ip.family, 6);
|
||||
});
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET6_HOST, {
|
||||
all: true,
|
||||
family: 6
|
||||
}));
|
||||
|
||||
const req = dns.lookup(
|
||||
addresses.INET6_HOST,
|
||||
{ all: true, family: 6 },
|
||||
common.mustSucceed((ips) => {
|
||||
validateResult(ips);
|
||||
done();
|
||||
})
|
||||
);
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TEST(function test_lookupservice_ip_ipv6(done) {
|
||||
// const req = dns.lookupService(
|
||||
// '::1', 80,
|
||||
// common.mustCall((err, host, service) => {
|
||||
// if (err) {
|
||||
// // Not skipping the test, rather checking an alternative result,
|
||||
// // i.e. that ::1 may not be configured (e.g. in /etc/hosts)
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// return done();
|
||||
// }
|
||||
// assert.strictEqual(typeof host, 'string');
|
||||
// assert(host);
|
||||
// assert(['http', 'www', '80'].includes(service));
|
||||
// done();
|
||||
// })
|
||||
// );
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
// Disabled because it appears to be not working on Linux.
|
||||
// TEST(function test_lookup_localhost_ipv6(done) {
|
||||
// var req = dns.lookup('localhost', 6, function(err, ip, family) {
|
||||
// assert.ifError(err);
|
||||
// assert.ok(net.isIPv6(ip));
|
||||
// assert.strictEqual(family, 6);
|
||||
//
|
||||
// done();
|
||||
// });
|
||||
//
|
||||
// checkWrap(req);
|
||||
// });
|
|
@ -1,766 +0,0 @@
|
|||
// deno-fmt-ignore-file
|
||||
// deno-lint-ignore-file
|
||||
|
||||
// Copyright Joyent and Node contributors. All rights reserved. MIT license.
|
||||
// Taken from Node 16.13.0
|
||||
// This file is automatically generated by "node/_tools/setup.ts". Do not modify this file manually
|
||||
|
||||
// Flags: --expose-internals
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the
|
||||
// "Software"), to deal in the Software without restriction, including
|
||||
// without limitation the rights to use, copy, modify, merge, publish,
|
||||
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
||||
// persons to whom the Software is furnished to do so, subject to the
|
||||
// following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included
|
||||
// in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
||||
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
||||
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
'use strict';
|
||||
|
||||
// TODO(cmorten): enable remaining tests once functionality is implemented.
|
||||
|
||||
const common = require('../common');
|
||||
const { addresses } = require('../common/internet');
|
||||
const { internalBinding } = require('internal/test/binding');
|
||||
// const { getSystemErrorName } = require('util');
|
||||
const assert = require('assert');
|
||||
const dns = require('dns');
|
||||
const net = require('net');
|
||||
const isIPv4 = net.isIPv4;
|
||||
const isIPv6 = net.isIPv6;
|
||||
const util = require('util');
|
||||
const dnsPromises = dns.promises;
|
||||
|
||||
let expected = 0;
|
||||
let completed = 0;
|
||||
let running = false;
|
||||
const queue = [];
|
||||
|
||||
|
||||
function TEST(f) {
|
||||
function next() {
|
||||
const f = queue.shift();
|
||||
if (f) {
|
||||
running = true;
|
||||
console.log(f.name);
|
||||
f(done);
|
||||
}
|
||||
}
|
||||
|
||||
function done() {
|
||||
running = false;
|
||||
completed++;
|
||||
process.nextTick(next);
|
||||
}
|
||||
|
||||
expected++;
|
||||
queue.push(f);
|
||||
|
||||
if (!running) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function checkWrap(req) {
|
||||
assert.strictEqual(typeof req, 'object');
|
||||
}
|
||||
|
||||
|
||||
// TEST(function test_reverse_bogus(done) {
|
||||
// dnsPromises.reverse('bogus ip')
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'EINVAL');
|
||||
// assert.strictEqual(getSystemErrorName(err.errno), 'EINVAL');
|
||||
// }));
|
||||
|
||||
// assert.throws(() => {
|
||||
// dns.reverse('bogus ip', common.mustNotCall());
|
||||
// }, /^Error: getHostByAddr EINVAL bogus ip$/);
|
||||
// done();
|
||||
// });
|
||||
|
||||
// TEST(async function test_resolve4_ttl(done) {
|
||||
// function validateResult(result) {
|
||||
// assert.ok(result.length > 0);
|
||||
|
||||
// for (const item of result) {
|
||||
// assert.strictEqual(typeof item, 'object');
|
||||
// assert.strictEqual(typeof item.ttl, 'number');
|
||||
// assert.strictEqual(typeof item.address, 'string');
|
||||
// assert.ok(item.ttl >= 0);
|
||||
// assert.ok(isIPv4(item.address));
|
||||
// }
|
||||
// }
|
||||
|
||||
// validateResult(await dnsPromises.resolve4(addresses.INET4_HOST, {
|
||||
// ttl: true
|
||||
// }));
|
||||
|
||||
// const req = dns.resolve4(addresses.INET4_HOST, {
|
||||
// ttl: true
|
||||
// }, function(err, result) {
|
||||
// assert.ifError(err);
|
||||
// validateResult(result);
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
// TEST(async function test_resolve6_ttl(done) {
|
||||
// function validateResult(result) {
|
||||
// assert.ok(result.length > 0);
|
||||
|
||||
// for (const item of result) {
|
||||
// assert.strictEqual(typeof item, 'object');
|
||||
// assert.strictEqual(typeof item.ttl, 'number');
|
||||
// assert.strictEqual(typeof item.address, 'string');
|
||||
// assert.ok(item.ttl >= 0);
|
||||
// assert.ok(isIPv6(item.address));
|
||||
// }
|
||||
// }
|
||||
|
||||
// validateResult(await dnsPromises.resolve6(addresses.INET6_HOST, {
|
||||
// ttl: true
|
||||
// }));
|
||||
|
||||
// const req = dns.resolve6(addresses.INET6_HOST, {
|
||||
// ttl: true
|
||||
// }, function(err, result) {
|
||||
// assert.ifError(err);
|
||||
// validateResult(result);
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_resolveMx(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(result.length > 0);
|
||||
|
||||
for (const item of result) {
|
||||
assert.strictEqual(typeof item, 'object');
|
||||
assert.ok(item.exchange);
|
||||
assert.strictEqual(typeof item.exchange, 'string');
|
||||
assert.strictEqual(typeof item.priority, 'number');
|
||||
}
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolveMx(addresses.MX_HOST));
|
||||
|
||||
const req = dns.resolveMx(addresses.MX_HOST, function(err, result) {
|
||||
assert.ifError(err);
|
||||
validateResult(result);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolveMx_failure(done) {
|
||||
// dnsPromises.resolveMx(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolveMx(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_resolveNs(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(result.length > 0);
|
||||
|
||||
for (const item of result) {
|
||||
assert.ok(item);
|
||||
assert.strictEqual(typeof item, 'string');
|
||||
}
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolveNs(addresses.NS_HOST));
|
||||
|
||||
const req = dns.resolveNs(addresses.NS_HOST, function(err, names) {
|
||||
assert.ifError(err);
|
||||
validateResult(names);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolveNs_failure(done) {
|
||||
// dnsPromises.resolveNs(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolveNs(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_resolveSrv(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(result.length > 0);
|
||||
|
||||
for (const item of result) {
|
||||
assert.strictEqual(typeof item, 'object');
|
||||
assert.ok(item.name);
|
||||
assert.strictEqual(typeof item.name, 'string');
|
||||
assert.strictEqual(typeof item.port, 'number');
|
||||
assert.strictEqual(typeof item.priority, 'number');
|
||||
assert.strictEqual(typeof item.weight, 'number');
|
||||
}
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolveSrv(addresses.SRV_HOST));
|
||||
|
||||
const req = dns.resolveSrv(addresses.SRV_HOST, function(err, result) {
|
||||
assert.ifError(err);
|
||||
validateResult(result);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolveSrv_failure(done) {
|
||||
// dnsPromises.resolveSrv(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolveSrv(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
// TODO(bartlomieju): this test started failing on CI on Dec 28th, 2023 returning
|
||||
// ENOTFOUND. It's unclear what's going on, since `dig -x 8.8.8.8.in-addr.arpa`
|
||||
// returns correct PTR record.
|
||||
// TEST(async function test_resolvePtr(done) {
|
||||
// function validateResult(result) {
|
||||
// assert.ok(result.length > 0);
|
||||
|
||||
// for (const item of result) {
|
||||
// assert.ok(item);
|
||||
// assert.strictEqual(typeof item, 'string');
|
||||
// }
|
||||
// }
|
||||
|
||||
// validateResult(await dnsPromises.resolvePtr(addresses.PTR_HOST));
|
||||
|
||||
// const req = dns.resolvePtr(addresses.PTR_HOST, function(err, result) {
|
||||
// assert.ifError(err);
|
||||
// validateResult(result);
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolvePtr_failure(done) {
|
||||
// dnsPromises.resolvePtr(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolvePtr(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_resolveNaptr(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(result.length > 0);
|
||||
|
||||
for (const item of result) {
|
||||
assert.strictEqual(typeof item, 'object');
|
||||
assert.strictEqual(typeof item.flags, 'string');
|
||||
assert.strictEqual(typeof item.service, 'string');
|
||||
assert.strictEqual(typeof item.regexp, 'string');
|
||||
assert.strictEqual(typeof item.replacement, 'string');
|
||||
assert.strictEqual(typeof item.order, 'number');
|
||||
assert.strictEqual(typeof item.preference, 'number');
|
||||
}
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolveNaptr(addresses.NAPTR_HOST));
|
||||
|
||||
const req = dns.resolveNaptr(addresses.NAPTR_HOST, function(err, result) {
|
||||
assert.ifError(err);
|
||||
validateResult(result);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolveNaptr_failure(done) {
|
||||
// dnsPromises.resolveNaptr(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolveNaptr(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_resolveSoa(done) {
|
||||
function validateResult(result) {
|
||||
assert.strictEqual(typeof result, 'object');
|
||||
assert.strictEqual(typeof result.nsname, 'string');
|
||||
assert.ok(result.nsname.length > 0);
|
||||
assert.strictEqual(typeof result.hostmaster, 'string');
|
||||
assert.ok(result.hostmaster.length > 0);
|
||||
assert.strictEqual(typeof result.serial, 'number');
|
||||
assert.ok((result.serial > 0) && (result.serial < 4294967295));
|
||||
assert.strictEqual(typeof result.refresh, 'number');
|
||||
assert.ok((result.refresh > 0) && (result.refresh < 2147483647));
|
||||
assert.strictEqual(typeof result.retry, 'number');
|
||||
assert.ok((result.retry > 0) && (result.retry < 2147483647));
|
||||
assert.strictEqual(typeof result.expire, 'number');
|
||||
assert.ok((result.expire > 0) && (result.expire < 2147483647));
|
||||
assert.strictEqual(typeof result.minttl, 'number');
|
||||
assert.ok((result.minttl >= 0) && (result.minttl < 2147483647));
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolveSoa(addresses.SOA_HOST));
|
||||
|
||||
const req = dns.resolveSoa(addresses.SOA_HOST, function(err, result) {
|
||||
assert.ifError(err);
|
||||
validateResult(result);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolveSoa_failure(done) {
|
||||
// dnsPromises.resolveSoa(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolveSoa(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
TEST(async function test_resolveCaa(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(Array.isArray(result),
|
||||
`expected array, got ${util.inspect(result)}`);
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert.strictEqual(typeof result[0].critical, 'number');
|
||||
assert.strictEqual(result[0].critical, 0);
|
||||
assert.strictEqual(result[0].issue, 'pki.goog');
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolveCaa(addresses.CAA_HOST));
|
||||
|
||||
const req = dns.resolveCaa(addresses.CAA_HOST, function(err, records) {
|
||||
assert.ifError(err);
|
||||
validateResult(records);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// NOTE(bartlomieju): this test started failing around July 11th, 2023.
|
||||
// TEST(async function test_resolveCname(done) {
|
||||
// function validateResult(result) {
|
||||
// assert.ok(result.length > 0);
|
||||
//
|
||||
// for (const item of result) {
|
||||
// assert.ok(item);
|
||||
// assert.strictEqual(typeof item, 'string');
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// validateResult(await dnsPromises.resolveCname(addresses.CNAME_HOST));
|
||||
//
|
||||
// const req = dns.resolveCname(addresses.CNAME_HOST, function(err, names) {
|
||||
// assert.ifError(err);
|
||||
// validateResult(names);
|
||||
// done();
|
||||
// });
|
||||
//
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolveCname_failure(done) {
|
||||
// dnsPromises.resolveCname(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolveCname(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
|
||||
TEST(async function test_resolveTxt(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(Array.isArray(result[0]));
|
||||
assert.strictEqual(result.length, 1);
|
||||
assert(result[0][0].startsWith('v=spf1'));
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.resolveTxt(addresses.TXT_HOST));
|
||||
|
||||
const req = dns.resolveTxt(addresses.TXT_HOST, function(err, records) {
|
||||
assert.ifError(err);
|
||||
validateResult(records);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolveTxt_failure(done) {
|
||||
// dnsPromises.resolveTxt(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.mustCall((err) => {
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// }));
|
||||
|
||||
// const req = dns.resolveTxt(addresses.NOT_FOUND, function(err, result) {
|
||||
// assert.ok(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
|
||||
// assert.strictEqual(result, undefined);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
|
||||
TEST(function test_lookup_failure(done) {
|
||||
dnsPromises.lookup(addresses.NOT_FOUND, 4)
|
||||
.then(common.mustNotCall())
|
||||
.catch(common.expectsError({ code: dns.NOTFOUND }));
|
||||
|
||||
const req = dns.lookup(addresses.NOT_FOUND, 4, (err) => {
|
||||
assert.ok(err instanceof Error);
|
||||
assert.strictEqual(err.code, dns.NOTFOUND);
|
||||
assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
assert.doesNotMatch(err.message, /ENOENT/);
|
||||
assert.ok(err.message.includes(addresses.NOT_FOUND));
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(async function test_lookup_ip_all(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.ok(result.length > 0);
|
||||
assert.strictEqual(result[0].address, '127.0.0.1');
|
||||
assert.strictEqual(result[0].family, 4);
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup('127.0.0.1', { all: true }));
|
||||
|
||||
const req = dns.lookup(
|
||||
'127.0.0.1',
|
||||
{ all: true },
|
||||
function(err, ips, family) {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(family, undefined);
|
||||
validateResult(ips);
|
||||
done();
|
||||
}
|
||||
);
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(function test_lookup_ip_all_promise(done) {
|
||||
const req = util.promisify(dns.lookup)('127.0.0.1', { all: true })
|
||||
.then(function(ips) {
|
||||
assert.ok(Array.isArray(ips));
|
||||
assert.ok(ips.length > 0);
|
||||
assert.strictEqual(ips[0].address, '127.0.0.1');
|
||||
assert.strictEqual(ips[0].family, 4);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(function test_lookup_ip_promise(done) {
|
||||
util.promisify(dns.lookup)('127.0.0.1')
|
||||
.then(function({ address, family }) {
|
||||
assert.strictEqual(address, '127.0.0.1');
|
||||
assert.strictEqual(family, 4);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
TEST(async function test_lookup_null_all(done) {
|
||||
assert.deepStrictEqual(await dnsPromises.lookup(null, { all: true }), []);
|
||||
|
||||
const req = dns.lookup(null, { all: true }, (err, ips) => {
|
||||
assert.ifError(err);
|
||||
assert.ok(Array.isArray(ips));
|
||||
assert.strictEqual(ips.length, 0);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
TEST(async function test_lookup_all_mixed(done) {
|
||||
function validateResult(result) {
|
||||
assert.ok(Array.isArray(result));
|
||||
assert.ok(result.length > 0);
|
||||
|
||||
result.forEach(function(ip) {
|
||||
if (isIPv4(ip.address))
|
||||
assert.strictEqual(ip.family, 4);
|
||||
else if (isIPv6(ip.address))
|
||||
assert.strictEqual(ip.family, 6);
|
||||
else
|
||||
assert.fail('unexpected IP address');
|
||||
});
|
||||
}
|
||||
|
||||
validateResult(await dnsPromises.lookup(addresses.INET_HOST, { all: true }));
|
||||
|
||||
const req = dns.lookup(addresses.INET_HOST, {
|
||||
all: true
|
||||
}, function(err, ips) {
|
||||
assert.ifError(err);
|
||||
validateResult(ips);
|
||||
done();
|
||||
});
|
||||
|
||||
checkWrap(req);
|
||||
});
|
||||
|
||||
|
||||
// TEST(function test_lookupservice_invalid(done) {
|
||||
// dnsPromises.lookupService('1.2.3.4', 80)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.expectsError({ code: 'ENOTFOUND' }));
|
||||
|
||||
// const req = dns.lookupService('1.2.3.4', 80, (err) => {
|
||||
// assert(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND');
|
||||
// assert.match(err.message, /1\.2\.3\.4/);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
|
||||
// TEST(function test_reverse_failure(done) {
|
||||
// dnsPromises.reverse('203.0.113.0')
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.expectsError({
|
||||
// code: 'ENOTFOUND',
|
||||
// hostname: '203.0.113.0'
|
||||
// }));
|
||||
|
||||
// // 203.0.113.0/24 are addresses reserved for (RFC) documentation use only
|
||||
// const req = dns.reverse('203.0.113.0', function(err) {
|
||||
// assert(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code...
|
||||
// assert.strictEqual(err.hostname, '203.0.113.0');
|
||||
// assert.match(err.message, /203\.0\.113\.0/);
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_lookup_failure(done) {
|
||||
// dnsPromises.lookup(addresses.NOT_FOUND)
|
||||
// .then(common.mustNotCall())
|
||||
// .catch(common.expectsError({
|
||||
// code: 'ENOTFOUND',
|
||||
// hostname: addresses.NOT_FOUND
|
||||
// }));
|
||||
|
||||
// const req = dns.lookup(addresses.NOT_FOUND, (err) => {
|
||||
// assert(err instanceof Error);
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code...
|
||||
// assert.strictEqual(err.hostname, addresses.NOT_FOUND);
|
||||
// assert.ok(err.message.includes(addresses.NOT_FOUND));
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
|
||||
// TODO(bartlomieju): this test became very flaky on CI, returning `UNKNOWN`
|
||||
// instead of `ENOTFOUND`.
|
||||
// TEST(function test_resolve_failure(done) {
|
||||
// const req = dns.resolve4(addresses.NOT_FOUND, (err) => {
|
||||
// assert(err instanceof Error);
|
||||
|
||||
// switch (err.code) {
|
||||
// case 'ENOTFOUND':
|
||||
// case 'ESERVFAIL':
|
||||
// break;
|
||||
// default:
|
||||
// assert.strictEqual(err.code, 'ENOTFOUND'); // Silly error code...
|
||||
// break;
|
||||
// }
|
||||
|
||||
// assert.strictEqual(err.hostname, addresses.NOT_FOUND);
|
||||
// assert.ok(err.message.includes(addresses.NOT_FOUND));
|
||||
|
||||
// done();
|
||||
// });
|
||||
|
||||
// checkWrap(req);
|
||||
// });
|
||||
|
||||
|
||||
let getaddrinfoCallbackCalled = false;
|
||||
|
||||
console.log(`looking up ${addresses.INET4_HOST}..`);
|
||||
|
||||
const cares = internalBinding('cares_wrap');
|
||||
const req = new cares.GetAddrInfoReqWrap();
|
||||
cares.getaddrinfo(req, addresses.INET4_HOST, 4,
|
||||
/* hints */ 0, /* verbatim */ true);
|
||||
|
||||
req.oncomplete = function(err, domains) {
|
||||
assert.strictEqual(err, 0);
|
||||
console.log(`${addresses.INET4_HOST} = ${domains}`);
|
||||
assert.ok(Array.isArray(domains));
|
||||
assert.ok(domains.length >= 1);
|
||||
assert.strictEqual(typeof domains[0], 'string');
|
||||
getaddrinfoCallbackCalled = true;
|
||||
};
|
||||
|
||||
process.on('exit', function() {
|
||||
console.log(`${completed} tests completed`);
|
||||
assert.strictEqual(running, false);
|
||||
assert.strictEqual(completed, expected);
|
||||
assert.ok(getaddrinfoCallbackCalled);
|
||||
});
|
||||
|
||||
// Should not throw.
|
||||
dns.lookup(addresses.INET6_HOST, 6, common.mustCall());
|
||||
dns.lookup(addresses.INET_HOST, {}, common.mustCall());
|
||||
// dns.lookupService('0.0.0.0', '0', common.mustCall());
|
||||
// dns.lookupService('0.0.0.0', 0, common.mustCall());
|
||||
(async function() {
|
||||
await dnsPromises.lookup(addresses.INET6_HOST, 6);
|
||||
await dnsPromises.lookup(addresses.INET_HOST, {});
|
||||
})().then(common.mustCall());
|
14
tests/registry/jsr/@deno/otel/0.0.2/deno.json
Normal file
14
tests/registry/jsr/@deno/otel/0.0.2/deno.json
Normal file
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "@deno/otel",
|
||||
"version": "0.0.2",
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./register": "./src/register.ts"
|
||||
},
|
||||
"tasks": {
|
||||
"check:license": "deno run -A tools/check_license.ts",
|
||||
"check:docs": "deno doc --lint src/index.ts",
|
||||
"check": "deno task check:license --check",
|
||||
"ok": "deno fmt --check && deno lint && deno task check"
|
||||
}
|
||||
}
|
38
tests/registry/jsr/@deno/otel/0.0.2/src/index.ts
Normal file
38
tests/registry/jsr/@deno/otel/0.0.2/src/index.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2024-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { context } from "npm:@opentelemetry/api@1";
|
||||
import {
|
||||
BasicTracerProvider,
|
||||
SimpleSpanProcessor,
|
||||
} from "npm:@opentelemetry/sdk-trace-base@1";
|
||||
|
||||
// @ts-ignore Deno.telemetry is not typed yet
|
||||
const telemetry = Deno.telemetry ?? Deno.tracing;
|
||||
|
||||
let COUNTER = 1;
|
||||
|
||||
/**
|
||||
* Register `Deno.telemetry` with the OpenTelemetry library.
|
||||
*/
|
||||
export function register() {
|
||||
context.setGlobalContextManager(
|
||||
new telemetry.ContextManager() ?? telemetry.ContextManager(),
|
||||
);
|
||||
|
||||
const provider = new BasicTracerProvider({
|
||||
idGenerator: Deno.env.get("DENO_UNSTABLE_OTEL_DETERMINISTIC") === "1" ? {
|
||||
generateSpanId() {
|
||||
return "1" + String(COUNTER++).padStart(15, "0");
|
||||
},
|
||||
generateTraceId() {
|
||||
return "1" + String(COUNTER++).padStart(31, "0");
|
||||
}
|
||||
} : undefined
|
||||
});
|
||||
|
||||
// @ts-ignore Deno.tracing is not typed yet
|
||||
const exporter = new telemetry.SpanExporter();
|
||||
provider.addSpanProcessor(new SimpleSpanProcessor(exporter));
|
||||
|
||||
provider.register();
|
||||
}
|
5
tests/registry/jsr/@deno/otel/0.0.2/src/register.ts
Normal file
5
tests/registry/jsr/@deno/otel/0.0.2/src/register.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright 2024-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { register } from "./index.ts";
|
||||
|
||||
register();
|
6
tests/registry/jsr/@deno/otel/0.0.2_meta.json
Normal file
6
tests/registry/jsr/@deno/otel/0.0.2_meta.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"exports": {
|
||||
".": "./src/index.ts",
|
||||
"./register": "./src/register.ts"
|
||||
}
|
||||
}
|
8
tests/registry/jsr/@deno/otel/meta.json
Normal file
8
tests/registry/jsr/@deno/otel/meta.json
Normal file
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"scope": "deno",
|
||||
"name": "otel",
|
||||
"latest": "0.0.2",
|
||||
"versions": {
|
||||
"0.0.2": {}
|
||||
}
|
||||
}
|
1
tests/registry/npm/@opentelemetry/api/registry.json
Normal file
1
tests/registry/npm/@opentelemetry/api/registry.json
Normal file
File diff suppressed because one or more lines are too long
1
tests/registry/npm/@opentelemetry/core/registry.json
Normal file
1
tests/registry/npm/@opentelemetry/core/registry.json
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"spans": [
|
||||
{
|
||||
"traceId": "00000000000000000000000000000002",
|
||||
"spanId": "0000000000000003",
|
||||
"traceId": "10000000000000000000000000000002",
|
||||
"spanId": "1000000000000003",
|
||||
"traceState": "",
|
||||
"parentSpanId": "0000000000000001",
|
||||
"parentSpanId": "1000000000000001",
|
||||
"flags": 1,
|
||||
"name": "inner span",
|
||||
"kind": 1,
|
||||
|
@ -22,8 +22,8 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"traceId": "00000000000000000000000000000002",
|
||||
"spanId": "0000000000000001",
|
||||
"traceId": "10000000000000000000000000000002",
|
||||
"spanId": "1000000000000001",
|
||||
"traceState": "",
|
||||
"parentSpanId": "",
|
||||
"flags": 1,
|
||||
|
@ -55,8 +55,8 @@
|
|||
"attributes": [],
|
||||
"droppedAttributesCount": 0,
|
||||
"flags": 1,
|
||||
"traceId": "00000000000000000000000000000002",
|
||||
"spanId": "0000000000000003"
|
||||
"traceId": "10000000000000000000000000000002",
|
||||
"spanId": "1000000000000003"
|
||||
},
|
||||
{
|
||||
"timeUnixNano": "0",
|
||||
|
@ -69,8 +69,8 @@
|
|||
"attributes": [],
|
||||
"droppedAttributesCount": 0,
|
||||
"flags": 1,
|
||||
"traceId": "00000000000000000000000000000002",
|
||||
"spanId": "0000000000000003"
|
||||
"traceId": "10000000000000000000000000000002",
|
||||
"spanId": "1000000000000003"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
import { trace } from "npm:@opentelemetry/api@1.9.0";
|
||||
import "jsr:@deno/otel@0.0.2/register";
|
||||
|
||||
const tracer = trace.getTracer("example-tracer");
|
||||
|
||||
async function inner() {
|
||||
using _span = new Deno.tracing.Span("inner span");
|
||||
await tracer.startActiveSpan("inner span", async (span) => {
|
||||
console.log("log 1");
|
||||
await 1;
|
||||
console.log("log 2");
|
||||
span.end();
|
||||
});
|
||||
}
|
||||
|
||||
const server = Deno.serve({
|
||||
|
@ -17,8 +24,11 @@ const server = Deno.serve({
|
|||
}
|
||||
},
|
||||
handler: async (_req) => {
|
||||
using _span = new Deno.tracing.Span("outer span");
|
||||
return await tracer.startActiveSpan("outer span", async (span) => {
|
||||
await inner();
|
||||
return new Response(null, { status: 200 });
|
||||
const resp = new Response(null, { status: 200 });
|
||||
span.end();
|
||||
return resp;
|
||||
});
|
||||
},
|
||||
});
|
||||
|
|
|
@ -10,7 +10,7 @@ const server = Deno.serve(
|
|||
port: 0,
|
||||
onListen({ port }) {
|
||||
const command = new Deno.Command(Deno.execPath(), {
|
||||
args: ["run", "-A", "--unstable-otel", Deno.args[0]],
|
||||
args: ["run", "-A", "-q", "--unstable-otel", Deno.args[0]],
|
||||
env: {
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: "http/json",
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: `http://localhost:${port}`,
|
||||
|
|
32
tests/specs/compile/code_cache/__test__.jsonc
Normal file
32
tests/specs/compile/code_cache/__test__.jsonc
Normal file
|
@ -0,0 +1,32 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"steps": [{
|
||||
"if": "unix",
|
||||
"args": "compile --output using_code_cache --log-level=debug main.ts",
|
||||
"output": "[WILDCARD]"
|
||||
}, {
|
||||
"if": "unix",
|
||||
"commandName": "./using_code_cache",
|
||||
"args": [],
|
||||
"output": "first_run.out"
|
||||
}, {
|
||||
"if": "unix",
|
||||
"commandName": "./using_code_cache",
|
||||
"args": [],
|
||||
"output": "second_run.out"
|
||||
}, {
|
||||
"if": "windows",
|
||||
"args": "compile --output using_code_cache.exe --log-level=debug main.ts",
|
||||
"output": "[WILDCARD]"
|
||||
}, {
|
||||
"if": "windows",
|
||||
"commandName": "./using_code_cache.exe",
|
||||
"args": [],
|
||||
"output": "first_run.out"
|
||||
}, {
|
||||
"if": "windows",
|
||||
"commandName": "./using_code_cache.exe",
|
||||
"args": [],
|
||||
"output": "second_run.out"
|
||||
}]
|
||||
}
|
1
tests/specs/compile/code_cache/first_run.out
Normal file
1
tests/specs/compile/code_cache/first_run.out
Normal file
|
@ -0,0 +1 @@
|
|||
[WILDCARD]Serialized 9 code cache entries[WILDCARD]
|
3
tests/specs/compile/code_cache/main.ts
Normal file
3
tests/specs/compile/code_cache/main.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { join } from "jsr:@std/url@0.220/join";
|
||||
|
||||
console.log(join);
|
1
tests/specs/compile/code_cache/second_run.out
Normal file
1
tests/specs/compile/code_cache/second_run.out
Normal file
|
@ -0,0 +1 @@
|
|||
[WILDCARD]Loaded 9 code cache entries[WILDCARD][Function: join][WILDCARD]
|
22
tests/specs/compile/no_code_cache/__test__.jsonc
Normal file
22
tests/specs/compile/no_code_cache/__test__.jsonc
Normal file
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"steps": [{
|
||||
"if": "unix",
|
||||
"args": "compile --output no_code_cache --no-code-cache --log-level=debug main.ts",
|
||||
"output": "[WILDCARD]"
|
||||
}, {
|
||||
"if": "unix",
|
||||
"commandName": "./no_code_cache",
|
||||
"args": [],
|
||||
"output": "main.out"
|
||||
}, {
|
||||
"if": "windows",
|
||||
"args": "compile --output no_code_cache.exe --no-code-cache --log-level=debug main.ts",
|
||||
"output": "[WILDCARD]"
|
||||
}, {
|
||||
"if": "windows",
|
||||
"commandName": "./no_code_cache.exe",
|
||||
"args": [],
|
||||
"output": "main.out"
|
||||
}]
|
||||
}
|
1
tests/specs/compile/no_code_cache/main.out
Normal file
1
tests/specs/compile/no_code_cache/main.out
Normal file
|
@ -0,0 +1 @@
|
|||
[WILDCARD]Code cache disabled.[WILDCARD]
|
3
tests/specs/compile/no_code_cache/main.ts
Normal file
3
tests/specs/compile/no_code_cache/main.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
import { join } from "jsr:@std/url@0.220/join";
|
||||
|
||||
console.log(join);
|
1
tests/specs/fmt/gitignore/.gitignore
vendored
Normal file
1
tests/specs/fmt/gitignore/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/dist/
|
6
tests/specs/fmt/gitignore/__test__.jsonc
Normal file
6
tests/specs/fmt/gitignore/__test__.jsonc
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"args": "fmt --check",
|
||||
"output": "expected.out",
|
||||
"exitCode": 1
|
||||
}
|
6
tests/specs/fmt/gitignore/dist/file1.js
vendored
Normal file
6
tests/specs/fmt/gitignore/dist/file1.js
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
// This file is in `.gitignore` simulating that it's generated by a build tool
|
||||
// and should not be linted
|
||||
function foo( ) {
|
||||
console.log( "hello")
|
||||
}
|
||||
|
10
tests/specs/fmt/gitignore/expected.out
Normal file
10
tests/specs/fmt/gitignore/expected.out
Normal file
|
@ -0,0 +1,10 @@
|
|||
|
||||
from [WILDCARD]file2.ts:
|
||||
1 | -function foo( ): any {
|
||||
1 | +function foo(): any {
|
||||
2 | - console.log( "hello")
|
||||
2 | + console.log("hello");
|
||||
3 | - }
|
||||
3 | +}
|
||||
|
||||
error: Found 1 not formatted file in 1 file
|
3
tests/specs/fmt/gitignore/file2.ts
Normal file
3
tests/specs/fmt/gitignore/file2.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
function foo( ): any {
|
||||
console.log( "hello")
|
||||
}
|
1
tests/specs/lint/gitignore/.gitignore
vendored
Normal file
1
tests/specs/lint/gitignore/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/dist/
|
5
tests/specs/lint/gitignore/__test__.jsonc
Normal file
5
tests/specs/lint/gitignore/__test__.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"args": "lint",
|
||||
"output": "expected.out",
|
||||
"exitCode": 1
|
||||
}
|
3
tests/specs/lint/gitignore/dist/file1.js
vendored
Normal file
3
tests/specs/lint/gitignore/dist/file1.js
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
// This file is in `.gitignore` simulating that it's generated by a build tool
|
||||
// and should not be linted
|
||||
while (false) {}
|
12
tests/specs/lint/gitignore/expected.out
Normal file
12
tests/specs/lint/gitignore/expected.out
Normal file
|
@ -0,0 +1,12 @@
|
|||
error[no-empty]: Empty block statement
|
||||
--> [WILDCARD]file2.ts:3:14
|
||||
|
|
||||
3 | } catch (_e) {}
|
||||
| ^^
|
||||
= hint: Add code or comment to the empty block
|
||||
|
||||
docs: https://lint.deno.land/rules/no-empty
|
||||
|
||||
|
||||
Found 1 problem
|
||||
Checked 1 file
|
6
tests/specs/lint/gitignore/file2.ts
Normal file
6
tests/specs/lint/gitignore/file2.ts
Normal file
|
@ -0,0 +1,6 @@
|
|||
try {
|
||||
await Deno.open("./some/file.txt");
|
||||
} catch (_e) {}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
function _foo(): any {}
|
|
@ -677,3 +677,26 @@ Deno.test("generateKeyPair large pem", function () {
|
|||
},
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test("generateKeyPair promisify", async () => {
|
||||
const passphrase = "mypassphrase";
|
||||
const cipher = "aes-256-cbc";
|
||||
const modulusLength = 4096;
|
||||
|
||||
const { privateKey, publicKey } = await promisify(generateKeyPair)("rsa", {
|
||||
modulusLength,
|
||||
publicKeyEncoding: {
|
||||
type: "spki",
|
||||
format: "pem",
|
||||
},
|
||||
privateKeyEncoding: {
|
||||
type: "pkcs8",
|
||||
format: "pem",
|
||||
cipher,
|
||||
passphrase,
|
||||
},
|
||||
});
|
||||
|
||||
assert(publicKey.startsWith("-----BEGIN PUBLIC KEY-----"));
|
||||
assert(privateKey.startsWith("-----BEGIN PRIVATE KEY-----"));
|
||||
});
|
||||
|
|
|
@ -247,7 +247,7 @@
|
|||
"ext:runtime/41_prompt.js": "../runtime/js/41_prompt.js",
|
||||
"ext:runtime/90_deno_ns.js": "../runtime/js/90_deno_ns.js",
|
||||
"ext:runtime/98_global_scope.js": "../runtime/js/98_global_scope.js",
|
||||
"ext:runtime/telemetry.js": "../runtime/js/telemetry.js",
|
||||
"ext:runtime/telemetry.ts": "../runtime/js/telemetry.ts",
|
||||
"ext:deno_node/_util/std_fmt_colors.ts": "../ext/node/polyfills/_util/std_fmt_colors.ts",
|
||||
"@std/archive": "../tests/util/std/archive/mod.ts",
|
||||
"@std/archive/tar": "../tests/util/std/archive/tar.ts",
|
||||
|
|
Loading…
Reference in a new issue