diff --git a/Cargo.lock b/Cargo.lock index 93ba0c55e7..531c922294 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1186,10 +1186,18 @@ name = "deno_node" version = "0.26.0" dependencies = [ "deno_core", + "digest 0.10.6", + "md-5", + "md4", "once_cell", "path-clean", "regex", + "ripemd", "serde", + "sha-1 0.10.0", + "sha2", + "sha3", + "typenum", ] [[package]] @@ -2521,6 +2529,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "keccak" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3afef3b6eff9ce9d8ff9b3601125eec7f0c8cbac7abd14f355d053fa56c98768" +dependencies = [ + "cpufeatures", +] + [[package]] name = "khronos-egl" version = "4.1.0" @@ -2779,6 +2796,24 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +[[package]] +name = "md-5" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca" +dependencies = [ + "digest 0.10.6", +] + +[[package]] +name = "md4" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da5ac363534dce5fabf69949225e174fbf111a498bf0ff794c8ea1fba9f3dda" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "memchr" version = "2.5.0" @@ -3693,6 +3728,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "ripemd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd124222d17ad93a644ed9d011a40f4fb64aa54275c08cc216524a9ea82fb09f" +dependencies = [ + "digest 0.10.6", +] + [[package]] name = "ron" version = "0.8.0" @@ -4083,6 +4127,16 @@ dependencies = [ "digest 0.10.6", ] +[[package]] +name = "sha3" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdf0c33fae925bdc080598b84bc15c55e7b9a4a43b3c704da051f977469691c9" +dependencies = [ + "digest 0.10.6", + "keccak", +] + [[package]] name = "shell-escape" version = "0.1.5" diff --git a/cli/build.rs b/cli/build.rs index 867f3fb982..84d8fd5498 100644 --- a/cli/build.rs +++ b/cli/build.rs @@ -343,6 +343,7 @@ fn create_cli_snapshot(snapshot_path: PathBuf) { false, // No --unstable. ), deno_node::init::(None), // No --unstable. + deno_node::init_polyfill(), deno_ffi::init::(false), deno_net::init::( None, false, // No --unstable. diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index a2b6a6835f..3d8a95fe86 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -15,7 +15,15 @@ path = "lib.rs" [dependencies] deno_core.workspace = true +digest = { version = "0.10.5", features = ["core-api", "std"] } +md-5 = "0.10.5" +md4 = "0.10.2" once_cell.workspace = true path-clean = "=0.1.0" regex.workspace = true +ripemd = "0.1.3" serde = "1.0.149" +sha-1 = "0.10.0" +sha2 = "0.10.6" +sha3 = "0.10.5" +typenum = "1.15.0" diff --git a/ext/node/crypto/digest.rs b/ext/node/crypto/digest.rs new file mode 100644 index 0000000000..4fab58a432 --- /dev/null +++ b/ext/node/crypto/digest.rs @@ -0,0 +1,117 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::Resource; +use digest::Digest; +use digest::DynDigest; +use std::borrow::Cow; +use std::cell::RefCell; +use std::rc::Rc; + +pub enum Hash { + Md4(Box), + Md5(Box), + Ripemd160(Box), + Sha1(Box), + Sha224(Box), + Sha256(Box), + Sha384(Box), + Sha512(Box), +} + +pub struct Context { + pub hash: Rc>, +} + +impl Context { + pub fn new(algorithm: &str) -> Result { + Ok(Self { + hash: Rc::new(RefCell::new(Hash::new(algorithm)?)), + }) + } + + pub fn update(&self, data: &[u8]) { + self.hash.borrow_mut().update(data); + } + + pub fn digest(self) -> Result, AnyError> { + let hash = Rc::try_unwrap(self.hash) + .map_err(|_| type_error("Hash context is already in use"))?; + + let hash = hash.into_inner(); + Ok(hash.digest_and_drop()) + } +} + +impl Clone for Context { + fn clone(&self) -> Self { + Self { + hash: Rc::new(RefCell::new(self.hash.borrow().clone())), + } + } +} + +impl Resource for Context { + fn name(&self) -> Cow { + "cryptoDigest".into() + } +} + +use Hash::*; + +impl Hash { + pub fn new(algorithm_name: &str) -> Result { + Ok(match algorithm_name { + "md4" => Md4(Default::default()), + "md5" => Md5(Default::default()), + "ripemd160" => Ripemd160(Default::default()), + "sha1" => Sha1(Default::default()), + "sha224" => Sha224(Default::default()), + "sha256" => Sha256(Default::default()), + "sha384" => Sha384(Default::default()), + "sha512" => Sha512(Default::default()), + _ => return Err(type_error("unsupported algorithm")), + }) + } + + pub fn update(&mut self, data: &[u8]) { + match self { + Md4(context) => Digest::update(&mut **context, data), + Md5(context) => Digest::update(&mut **context, data), + Ripemd160(context) => Digest::update(&mut **context, data), + Sha1(context) => Digest::update(&mut **context, data), + Sha224(context) => Digest::update(&mut **context, data), + Sha256(context) => Digest::update(&mut **context, data), + Sha384(context) => Digest::update(&mut **context, data), + Sha512(context) => Digest::update(&mut **context, data), + }; + } + + pub fn digest_and_drop(self) -> Box<[u8]> { + match self { + Md4(context) => context.finalize(), + Md5(context) => context.finalize(), + Ripemd160(context) => context.finalize(), + Sha1(context) => context.finalize(), + Sha224(context) => context.finalize(), + Sha256(context) => context.finalize(), + Sha384(context) => context.finalize(), + Sha512(context) => context.finalize(), + } + } +} + +impl Clone for Hash { + fn clone(&self) -> Self { + match self { + Md4(_) => Md4(Default::default()), + Md5(_) => Md5(Default::default()), + Ripemd160(_) => Ripemd160(Default::default()), + Sha1(_) => Sha1(Default::default()), + Sha224(_) => Sha224(Default::default()), + Sha256(_) => Sha256(Default::default()), + Sha384(_) => Sha384(Default::default()), + Sha512(_) => Sha512(Default::default()), + } + } +} diff --git a/ext/node/crypto/mod.rs b/ext/node/crypto/mod.rs new file mode 100644 index 0000000000..be92dc7ef6 --- /dev/null +++ b/ext/node/crypto/mod.rs @@ -0,0 +1,49 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +use deno_core::error::type_error; +use deno_core::error::AnyError; +use deno_core::op; +use deno_core::OpState; +use deno_core::ResourceId; +use deno_core::ZeroCopyBuf; +use std::rc::Rc; + +mod digest; + +#[op] +pub fn op_node_create_hash( + state: &mut OpState, + algorithm: String, +) -> Result { + Ok(state.resource_table.add(digest::Context::new(&algorithm)?)) +} + +#[op] +pub fn op_node_hash_update( + state: &mut OpState, + rid: ResourceId, + data: &[u8], +) -> Result<(), AnyError> { + let context = state.resource_table.get::(rid)?; + context.update(data); + Ok(()) +} + +#[op] +pub fn op_node_hash_digest( + state: &mut OpState, + rid: ResourceId, +) -> Result { + let context = state.resource_table.take::(rid)?; + let context = Rc::try_unwrap(context) + .map_err(|_| type_error("Hash context is already in use"))?; + Ok(context.digest()?.into()) +} + +#[op] +pub fn op_node_hash_clone( + state: &mut OpState, + rid: ResourceId, +) -> Result { + let context = state.resource_table.get::(rid)?; + Ok(state.resource_table.add(context.as_ref().clone())) +} diff --git a/ext/node/lib.rs b/ext/node/lib.rs index aabbbfafd3..0987febe86 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -2,7 +2,9 @@ use deno_core::error::AnyError; use deno_core::include_js_files; +use deno_core::include_js_files_dir; use deno_core::located_script_name; +use deno_core::op; use deno_core::Extension; use deno_core::JsRuntime; use once_cell::sync::Lazy; @@ -11,6 +13,7 @@ use std::path::Path; use std::path::PathBuf; use std::rc::Rc; +mod crypto; pub mod errors; mod ops; mod package_json; @@ -62,8 +65,6 @@ pub trait RequireNpmResolver { ) -> Result<(), AnyError>; } -pub const MODULE_ES_SHIM: &str = include_str!("./module_es_shim.js"); - pub static NODE_GLOBAL_THIS_NAME: Lazy = Lazy::new(|| { let now = std::time::SystemTime::now(); let seconds = now @@ -83,10 +84,344 @@ pub static NODE_ENV_VAR_ALLOWLIST: Lazy> = Lazy::new(|| { set }); +#[op] +fn op_node_build_os() -> String { + std::env::var("TARGET") + .unwrap() + .split('-') + .nth(2) + .unwrap() + .to_string() +} + +pub fn init_polyfill() -> Extension { + let esm_files = include_js_files_dir!( + dir "polyfills", + "_core.ts", + "_crypto/crypto_browserify/asn1.js/base/buffer.js", + "_crypto/crypto_browserify/asn1.js/base/node.js", + "_crypto/crypto_browserify/asn1.js/base/reporter.js", + "_crypto/crypto_browserify/asn1.js/constants/der.js", + "_crypto/crypto_browserify/asn1.js/decoders/der.js", + "_crypto/crypto_browserify/asn1.js/decoders/pem.js", + "_crypto/crypto_browserify/asn1.js/encoders/der.js", + "_crypto/crypto_browserify/asn1.js/encoders/pem.js", + "_crypto/crypto_browserify/asn1.js/mod.js", + "_crypto/crypto_browserify/bn.js/bn.js", + "_crypto/crypto_browserify/browserify_aes/aes.js", + "_crypto/crypto_browserify/browserify_aes/auth_cipher.js", + "_crypto/crypto_browserify/browserify_aes/decrypter.js", + "_crypto/crypto_browserify/browserify_aes/encrypter.js", + "_crypto/crypto_browserify/browserify_aes/ghash.js", + "_crypto/crypto_browserify/browserify_aes/incr32.js", + "_crypto/crypto_browserify/browserify_aes/mod.js", + "_crypto/crypto_browserify/browserify_aes/modes/cbc.js", + "_crypto/crypto_browserify/browserify_aes/modes/cfb.js", + "_crypto/crypto_browserify/browserify_aes/modes/cfb1.js", + "_crypto/crypto_browserify/browserify_aes/modes/cfb8.js", + "_crypto/crypto_browserify/browserify_aes/modes/ctr.js", + "_crypto/crypto_browserify/browserify_aes/modes/ecb.js", + "_crypto/crypto_browserify/browserify_aes/modes/mod.js", + "_crypto/crypto_browserify/browserify_aes/modes/ofb.js", + "_crypto/crypto_browserify/browserify_aes/stream_cipher.js", + "_crypto/crypto_browserify/browserify_aes/xor.ts", + "_crypto/crypto_browserify/browserify_rsa.js", + "_crypto/crypto_browserify/cipher_base.js", + "_crypto/crypto_browserify/evp_bytes_to_key.ts", + "_crypto/crypto_browserify/parse_asn1/asn1.js", + "_crypto/crypto_browserify/parse_asn1/certificate.js", + "_crypto/crypto_browserify/parse_asn1/fix_proc.js", + "_crypto/crypto_browserify/parse_asn1/mod.js", + "_crypto/crypto_browserify/public_encrypt/mgf.js", + "_crypto/crypto_browserify/public_encrypt/mod.js", + "_crypto/crypto_browserify/public_encrypt/private_decrypt.js", + "_crypto/crypto_browserify/public_encrypt/public_encrypt.js", + "_crypto/crypto_browserify/public_encrypt/with_public.js", + "_crypto/crypto_browserify/public_encrypt/xor.js", + "_crypto/crypto_browserify/randombytes.ts", + "_events.mjs", + "_fs/_fs_access.ts", + "_fs/_fs_appendFile.ts", + "_fs/_fs_chmod.ts", + "_fs/_fs_chown.ts", + "_fs/_fs_close.ts", + "_fs/_fs_common.ts", + "_fs/_fs_constants.ts", + "_fs/_fs_copy.ts", + "_fs/_fs_dir.ts", + "_fs/_fs_dirent.ts", + "_fs/_fs_exists.ts", + "_fs/_fs_fdatasync.ts", + "_fs/_fs_fstat.ts", + "_fs/_fs_fsync.ts", + "_fs/_fs_ftruncate.ts", + "_fs/_fs_futimes.ts", + "_fs/_fs_link.ts", + "_fs/_fs_lstat.ts", + "_fs/_fs_mkdir.ts", + "_fs/_fs_mkdtemp.ts", + "_fs/_fs_open.ts", + "_fs/_fs_opendir.ts", + "_fs/_fs_read.ts", + "_fs/_fs_readdir.ts", + "_fs/_fs_readFile.ts", + "_fs/_fs_readlink.ts", + "_fs/_fs_realpath.ts", + "_fs/_fs_rename.ts", + "_fs/_fs_rm.ts", + "_fs/_fs_rmdir.ts", + "_fs/_fs_stat.ts", + "_fs/_fs_symlink.ts", + "_fs/_fs_truncate.ts", + "_fs/_fs_unlink.ts", + "_fs/_fs_utimes.ts", + "_fs/_fs_watch.ts", + "_fs/_fs_write.mjs", + "_fs/_fs_writeFile.ts", + "_fs/_fs_writev.mjs", + "_http_agent.mjs", + "_http_common.ts", + "_http_outgoing.ts", + "_next_tick.ts", + "_pako.mjs", + "_process/exiting.ts", + "_process/process.ts", + "_process/stdio.mjs", + "_process/streams.mjs", + "_readline.mjs", + "_stream.mjs", + "_tls_common.ts", + "_tls_wrap.ts", + "_util/_util_callbackify.ts", + "_util/asserts.ts", + "_util/async.ts", + "_util/os.ts", + "_util/std_asserts.ts", + "_util/std_fmt_colors.ts", + "_util/std_testing_diff.ts", + "_utils.ts", + "_zlib_binding.mjs", + "_zlib.mjs", + "assert.ts", + "assert/strict.ts", + "assertion_error.ts", + "async_hooks.ts", + "buffer.ts", + "child_process.ts", + "cluster.ts", + "console.ts", + "constants.ts", + "crypto.ts", + "dgram.ts", + "diagnostics_channel.ts", + "dns.ts", + "dns/promises.ts", + "domain.ts", + "events.ts", + "fs.ts", + "fs/promises.ts", + "global.ts", + "http.ts", + "http2.ts", + "https.ts", + "inspector.ts", + "internal_binding/_libuv_winerror.ts", + "internal_binding/_listen.ts", + "internal_binding/_node.ts", + "internal_binding/_timingSafeEqual.ts", + "internal_binding/_utils.ts", + "internal_binding/_winerror.ts", + "internal_binding/ares.ts", + "internal_binding/async_wrap.ts", + "internal_binding/buffer.ts", + "internal_binding/cares_wrap.ts", + "internal_binding/config.ts", + "internal_binding/connection_wrap.ts", + "internal_binding/constants.ts", + "internal_binding/contextify.ts", + "internal_binding/credentials.ts", + "internal_binding/crypto.ts", + "internal_binding/errors.ts", + "internal_binding/fs_dir.ts", + "internal_binding/fs_event_wrap.ts", + "internal_binding/fs.ts", + "internal_binding/handle_wrap.ts", + "internal_binding/heap_utils.ts", + "internal_binding/http_parser.ts", + "internal_binding/icu.ts", + "internal_binding/inspector.ts", + "internal_binding/js_stream.ts", + "internal_binding/messaging.ts", + "internal_binding/mod.ts", + "internal_binding/module_wrap.ts", + "internal_binding/native_module.ts", + "internal_binding/natives.ts", + "internal_binding/node_file.ts", + "internal_binding/node_options.ts", + "internal_binding/options.ts", + "internal_binding/os.ts", + "internal_binding/performance.ts", + "internal_binding/pipe_wrap.ts", + "internal_binding/process_methods.ts", + "internal_binding/report.ts", + "internal_binding/serdes.ts", + "internal_binding/signal_wrap.ts", + "internal_binding/spawn_sync.ts", + "internal_binding/stream_wrap.ts", + "internal_binding/string_decoder.ts", + "internal_binding/symbols.ts", + "internal_binding/task_queue.ts", + "internal_binding/tcp_wrap.ts", + "internal_binding/timers.ts", + "internal_binding/tls_wrap.ts", + "internal_binding/trace_events.ts", + "internal_binding/tty_wrap.ts", + "internal_binding/types.ts", + "internal_binding/udp_wrap.ts", + "internal_binding/url.ts", + "internal_binding/util.ts", + "internal_binding/uv.ts", + "internal_binding/v8.ts", + "internal_binding/worker.ts", + "internal_binding/zlib.ts", + "internal/assert.mjs", + "internal/async_hooks.ts", + "internal/blob.mjs", + "internal/buffer.mjs", + "internal/child_process.ts", + "internal/cli_table.ts", + "internal/console/constructor.mjs", + "internal/constants.ts", + "internal/crypto/_hex.ts", + "internal/crypto/_keys.ts", + "internal/crypto/_randomBytes.ts", + "internal/crypto/_randomFill.ts", + "internal/crypto/_randomInt.ts", + "internal/crypto/certificate.ts", + "internal/crypto/cipher.ts", + "internal/crypto/constants.ts", + "internal/crypto/diffiehellman.ts", + "internal/crypto/hash.ts", + "internal/crypto/hkdf.ts", + "internal/crypto/keygen.ts", + "internal/crypto/keys.ts", + "internal/crypto/pbkdf2.ts", + "internal/crypto/random.ts", + "internal/crypto/scrypt.ts", + "internal/crypto/sig.ts", + "internal/crypto/types.ts", + "internal/crypto/util.ts", + "internal/crypto/x509.ts", + "internal/dgram.ts", + "internal/dns/promises.ts", + "internal/dns/utils.ts", + "internal/dtrace.ts", + "internal/error_codes.ts", + "internal/errors.ts", + "internal/event_target.mjs", + "internal/fixed_queue.ts", + "internal/freelist.ts", + "internal/fs/streams.mjs", + "internal/fs/utils.mjs", + "internal/hide_stack_frames.ts", + "internal/http.ts", + "internal/idna.ts", + "internal/net.ts", + "internal/normalize_encoding.mjs", + "internal/options.ts", + "internal/primordials.mjs", + "internal/process/per_thread.mjs", + "internal/querystring.ts", + "internal/readline/callbacks.mjs", + "internal/readline/emitKeypressEvents.mjs", + "internal/readline/interface.mjs", + "internal/readline/promises.mjs", + "internal/readline/symbols.mjs", + "internal/readline/utils.mjs", + "internal/stream_base_commons.ts", + "internal/streams/add-abort-signal.mjs", + "internal/streams/buffer_list.mjs", + "internal/streams/destroy.mjs", + "internal/streams/duplex.mjs", + "internal/streams/end-of-stream.mjs", + "internal/streams/lazy_transform.mjs", + "internal/streams/legacy.mjs", + "internal/streams/passthrough.mjs", + "internal/streams/readable.mjs", + "internal/streams/state.mjs", + "internal/streams/transform.mjs", + "internal/streams/utils.mjs", + "internal/streams/writable.mjs", + "internal/test/binding.ts", + "internal/timers.mjs", + "internal/url.ts", + "internal/util.mjs", + "internal/util/comparisons.ts", + "internal/util/debuglog.ts", + "internal/util/inspect.mjs", + "internal/util/types.ts", + "internal/validators.mjs", + "module_all.ts", + "module_esm.ts", + "module.js", + "net.ts", + "os.ts", + "path.ts", + "path/_constants.ts", + "path/_interface.ts", + "path/_util.ts", + "path/common.ts", + "path/glob.ts", + "path/mod.ts", + "path/posix.ts", + "path/separator.ts", + "path/win32.ts", + "perf_hooks.ts", + "process.ts", + "punycode.ts", + "querystring.ts", + "readline.ts", + "readline/promises.ts", + "repl.ts", + "stream.ts", + "stream/consumers.mjs", + "stream/promises.mjs", + "stream/web.ts", + "string_decoder_bench.js", + "string_decoder.ts", + "sys.ts", + "timers.ts", + "timers/promises.ts", + "tls.ts", + "tty.ts", + "upstream_modules.ts", + "url.ts", + "util.ts", + "util/types.ts", + "v8.ts", + "vm.ts", + "wasi.ts", + "worker_threads.ts", + "zlib.ts", + ); + + Extension::builder(env!("CARGO_PKG_NAME")) + .esm(esm_files) + .esm_entry_point("internal:deno_node/polyfills/module_all.ts") + .ops(vec![ + crypto::op_node_create_hash::decl(), + crypto::op_node_hash_update::decl(), + crypto::op_node_hash_digest::decl(), + crypto::op_node_hash_clone::decl(), + op_node_build_os::decl(), + ]) + .build() +} + pub fn init( maybe_npm_resolver: Option>, ) -> Extension { - Extension::builder(env!("CARGO_PKG_NAME")) + Extension::builder("deno_node_loading") .esm(include_js_files!( "01_node.js", "02_require.js", diff --git a/ext/node/polyfill.rs b/ext/node/polyfill.rs index 371b27be6c..f3a704b811 100644 --- a/ext/node/polyfill.rs +++ b/ext/node/polyfill.rs @@ -103,7 +103,7 @@ pub static SUPPORTED_BUILTIN_NODE_MODULES: &[NodeModulePolyfill] = &[ NodeModulePolyfill { name: "module", specifier: NodeModulePolyfillSpecifier::Embedded( - "internal:deno_node/module_es_shim.js", + "internal:deno_node_loading/module_es_shim.js", ), }, NodeModulePolyfill { diff --git a/ext/node/polyfills/README.md b/ext/node/polyfills/README.md new file mode 100644 index 0000000000..ddad19dd68 --- /dev/null +++ b/ext/node/polyfills/README.md @@ -0,0 +1,248 @@ +# Deno Node.js compatibility + +This module is meant to have a compatibility layer for the +[Node.js standard library](https://nodejs.org/docs/latest/api/). + +**Warning**: Any function of this module should not be referred anywhere in the +Deno standard library as it's a compatibility module. + +## Supported modules + +- [x] assert +- [x] assert/strict _partly_ +- [x] async_hooks _partly_ +- [x] buffer +- [x] child_process _partly_ +- [x] cluster _partly_ +- [x] console _partly_ +- [x] constants _partly_ +- [x] crypto _partly_ +- [x] dgram _partly_ +- [x] diagnostics_channel _partly_ +- [x] dns _partly_ +- [x] events +- [x] fs _partly_ +- [x] fs/promises _partly_ +- [x] http _partly_ +- [ ] http2 +- [x] https _partly_ +- [x] inspector _partly_ +- [x] module +- [x] net +- [x] os _partly_ +- [x] path +- [x] path/posix +- [x] path/win32 +- [x] perf_hooks +- [x] process _partly_ +- [x] punycode +- [x] querystring +- [x] readline +- [x] repl _partly_ +- [x] stream +- [x] stream/promises +- [x] stream/web _partly_ +- [x] string_decoder +- [x] sys +- [x] timers +- [x] timers/promises +- [ ] tls +- [ ] trace_events +- [x] tty _partly_ +- [x] url +- [x] util _partly_ +- [x] util/types _partly_ +- [ ] v8 +- [x] vm _partly_ +- [x] wasi +- [ ] webcrypto +- [x] worker_threads +- [ ] zlib + +* [x] node globals _partly_ + +### Deprecated + +These modules are deprecated in Node.js and will probably not be polyfilled: + +- domain +- freelist + +### Experimental + +These modules are experimental in Node.js and will not be polyfilled until they +are stable: + +- diagnostics_channel +- async_hooks +- policies +- trace_events +- wasi +- webcrypto + +## CommonJS modules loading + +`createRequire(...)` is provided to create a `require` function for loading CJS +modules. It also sets supported globals. + +```ts +import { createRequire } from "https://deno.land/std@$STD_VERSION/node/module.ts"; + +const require = createRequire(import.meta.url); +// Loads native module polyfill. +const path = require("path"); +// Loads extensionless module. +const cjsModule = require("./my_mod"); +// Visits node_modules. +const leftPad = require("left-pad"); +``` + +## Contributing + +### Setting up the test runner + +This library contains automated tests pulled directly from the Node.js repo in +order ensure compatibility. + +Setting up the test runner is as simple as running the `node/_tools/setup.ts` +file, this will pull the configured tests in and then add them to the test +workflow. + +```zsh +$ deno task node:setup +``` + +You can additionally pass the `-y`/`-n` flag to use test cache or generating +tests from scratch instead of being prompted at the moment of running it. + +```zsh +# Will use downloaded tests instead of prompting user +$ deno run --allow-read --allow-net --allow-write node/_tools/setup.ts -y +# Will not prompt but will download and extract the tests directly +$ deno run --allow-read --allow-net --allow-write node/_tools/setup.ts -n +``` + +To run the tests you have set up, do the following: + +```zsh +$ deno test --allow-read --allow-run node/_tools/test.ts +``` + +If you want to run specific Node.js test files, you can use the following +command + +```shellsession +$ deno test -A node/_tools/test.ts -- +``` + +For example, if you want to run only +`node/_tools/test/parallel/test-event-emitter-check-listener-leaks.js`, you can +use: + +```shellsession +$ deno test -A node/_tools/test.ts -- test-event-emitter-check-listener-leaks.js +``` + +If you want to run all test files which contains `event-emitter` in filename, +then you can use: + +```shellsession +$ deno test -A node/_tools/test.ts -- event-emitter +``` + +The test should be passing with the latest deno, so if the test fails, try the +following: + +- `$ deno upgrade` +- `$ git submodule update --init` +- Use + [`--unstable` flag](https://deno.land/manual@v1.15.3/runtime/stability#standard-modules) + +To enable new tests, simply add a new entry inside `node/_tools/config.json` +under the `tests` property. The structure this entries must have has to resemble +a path inside `https://github.com/nodejs/node/tree/main/test`. + +Adding a new entry under the `ignore` option will indicate the test runner that +it should not regenerate that file from scratch the next time the setup is run, +this is specially useful to keep track of files that have been manually edited +to pass certain tests. However, avoid doing such manual changes to the test +files, since that may cover up inconsistencies between the node library and +actual node behavior. + +### Working with child processes ? Use `DENO_NODE_COMPAT_URL` + +When working with `child_process` modules, you will have to run tests pulled +from Node.js. These tests usually spawn deno child processes via the use of +`process.execPath`. The `deno` executable will use its own embedded version of +std modules, then you may get the impression your code is not really working as +it should. + +To prevent this, set `DENO_NODE_COMPAT_URL` with the absolute path to your +`deno_std` repo, ending with a trailing slash: + +``` +export DENO_NODE_COMPAT_URL=$PWD/ +# or +export DENO_NODE_COMPAT_URL=file:///path/to/deno_std/dir/ +``` + +Then, `deno` will use your local copy of `deno_std` instead of latest version. + +### Best practices + +When converting from promise-based to callback-based APIs, the most obvious way +is like this: + +```ts, ignore +promise.then((value) => callback(null, value)).catch(callback); +``` + +This has a subtle bug - if the callback throws an error, the catch statement +will also catch _that_ error, and the callback will be called twice. The correct +way to do it is like this: + +```ts, ignore +promise.then((value) => callback(null, value), callback); +``` + +The second parameter of `then` can also be used to catch errors, but only errors +from the existing promise, not the new one created by the callback. + +If the Deno equivalent is actually synchronous, there's a similar problem with +try/catch statements: + +```ts, ignore +try { + const value = process(); + callback(null, value); +} catch (err) { + callback(err); +} +``` + +Since the callback is called within the `try` block, any errors from it will be +caught and call the callback again. + +The correct way to do it is like this: + +```ts, ignore +let err, value; +try { + value = process(); +} catch (e) { + err = e; +} +if (err) { + callback(err); // Make sure arguments.length === 1 +} else { + callback(null, value); +} +``` + +It's not as clean, but prevents the callback being called twice. + +### Remaining Tests + +Node compatibility can be measured by how many native Node tests pass. If you'd +like to know what you can work on, check out the list of Node tests remaining +[here](_tools/TODO.md). diff --git a/ext/node/polyfills/_core.ts b/ext/node/polyfills/_core.ts new file mode 100644 index 0000000000..73a74172fa --- /dev/null +++ b/ext/node/polyfills/_core.ts @@ -0,0 +1,83 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// This module provides an interface to `Deno.core`. For environments +// that don't have access to `Deno.core` some APIs are polyfilled, while +// some are unavailble and throw on call. +// Note: deno_std shouldn't use Deno.core namespace. We should minimize these +// usages. + +import { TextEncoder } from "internal:deno_web/08_text_encoding.js"; + +// deno-lint-ignore no-explicit-any +let DenoCore: any; + +// deno-lint-ignore no-explicit-any +const { Deno } = globalThis as any; + +// @ts-ignore Deno.core is not defined in types +if (Deno?.[Deno.internal]?.core) { + // @ts-ignore Deno[Deno.internal].core is not defined in types + DenoCore = Deno[Deno.internal].core; +} else if (Deno?.core) { + // @ts-ignore Deno.core is not defined in types + DenoCore = Deno.core; +} else { + DenoCore = {}; +} + +export const core = { + runMicrotasks: DenoCore.runMicrotasks ?? function () { + throw new Error( + "Deno.core.runMicrotasks() is not supported in this environment", + ); + }, + setHasTickScheduled: DenoCore.setHasTickScheduled ?? function () { + throw new Error( + "Deno.core.setHasTickScheduled() is not supported in this environment", + ); + }, + hasTickScheduled: DenoCore.hasTickScheduled ?? function () { + throw new Error( + "Deno.core.hasTickScheduled() is not supported in this environment", + ); + }, + setNextTickCallback: DenoCore.setNextTickCallback ?? undefined, + setMacrotaskCallback: DenoCore.setMacrotaskCallback ?? function () { + throw new Error( + "Deno.core.setNextTickCallback() is not supported in this environment", + ); + }, + evalContext: DenoCore.evalContext ?? + function (_code: string, _filename: string) { + throw new Error( + "Deno.core.evalContext is not supported in this environment", + ); + }, + encode: DenoCore.encode ?? function (chunk: string): Uint8Array { + return new TextEncoder().encode(chunk); + }, + eventLoopHasMoreWork: DenoCore.eventLoopHasMoreWork ?? function (): boolean { + return false; + }, + isProxy: DenoCore.isProxy ?? function (): boolean { + return false; + }, + getPromiseDetails: DenoCore.getPromiseDetails ?? + function (_promise: Promise): [number, unknown] { + throw new Error( + "Deno.core.getPromiseDetails is not supported in this environment", + ); + }, + setPromiseHooks: DenoCore.setPromiseHooks ?? function () { + throw new Error( + "Deno.core.setPromiseHooks is not supported in this environment", + ); + }, + ops: DenoCore.ops ?? { + op_napi_open(_filename: string) { + throw new Error( + "Node API is not supported in this environment", + ); + }, + }, +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/README.md b/ext/node/polyfills/_crypto/crypto_browserify/README.md new file mode 100644 index 0000000000..f618a177b7 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/README.md @@ -0,0 +1,2 @@ +This directory contains the libraries ported from +[crypto-browserify](https://github.com/crypto-browserify) organization. diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js new file mode 100644 index 0000000000..cb01476cdd --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js @@ -0,0 +1,167 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import { Reporter } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/reporter.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export function DecoderBuffer(base, options) { + Reporter.call(this, options); + if (!Buffer.isBuffer(base)) { + this.error("Input not Buffer"); + return; + } + + this.base = base; + this.offset = 0; + this.length = base.length; +} +// inherits(DecoderBuffer, Reporter); +DecoderBuffer.prototype = Object.create(Reporter.prototype, { + constructor: { + value: DecoderBuffer, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +DecoderBuffer.isDecoderBuffer = function isDecoderBuffer(data) { + if (data instanceof DecoderBuffer) { + return true; + } + + // Or accept compatible API + const isCompatible = typeof data === "object" && + Buffer.isBuffer(data.base) && + data.constructor.name === "DecoderBuffer" && + typeof data.offset === "number" && + typeof data.length === "number" && + typeof data.save === "function" && + typeof data.restore === "function" && + typeof data.isEmpty === "function" && + typeof data.readUInt8 === "function" && + typeof data.skip === "function" && + typeof data.raw === "function"; + + return isCompatible; +}; + +DecoderBuffer.prototype.save = function save() { + return { offset: this.offset, reporter: Reporter.prototype.save.call(this) }; +}; + +DecoderBuffer.prototype.restore = function restore(save) { + // Return skipped data + const res = new DecoderBuffer(this.base); + res.offset = save.offset; + res.length = this.offset; + + this.offset = save.offset; + Reporter.prototype.restore.call(this, save.reporter); + + return res; +}; + +DecoderBuffer.prototype.isEmpty = function isEmpty() { + return this.offset === this.length; +}; + +DecoderBuffer.prototype.readUInt8 = function readUInt8(fail) { + if (this.offset + 1 <= this.length) { + return this.base.readUInt8(this.offset++, true); + } else { + return this.error(fail || "DecoderBuffer overrun"); + } +}; + +DecoderBuffer.prototype.skip = function skip(bytes, fail) { + if (!(this.offset + bytes <= this.length)) { + return this.error(fail || "DecoderBuffer overrun"); + } + + const res = new DecoderBuffer(this.base); + + // Share reporter state + res._reporterState = this._reporterState; + + res.offset = this.offset; + res.length = this.offset + bytes; + this.offset += bytes; + return res; +}; + +DecoderBuffer.prototype.raw = function raw(save) { + return this.base.slice(save ? save.offset : this.offset, this.length); +}; + +export function EncoderBuffer(value, reporter) { + if (Array.isArray(value)) { + this.length = 0; + this.value = value.map(function (item) { + if (!EncoderBuffer.isEncoderBuffer(item)) { + item = new EncoderBuffer(item, reporter); + } + this.length += item.length; + return item; + }, this); + } else if (typeof value === "number") { + if (!(0 <= value && value <= 0xff)) { + return reporter.error("non-byte EncoderBuffer value"); + } + this.value = value; + this.length = 1; + } else if (typeof value === "string") { + this.value = value; + this.length = Buffer.byteLength(value); + } else if (Buffer.isBuffer(value)) { + this.value = value; + this.length = value.length; + } else { + return reporter.error("Unsupported type: " + typeof value); + } +} + +EncoderBuffer.isEncoderBuffer = function isEncoderBuffer(data) { + if (data instanceof EncoderBuffer) { + return true; + } + + // Or accept compatible API + const isCompatible = typeof data === "object" && + data.constructor.name === "EncoderBuffer" && + typeof data.length === "number" && + typeof data.join === "function"; + + return isCompatible; +}; + +EncoderBuffer.prototype.join = function join(out, offset) { + if (!out) { + out = Buffer.alloc(this.length); + } + if (!offset) { + offset = 0; + } + + if (this.length === 0) { + return out; + } + + if (Array.isArray(this.value)) { + this.value.forEach(function (item) { + item.join(out, offset); + offset += item.length; + }); + } else { + if (typeof this.value === "number") { + out[offset] = this.value; + } else if (typeof this.value === "string") { + out.write(this.value, offset); + } else if (Buffer.isBuffer(this.value)) { + this.value.copy(out, offset); + } + offset += this.length; + } + + return out; +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js new file mode 100644 index 0000000000..027778155e --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js @@ -0,0 +1,734 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import { Reporter } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/reporter.js"; +import { + DecoderBuffer, + EncoderBuffer, +} from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js"; +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; + +// Supported tags +const tags = [ + "seq", + "seqof", + "set", + "setof", + "objid", + "bool", + "gentime", + "utctime", + "null_", + "enum", + "int", + "objDesc", + "bitstr", + "bmpstr", + "charstr", + "genstr", + "graphstr", + "ia5str", + "iso646str", + "numstr", + "octstr", + "printstr", + "t61str", + "unistr", + "utf8str", + "videostr", +]; + +// Public methods list +const methods = [ + "key", + "obj", + "use", + "optional", + "explicit", + "implicit", + "def", + "choice", + "any", + "contains", +].concat(tags); + +// Overrided methods list +const overrided = [ + "_peekTag", + "_decodeTag", + "_use", + "_decodeStr", + "_decodeObjid", + "_decodeTime", + "_decodeNull", + "_decodeInt", + "_decodeBool", + "_decodeList", + + "_encodeComposite", + "_encodeStr", + "_encodeObjid", + "_encodeTime", + "_encodeNull", + "_encodeInt", + "_encodeBool", +]; + +export function Node(enc, parent, name) { + const state = {}; + this._baseState = state; + + state.name = name; + state.enc = enc; + + state.parent = parent || null; + state.children = null; + + // State + state.tag = null; + state.args = null; + state.reverseArgs = null; + state.choice = null; + state.optional = false; + state.any = false; + state.obj = false; + state.use = null; + state.useDecoder = null; + state.key = null; + state["default"] = null; + state.explicit = null; + state.implicit = null; + state.contains = null; + + // Should create new instance on each method + if (!state.parent) { + state.children = []; + this._wrap(); + } +} + +const stateProps = [ + "enc", + "parent", + "children", + "tag", + "args", + "reverseArgs", + "choice", + "optional", + "any", + "obj", + "use", + "alteredUse", + "key", + "default", + "explicit", + "implicit", + "contains", +]; + +Node.prototype.clone = function clone() { + const state = this._baseState; + const cstate = {}; + stateProps.forEach(function (prop) { + cstate[prop] = state[prop]; + }); + const res = new this.constructor(cstate.parent); + res._baseState = cstate; + return res; +}; + +Node.prototype._wrap = function wrap() { + const state = this._baseState; + methods.forEach(function (method) { + this[method] = function _wrappedMethod() { + const clone = new this.constructor(this); + state.children.push(clone); + return clone[method].apply(clone, arguments); + }; + }, this); +}; + +Node.prototype._init = function init(body) { + const state = this._baseState; + + assert(state.parent === null); + body.call(this); + + // Filter children + state.children = state.children.filter(function (child) { + return child._baseState.parent === this; + }, this); + assert(state.children.length === 1, "Root node can have only one child"); +}; + +Node.prototype._useArgs = function useArgs(args) { + const state = this._baseState; + + // Filter children and args + const children = args.filter(function (arg) { + return arg instanceof this.constructor; + }, this); + args = args.filter(function (arg) { + return !(arg instanceof this.constructor); + }, this); + + if (children.length !== 0) { + assert(state.children === null); + state.children = children; + + // Replace parent to maintain backward link + children.forEach(function (child) { + child._baseState.parent = this; + }, this); + } + if (args.length !== 0) { + assert(state.args === null); + state.args = args; + state.reverseArgs = args.map(function (arg) { + if (typeof arg !== "object" || arg.constructor !== Object) { + return arg; + } + + const res = {}; + Object.keys(arg).forEach(function (key) { + if (key == (key | 0)) { + key |= 0; + } + const value = arg[key]; + res[value] = key; + }); + return res; + }); + } +}; + +// +// Overrided methods +// + +overrided.forEach(function (method) { + Node.prototype[method] = function _overrided() { + const state = this._baseState; + throw new Error(method + " not implemented for encoding: " + state.enc); + }; +}); + +// +// Public methods +// + +tags.forEach(function (tag) { + Node.prototype[tag] = function _tagMethod() { + const state = this._baseState; + const args = Array.prototype.slice.call(arguments); + + assert(state.tag === null); + state.tag = tag; + + this._useArgs(args); + + return this; + }; +}); + +Node.prototype.use = function use(item) { + assert(item); + const state = this._baseState; + + assert(state.use === null); + state.use = item; + + return this; +}; + +Node.prototype.optional = function optional() { + const state = this._baseState; + + state.optional = true; + + return this; +}; + +Node.prototype.def = function def(val) { + const state = this._baseState; + + assert(state["default"] === null); + state["default"] = val; + state.optional = true; + + return this; +}; + +Node.prototype.explicit = function explicit(num) { + const state = this._baseState; + + assert(state.explicit === null && state.implicit === null); + state.explicit = num; + + return this; +}; + +Node.prototype.implicit = function implicit(num) { + const state = this._baseState; + + assert(state.explicit === null && state.implicit === null); + state.implicit = num; + + return this; +}; + +Node.prototype.obj = function obj() { + const state = this._baseState; + const args = Array.prototype.slice.call(arguments); + + state.obj = true; + + if (args.length !== 0) { + this._useArgs(args); + } + + return this; +}; + +Node.prototype.key = function key(newKey) { + const state = this._baseState; + + assert(state.key === null); + state.key = newKey; + + return this; +}; + +Node.prototype.any = function any() { + const state = this._baseState; + + state.any = true; + + return this; +}; + +Node.prototype.choice = function choice(obj) { + const state = this._baseState; + + assert(state.choice === null); + state.choice = obj; + this._useArgs( + Object.keys(obj).map(function (key) { + return obj[key]; + }), + ); + + return this; +}; + +Node.prototype.contains = function contains(item) { + const state = this._baseState; + + assert(state.use === null); + state.contains = item; + + return this; +}; + +// +// Decoding +// + +Node.prototype._decode = function decode(input, options) { + const state = this._baseState; + + // Decode root node + if (state.parent === null) { + return input.wrapResult(state.children[0]._decode(input, options)); + } + + let result = state["default"]; + let present = true; + + let prevKey = null; + if (state.key !== null) { + prevKey = input.enterKey(state.key); + } + + // Check if tag is there + if (state.optional) { + let tag = null; + if (state.explicit !== null) { + tag = state.explicit; + } else if (state.implicit !== null) { + tag = state.implicit; + } else if (state.tag !== null) { + tag = state.tag; + } + + if (tag === null && !state.any) { + // Trial and Error + const save = input.save(); + try { + if (state.choice === null) { + this._decodeGeneric(state.tag, input, options); + } else { + this._decodeChoice(input, options); + } + present = true; + } catch (_e) { + present = false; + } + input.restore(save); + } else { + present = this._peekTag(input, tag, state.any); + + if (input.isError(present)) { + return present; + } + } + } + + // Push object on stack + let prevObj; + if (state.obj && present) { + prevObj = input.enterObject(); + } + + if (present) { + // Unwrap explicit values + if (state.explicit !== null) { + const explicit = this._decodeTag(input, state.explicit); + if (input.isError(explicit)) { + return explicit; + } + input = explicit; + } + + const start = input.offset; + + // Unwrap implicit and normal values + if (state.use === null && state.choice === null) { + let save; + if (state.any) { + save = input.save(); + } + const body = this._decodeTag( + input, + state.implicit !== null ? state.implicit : state.tag, + state.any, + ); + if (input.isError(body)) { + return body; + } + + if (state.any) { + result = input.raw(save); + } else { + input = body; + } + } + + if (options && options.track && state.tag !== null) { + options.track(input.path(), start, input.length, "tagged"); + } + + if (options && options.track && state.tag !== null) { + options.track(input.path(), input.offset, input.length, "content"); + } + + // Select proper method for tag + if (state.any) { + // no-op + } else if (state.choice === null) { + result = this._decodeGeneric(state.tag, input, options); + } else { + result = this._decodeChoice(input, options); + } + + if (input.isError(result)) { + return result; + } + + // Decode children + if (!state.any && state.choice === null && state.children !== null) { + state.children.forEach(function decodeChildren(child) { + // NOTE: We are ignoring errors here, to let parser continue with other + // parts of encoded data + child._decode(input, options); + }); + } + + // Decode contained/encoded by schema, only in bit or octet strings + if (state.contains && (state.tag === "octstr" || state.tag === "bitstr")) { + const data = new DecoderBuffer(result); + result = this._getUse(state.contains, input._reporterState.obj) + ._decode(data, options); + } + } + + // Pop object + if (state.obj && present) { + result = input.leaveObject(prevObj); + } + + // Set key + if (state.key !== null && (result !== null || present === true)) { + input.leaveKey(prevKey, state.key, result); + } else if (prevKey !== null) { + input.exitKey(prevKey); + } + + return result; +}; + +Node.prototype._decodeGeneric = function decodeGeneric(tag, input, options) { + const state = this._baseState; + + if (tag === "seq" || tag === "set") { + return null; + } + if (tag === "seqof" || tag === "setof") { + return this._decodeList(input, tag, state.args[0], options); + } else if (/str$/.test(tag)) { + return this._decodeStr(input, tag, options); + } else if (tag === "objid" && state.args) { + return this._decodeObjid(input, state.args[0], state.args[1], options); + } else if (tag === "objid") { + return this._decodeObjid(input, null, null, options); + } else if (tag === "gentime" || tag === "utctime") { + return this._decodeTime(input, tag, options); + } else if (tag === "null_") { + return this._decodeNull(input, options); + } else if (tag === "bool") { + return this._decodeBool(input, options); + } else if (tag === "objDesc") { + return this._decodeStr(input, tag, options); + } else if (tag === "int" || tag === "enum") { + return this._decodeInt(input, state.args && state.args[0], options); + } + + if (state.use !== null) { + return this._getUse(state.use, input._reporterState.obj) + ._decode(input, options); + } else { + return input.error("unknown tag: " + tag); + } +}; + +Node.prototype._getUse = function _getUse(entity, obj) { + const state = this._baseState; + // Create altered use decoder if implicit is set + state.useDecoder = this._use(entity, obj); + assert(state.useDecoder._baseState.parent === null); + state.useDecoder = state.useDecoder._baseState.children[0]; + if (state.implicit !== state.useDecoder._baseState.implicit) { + state.useDecoder = state.useDecoder.clone(); + state.useDecoder._baseState.implicit = state.implicit; + } + return state.useDecoder; +}; + +Node.prototype._decodeChoice = function decodeChoice(input, options) { + const state = this._baseState; + let result = null; + let match = false; + + Object.keys(state.choice).some(function (key) { + const save = input.save(); + const node = state.choice[key]; + try { + const value = node._decode(input, options); + if (input.isError(value)) { + return false; + } + + result = { type: key, value: value }; + match = true; + } catch (_e) { + input.restore(save); + return false; + } + return true; + }, this); + + if (!match) { + return input.error("Choice not matched"); + } + + return result; +}; + +// +// Encoding +// + +Node.prototype._createEncoderBuffer = function createEncoderBuffer(data) { + return new EncoderBuffer(data, this.reporter); +}; + +Node.prototype._encode = function encode(data, reporter, parent) { + const state = this._baseState; + if (state["default"] !== null && state["default"] === data) { + return; + } + + const result = this._encodeValue(data, reporter, parent); + if (result === undefined) { + return; + } + + if (this._skipDefault(result, reporter, parent)) { + return; + } + + return result; +}; + +Node.prototype._encodeValue = function encode(data, reporter, parent) { + const state = this._baseState; + + // Decode root node + if (state.parent === null) { + return state.children[0]._encode(data, reporter || new Reporter()); + } + + let result = null; + + // Set reporter to share it with a child class + this.reporter = reporter; + + // Check if data is there + if (state.optional && data === undefined) { + if (state["default"] !== null) { + data = state["default"]; + } else { + return; + } + } + + // Encode children first + let content = null; + let primitive = false; + if (state.any) { + // Anything that was given is translated to buffer + result = this._createEncoderBuffer(data); + } else if (state.choice) { + result = this._encodeChoice(data, reporter); + } else if (state.contains) { + content = this._getUse(state.contains, parent)._encode(data, reporter); + primitive = true; + } else if (state.children) { + content = state.children.map(function (child) { + if (child._baseState.tag === "null_") { + return child._encode(null, reporter, data); + } + + if (child._baseState.key === null) { + return reporter.error("Child should have a key"); + } + const prevKey = reporter.enterKey(child._baseState.key); + + if (typeof data !== "object") { + return reporter.error("Child expected, but input is not object"); + } + + const res = child._encode(data[child._baseState.key], reporter, data); + reporter.leaveKey(prevKey); + + return res; + }, this).filter(function (child) { + return child; + }); + content = this._createEncoderBuffer(content); + } else { + if (state.tag === "seqof" || state.tag === "setof") { + // TODO(indutny): this should be thrown on DSL level + if (!(state.args && state.args.length === 1)) { + return reporter.error("Too many args for : " + state.tag); + } + + if (!Array.isArray(data)) { + return reporter.error("seqof/setof, but data is not Array"); + } + + const child = this.clone(); + child._baseState.implicit = null; + content = this._createEncoderBuffer(data.map(function (item) { + const state = this._baseState; + + return this._getUse(state.args[0], data)._encode(item, reporter); + }, child)); + } else if (state.use !== null) { + result = this._getUse(state.use, parent)._encode(data, reporter); + } else { + content = this._encodePrimitive(state.tag, data); + primitive = true; + } + } + + // Encode data itself + if (!state.any && state.choice === null) { + const tag = state.implicit !== null ? state.implicit : state.tag; + const cls = state.implicit === null ? "universal" : "context"; + + if (tag === null) { + if (state.use === null) { + reporter.error("Tag could be omitted only for .use()"); + } + } else { + if (state.use === null) { + result = this._encodeComposite(tag, primitive, cls, content); + } + } + } + + // Wrap in explicit + if (state.explicit !== null) { + result = this._encodeComposite(state.explicit, false, "context", result); + } + + return result; +}; + +Node.prototype._encodeChoice = function encodeChoice(data, reporter) { + const state = this._baseState; + + const node = state.choice[data.type]; + if (!node) { + assert( + false, + data.type + " not found in " + + JSON.stringify(Object.keys(state.choice)), + ); + } + return node._encode(data.value, reporter); +}; + +Node.prototype._encodePrimitive = function encodePrimitive(tag, data) { + const state = this._baseState; + + if (/str$/.test(tag)) { + return this._encodeStr(data, tag); + } else if (tag === "objid" && state.args) { + return this._encodeObjid(data, state.reverseArgs[0], state.args[1]); + } else if (tag === "objid") { + return this._encodeObjid(data, null, null); + } else if (tag === "gentime" || tag === "utctime") { + return this._encodeTime(data, tag); + } else if (tag === "null_") { + return this._encodeNull(); + } else if (tag === "int" || tag === "enum") { + return this._encodeInt(data, state.args && state.reverseArgs[0]); + } else if (tag === "bool") { + return this._encodeBool(data); + } else if (tag === "objDesc") { + return this._encodeStr(data, tag); + } else { + throw new Error("Unsupported tag: " + tag); + } +}; + +Node.prototype._isNumstr = function isNumstr(str) { + return /^[0-9 ]*$/.test(str); +}; + +Node.prototype._isPrintstr = function isPrintstr(str) { + return /^[A-Za-z0-9 '()+,-./:=?]*$/.test(str); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/reporter.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/reporter.js new file mode 100644 index 0000000000..509f6c5b3e --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/base/reporter.js @@ -0,0 +1,138 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +export function Reporter(options) { + this._reporterState = { + obj: null, + path: [], + options: options || {}, + errors: [], + }; +} + +Reporter.prototype.isError = function isError(obj) { + return obj instanceof ReporterError; +}; + +Reporter.prototype.save = function save() { + const state = this._reporterState; + + return { obj: state.obj, pathLen: state.path.length }; +}; + +Reporter.prototype.restore = function restore(data) { + const state = this._reporterState; + + state.obj = data.obj; + state.path = state.path.slice(0, data.pathLen); +}; + +Reporter.prototype.enterKey = function enterKey(key) { + return this._reporterState.path.push(key); +}; + +Reporter.prototype.exitKey = function exitKey(index) { + const state = this._reporterState; + + state.path = state.path.slice(0, index - 1); +}; + +Reporter.prototype.leaveKey = function leaveKey(index, key, value) { + const state = this._reporterState; + + this.exitKey(index); + if (state.obj !== null) { + state.obj[key] = value; + } +}; + +Reporter.prototype.path = function path() { + return this._reporterState.path.join("/"); +}; + +Reporter.prototype.enterObject = function enterObject() { + const state = this._reporterState; + + const prev = state.obj; + state.obj = {}; + return prev; +}; + +Reporter.prototype.leaveObject = function leaveObject(prev) { + const state = this._reporterState; + + const now = state.obj; + state.obj = prev; + return now; +}; + +Reporter.prototype.error = function error(msg) { + let err; + const state = this._reporterState; + + const inherited = msg instanceof ReporterError; + if (inherited) { + err = msg; + } else { + err = new ReporterError( + state.path.map(function (elem) { + return "[" + JSON.stringify(elem) + "]"; + }).join(""), + msg.message || msg, + msg.stack, + ); + } + + if (!state.options.partial) { + throw err; + } + + if (!inherited) { + state.errors.push(err); + } + + return err; +}; + +Reporter.prototype.wrapResult = function wrapResult(result) { + const state = this._reporterState; + if (!state.options.partial) { + return result; + } + + return { + result: this.isError(result) ? null : result, + errors: state.errors, + }; +}; + +function ReporterError(path, msg) { + this.path = path; + this.rethrow(msg); +} +// inherits(ReporterError, Error); +ReporterError.prototype = Object.create(Error.prototype, { + constructor: { + value: ReporterError, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +ReporterError.prototype.rethrow = function rethrow(msg) { + this.message = msg + " at: " + (this.path || "(shallow)"); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, ReporterError); + } + + if (!this.stack) { + try { + // IE only adds stack when thrown + throw new Error(this.message); + } catch (e) { + this.stack = e.stack; + } + } + return this; +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js new file mode 100644 index 0000000000..807a978641 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js @@ -0,0 +1,60 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +// Helper +function reverse(map) { + const res = {}; + + Object.keys(map).forEach(function (key) { + // Convert key to integer if it is stringified + if ((key | 0) == key) { + key = key | 0; + } + + const value = map[key]; + res[value] = key; + }); + + return res; +} + +export const tagClass = { + 0: "universal", + 1: "application", + 2: "context", + 3: "private", +}; +export const tagClassByName = reverse(tagClass); + +export const tag = { + 0x00: "end", + 0x01: "bool", + 0x02: "int", + 0x03: "bitstr", + 0x04: "octstr", + 0x05: "null_", + 0x06: "objid", + 0x07: "objDesc", + 0x08: "external", + 0x09: "real", + 0x0a: "enum", + 0x0b: "embed", + 0x0c: "utf8str", + 0x0d: "relativeOid", + 0x10: "seq", + 0x11: "set", + 0x12: "numstr", + 0x13: "printstr", + 0x14: "t61str", + 0x15: "videostr", + 0x16: "ia5str", + 0x17: "utctime", + 0x18: "gentime", + 0x19: "graphstr", + 0x1a: "iso646str", + 0x1b: "genstr", + 0x1c: "unistr", + 0x1d: "charstr", + 0x1e: "bmpstr", +}; +export const tagByName = reverse(tag); diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js new file mode 100644 index 0000000000..9ab8115078 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js @@ -0,0 +1,386 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import bignum from "internal:deno_node/polyfills/_crypto/crypto_browserify/bn.js/bn.js"; +import { DecoderBuffer } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js"; +import { Node } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js"; +import * as der from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js"; + +export function DERDecoder(entity) { + this.enc = "der"; + this.name = entity.name; + this.entity = entity; + + // Construct base tree + this.tree = new DERNode(); + this.tree._init(entity.body); +} + +DERDecoder.prototype.decode = function decode(data, options) { + if (!DecoderBuffer.isDecoderBuffer(data)) { + data = new DecoderBuffer(data, options); + } + return this.tree._decode(data, options); +}; + +// Tree methods + +function DERNode(parent) { + Node.call(this, "der", parent); +} +// inherits(DERNode, Node); +DERNode.prototype = Object.create(Node.prototype, { + constructor: { + value: DERNode, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +DERNode.prototype._peekTag = function peekTag(buffer, tag, any) { + if (buffer.isEmpty()) { + return false; + } + + const state = buffer.save(); + const decodedTag = derDecodeTag(buffer, 'Failed to peek tag: "' + tag + '"'); + if (buffer.isError(decodedTag)) { + return decodedTag; + } + + buffer.restore(state); + + return decodedTag.tag === tag || decodedTag.tagStr === tag || + (decodedTag.tagStr + "of") === tag || any; +}; + +DERNode.prototype._decodeTag = function decodeTag(buffer, tag, any) { + const decodedTag = derDecodeTag( + buffer, + 'Failed to decode tag of "' + tag + '"', + ); + if (buffer.isError(decodedTag)) { + return decodedTag; + } + + let len = derDecodeLen( + buffer, + decodedTag.primitive, + 'Failed to get length of "' + tag + '"', + ); + + // Failure + if (buffer.isError(len)) { + return len; + } + + if ( + !any && + decodedTag.tag !== tag && + decodedTag.tagStr !== tag && + decodedTag.tagStr + "of" !== tag + ) { + return buffer.error('Failed to match tag: "' + tag + '"'); + } + + if (decodedTag.primitive || len !== null) { + return buffer.skip(len, 'Failed to match body of: "' + tag + '"'); + } + + // Indefinite length... find END tag + const state = buffer.save(); + const res = this._skipUntilEnd( + buffer, + 'Failed to skip indefinite length body: "' + this.tag + '"', + ); + if (buffer.isError(res)) { + return res; + } + + len = buffer.offset - state.offset; + buffer.restore(state); + return buffer.skip(len, 'Failed to match body of: "' + tag + '"'); +}; + +DERNode.prototype._skipUntilEnd = function skipUntilEnd(buffer, fail) { + for (;;) { + const tag = derDecodeTag(buffer, fail); + if (buffer.isError(tag)) { + return tag; + } + const len = derDecodeLen(buffer, tag.primitive, fail); + if (buffer.isError(len)) { + return len; + } + + let res; + if (tag.primitive || len !== null) { + res = buffer.skip(len); + } else { + res = this._skipUntilEnd(buffer, fail); + } + + // Failure + if (buffer.isError(res)) { + return res; + } + + if (tag.tagStr === "end") { + break; + } + } +}; + +DERNode.prototype._decodeList = function decodeList( + buffer, + _tag, + decoder, + options, +) { + const result = []; + while (!buffer.isEmpty()) { + const possibleEnd = this._peekTag(buffer, "end"); + if (buffer.isError(possibleEnd)) { + return possibleEnd; + } + + const res = decoder.decode(buffer, "der", options); + if (buffer.isError(res) && possibleEnd) { + break; + } + result.push(res); + } + return result; +}; + +DERNode.prototype._decodeStr = function decodeStr(buffer, tag) { + if (tag === "bitstr") { + const unused = buffer.readUInt8(); + if (buffer.isError(unused)) { + return unused; + } + return { unused: unused, data: buffer.raw() }; + } else if (tag === "bmpstr") { + const raw = buffer.raw(); + if (raw.length % 2 === 1) { + return buffer.error("Decoding of string type: bmpstr length mismatch"); + } + + let str = ""; + for (let i = 0; i < raw.length / 2; i++) { + str += String.fromCharCode(raw.readUInt16BE(i * 2)); + } + return str; + } else if (tag === "numstr") { + const numstr = buffer.raw().toString("ascii"); + if (!this._isNumstr(numstr)) { + return buffer.error( + "Decoding of string type: " + + "numstr unsupported characters", + ); + } + return numstr; + } else if (tag === "octstr") { + return buffer.raw(); + } else if (tag === "objDesc") { + return buffer.raw(); + } else if (tag === "printstr") { + const printstr = buffer.raw().toString("ascii"); + if (!this._isPrintstr(printstr)) { + return buffer.error( + "Decoding of string type: " + + "printstr unsupported characters", + ); + } + return printstr; + } else if (/str$/.test(tag)) { + return buffer.raw().toString(); + } else { + return buffer.error("Decoding of string type: " + tag + " unsupported"); + } +}; + +DERNode.prototype._decodeObjid = function decodeObjid( + buffer, + values, + relative, +) { + let result; + const identifiers = []; + let ident = 0; + let subident = 0; + while (!buffer.isEmpty()) { + subident = buffer.readUInt8(); + ident <<= 7; + ident |= subident & 0x7f; + if ((subident & 0x80) === 0) { + identifiers.push(ident); + ident = 0; + } + } + if (subident & 0x80) { + identifiers.push(ident); + } + + const first = (identifiers[0] / 40) | 0; + const second = identifiers[0] % 40; + + if (relative) { + result = identifiers; + } else { + result = [first, second].concat(identifiers.slice(1)); + } + + if (values) { + let tmp = values[result.join(" ")]; + if (tmp === undefined) { + tmp = values[result.join(".")]; + } + if (tmp !== undefined) { + result = tmp; + } + } + + return result; +}; + +DERNode.prototype._decodeTime = function decodeTime(buffer, tag) { + const str = buffer.raw().toString(); + + let year; + let mon; + let day; + let hour; + let min; + let sec; + if (tag === "gentime") { + year = str.slice(0, 4) | 0; + mon = str.slice(4, 6) | 0; + day = str.slice(6, 8) | 0; + hour = str.slice(8, 10) | 0; + min = str.slice(10, 12) | 0; + sec = str.slice(12, 14) | 0; + } else if (tag === "utctime") { + year = str.slice(0, 2) | 0; + mon = str.slice(2, 4) | 0; + day = str.slice(4, 6) | 0; + hour = str.slice(6, 8) | 0; + min = str.slice(8, 10) | 0; + sec = str.slice(10, 12) | 0; + if (year < 70) { + year = 2000 + year; + } else { + year = 1900 + year; + } + } else { + return buffer.error("Decoding " + tag + " time is not supported yet"); + } + + return Date.UTC(year, mon - 1, day, hour, min, sec, 0); +}; + +DERNode.prototype._decodeNull = function decodeNull() { + return null; +}; + +DERNode.prototype._decodeBool = function decodeBool(buffer) { + const res = buffer.readUInt8(); + if (buffer.isError(res)) { + return res; + } else { + return res !== 0; + } +}; + +DERNode.prototype._decodeInt = function decodeInt(buffer, values) { + // Bigint, return as it is (assume big endian) + const raw = buffer.raw(); + let res = new bignum(raw); + + if (values) { + res = values[res.toString(10)] || res; + } + + return res; +}; + +DERNode.prototype._use = function use(entity, obj) { + if (typeof entity === "function") { + entity = entity(obj); + } + return entity._getDecoder("der").tree; +}; + +// Utility methods + +function derDecodeTag(buf, fail) { + let tag = buf.readUInt8(fail); + if (buf.isError(tag)) { + return tag; + } + + const cls = der.tagClass[tag >> 6]; + const primitive = (tag & 0x20) === 0; + + // Multi-octet tag - load + if ((tag & 0x1f) === 0x1f) { + let oct = tag; + tag = 0; + while ((oct & 0x80) === 0x80) { + oct = buf.readUInt8(fail); + if (buf.isError(oct)) { + return oct; + } + + tag <<= 7; + tag |= oct & 0x7f; + } + } else { + tag &= 0x1f; + } + const tagStr = der.tag[tag]; + + return { + cls: cls, + primitive: primitive, + tag: tag, + tagStr: tagStr, + }; +} + +function derDecodeLen(buf, primitive, fail) { + let len = buf.readUInt8(fail); + if (buf.isError(len)) { + return len; + } + + // Indefinite form + if (!primitive && len === 0x80) { + return null; + } + + // Definite form + if ((len & 0x80) === 0) { + // Short form + return len; + } + + // Long form + const num = len & 0x7f; + if (num > 4) { + return buf.error("length octect is too long"); + } + + len = 0; + for (let i = 0; i < num; i++) { + len <<= 8; + const j = buf.readUInt8(fail); + if (buf.isError(j)) { + return j; + } + len |= j; + } + + return len; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js new file mode 100644 index 0000000000..3dedfb2939 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js @@ -0,0 +1,63 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +import { DERDecoder } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js"; + +export function PEMDecoder(entity) { + DERDecoder.call(this, entity); + this.enc = "pem"; +} +// inherits(PEMDecoder, DERDecoder); +PEMDecoder.prototype = Object.create(DERDecoder.prototype, { + constructor: { + value: PEMDecoder, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +PEMDecoder.prototype.decode = function decode(data, options) { + const lines = data.toString().split(/[\r\n]+/g); + + const label = options.label.toUpperCase(); + + const re = /^-----(BEGIN|END) ([^-]+)-----$/; + let start = -1; + let end = -1; + for (let i = 0; i < lines.length; i++) { + const match = lines[i].match(re); + if (match === null) { + continue; + } + + if (match[2] !== label) { + continue; + } + + if (start === -1) { + if (match[1] !== "BEGIN") { + break; + } + start = i; + } else { + if (match[1] !== "END") { + break; + } + end = i; + break; + } + } + if (start === -1 || end === -1) { + throw new Error("PEM section not found for: " + label); + } + + const base64 = lines.slice(start + 1, end).join(""); + // Remove excessive symbols + base64.replace(/[^a-z0-9+/=]+/gi, ""); + + const input = Buffer.from(base64, "base64"); + return DERDecoder.prototype.decode.call(this, input, options); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/der.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/der.js new file mode 100644 index 0000000000..3f03ef347d --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/der.js @@ -0,0 +1,348 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { Node } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js"; + +// Import DER constants +import * as der from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js"; + +export function DEREncoder(entity) { + this.enc = "der"; + this.name = entity.name; + this.entity = entity; + + // Construct base tree + this.tree = new DERNode(); + this.tree._init(entity.body); +} + +DEREncoder.prototype.encode = function encode(data, reporter) { + return this.tree._encode(data, reporter).join(); +}; + +// Tree methods + +function DERNode(parent) { + Node.call(this, "der", parent); +} +// inherits(DERNode, Node); +DERNode.prototype = Object.create(Node.prototype, { + constructor: { + value: DERNode, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +DERNode.prototype._encodeComposite = function encodeComposite( + tag, + primitive, + cls, + content, +) { + const encodedTag = encodeTag(tag, primitive, cls, this.reporter); + + // Short form + if (content.length < 0x80) { + const header = Buffer.alloc(2); + header[0] = encodedTag; + header[1] = content.length; + return this._createEncoderBuffer([header, content]); + } + + // Long form + // Count octets required to store length + let lenOctets = 1; + for (let i = content.length; i >= 0x100; i >>= 8) { + lenOctets++; + } + + const header = Buffer.alloc(1 + 1 + lenOctets); + header[0] = encodedTag; + header[1] = 0x80 | lenOctets; + + for (let i = 1 + lenOctets, j = content.length; j > 0; i--, j >>= 8) { + header[i] = j & 0xff; + } + + return this._createEncoderBuffer([header, content]); +}; + +DERNode.prototype._encodeStr = function encodeStr(str, tag) { + if (tag === "bitstr") { + return this._createEncoderBuffer([str.unused | 0, str.data]); + } else if (tag === "bmpstr") { + const buf = Buffer.alloc(str.length * 2); + for (let i = 0; i < str.length; i++) { + buf.writeUInt16BE(str.charCodeAt(i), i * 2); + } + return this._createEncoderBuffer(buf); + } else if (tag === "numstr") { + if (!this._isNumstr(str)) { + return this.reporter.error( + "Encoding of string type: numstr supports " + + "only digits and space", + ); + } + return this._createEncoderBuffer(str); + } else if (tag === "printstr") { + if (!this._isPrintstr(str)) { + return this.reporter.error( + "Encoding of string type: printstr supports " + + "only latin upper and lower case letters, " + + "digits, space, apostrophe, left and rigth " + + "parenthesis, plus sign, comma, hyphen, " + + "dot, slash, colon, equal sign, " + + "question mark", + ); + } + return this._createEncoderBuffer(str); + } else if (/str$/.test(tag)) { + return this._createEncoderBuffer(str); + } else if (tag === "objDesc") { + return this._createEncoderBuffer(str); + } else { + return this.reporter.error( + "Encoding of string type: " + tag + + " unsupported", + ); + } +}; + +DERNode.prototype._encodeObjid = function encodeObjid(id, values, relative) { + if (typeof id === "string") { + if (!values) { + return this.reporter.error("string objid given, but no values map found"); + } + // deno-lint-ignore no-prototype-builtins + if (!values.hasOwnProperty(id)) { + return this.reporter.error("objid not found in values map"); + } + id = values[id].split(/[\s.]+/g); + for (let i = 0; i < id.length; i++) { + id[i] |= 0; + } + } else if (Array.isArray(id)) { + id = id.slice(); + for (let i = 0; i < id.length; i++) { + id[i] |= 0; + } + } + + if (!Array.isArray(id)) { + return this.reporter.error( + "objid() should be either array or string, " + + "got: " + JSON.stringify(id), + ); + } + + if (!relative) { + if (id[1] >= 40) { + return this.reporter.error("Second objid identifier OOB"); + } + id.splice(0, 2, id[0] * 40 + id[1]); + } + + // Count number of octets + let size = 0; + for (let i = 0; i < id.length; i++) { + let ident = id[i]; + for (size++; ident >= 0x80; ident >>= 7) { + size++; + } + } + + const objid = Buffer.alloc(size); + let offset = objid.length - 1; + for (let i = id.length - 1; i >= 0; i--) { + let ident = id[i]; + objid[offset--] = ident & 0x7f; + while ((ident >>= 7) > 0) { + objid[offset--] = 0x80 | (ident & 0x7f); + } + } + + return this._createEncoderBuffer(objid); +}; + +function two(num) { + if (num < 10) { + return "0" + num; + } else { + return num; + } +} + +DERNode.prototype._encodeTime = function encodeTime(time, tag) { + let str; + const date = new Date(time); + + if (tag === "gentime") { + str = [ + two(date.getUTCFullYear()), + two(date.getUTCMonth() + 1), + two(date.getUTCDate()), + two(date.getUTCHours()), + two(date.getUTCMinutes()), + two(date.getUTCSeconds()), + "Z", + ].join(""); + } else if (tag === "utctime") { + str = [ + two(date.getUTCFullYear() % 100), + two(date.getUTCMonth() + 1), + two(date.getUTCDate()), + two(date.getUTCHours()), + two(date.getUTCMinutes()), + two(date.getUTCSeconds()), + "Z", + ].join(""); + } else { + this.reporter.error("Encoding " + tag + " time is not supported yet"); + } + + return this._encodeStr(str, "octstr"); +}; + +DERNode.prototype._encodeNull = function encodeNull() { + return this._createEncoderBuffer(""); +}; + +DERNode.prototype._encodeInt = function encodeInt(num, values) { + if (typeof num === "string") { + if (!values) { + return this.reporter.error("String int or enum given, but no values map"); + } + // deno-lint-ignore no-prototype-builtins + if (!values.hasOwnProperty(num)) { + return this.reporter.error( + "Values map doesn't contain: " + + JSON.stringify(num), + ); + } + num = values[num]; + } + + // Bignum, assume big endian + if (typeof num !== "number" && !Buffer.isBuffer(num)) { + const numArray = num.toArray(); + if (!num.sign && numArray[0] & 0x80) { + numArray.unshift(0); + } + num = Buffer.from(numArray); + } + + if (Buffer.isBuffer(num)) { + let size = num.length; + if (num.length === 0) { + size++; + } + + const out = Buffer.alloc(size); + num.copy(out); + if (num.length === 0) { + out[0] = 0; + } + return this._createEncoderBuffer(out); + } + + if (num < 0x80) { + return this._createEncoderBuffer(num); + } + + if (num < 0x100) { + return this._createEncoderBuffer([0, num]); + } + + let size = 1; + for (let i = num; i >= 0x100; i >>= 8) { + size++; + } + + const out = new Array(size); + for (let i = out.length - 1; i >= 0; i--) { + out[i] = num & 0xff; + num >>= 8; + } + if (out[0] & 0x80) { + out.unshift(0); + } + + return this._createEncoderBuffer(Buffer.from(out)); +}; + +DERNode.prototype._encodeBool = function encodeBool(value) { + return this._createEncoderBuffer(value ? 0xff : 0); +}; + +DERNode.prototype._use = function use(entity, obj) { + if (typeof entity === "function") { + entity = entity(obj); + } + return entity._getEncoder("der").tree; +}; + +DERNode.prototype._skipDefault = function skipDefault( + dataBuffer, + reporter, + parent, +) { + const state = this._baseState; + let i; + if (state["default"] === null) { + return false; + } + + const data = dataBuffer.join(); + if (state.defaultBuffer === undefined) { + state.defaultBuffer = this._encodeValue(state["default"], reporter, parent) + .join(); + } + + if (data.length !== state.defaultBuffer.length) { + return false; + } + + for (i = 0; i < data.length; i++) { + if (data[i] !== state.defaultBuffer[i]) { + return false; + } + } + + return true; +}; + +// Utility methods + +function encodeTag(tag, primitive, cls, reporter) { + let res; + + if (tag === "seqof") { + tag = "seq"; + } else if (tag === "setof") { + tag = "set"; + } + + // deno-lint-ignore no-prototype-builtins + if (der.tagByName.hasOwnProperty(tag)) { + res = der.tagByName[tag]; + } else if (typeof tag === "number" && (tag | 0) === tag) { + res = tag; + } else { + return reporter.error("Unknown tag: " + tag); + } + + if (res >= 0x1f) { + return reporter.error("Multi-octet tag encoding unsupported"); + } + + if (!primitive) { + res |= 0x20; + } + + res |= der.tagClassByName[cls || "universal"] << 6; + + return res; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/pem.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/pem.js new file mode 100644 index 0000000000..d2487e1ced --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/pem.js @@ -0,0 +1,30 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import { DEREncoder } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/der.js"; + +export function PEMEncoder(entity) { + DEREncoder.call(this, entity); + this.enc = "pem"; +} +// inherits(PEMEncoder, DEREncoder); +PEMEncoder.prototype = Object.create(DEREncoder.prototype, { + constructor: { + value: PEMEncoder, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +PEMEncoder.prototype.encode = function encode(data, options) { + const buf = DEREncoder.prototype.encode.call(this, data); + + const p = buf.toString("base64"); + const out = ["-----BEGIN " + options.label + "-----"]; + for (let i = 0; i < p.length; i += 64) { + out.push(p.slice(i, i + 64)); + } + out.push("-----END " + options.label + "-----"); + return out.join("\n"); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/mod.js b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/mod.js new file mode 100644 index 0000000000..23cf79ca00 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/asn1.js/mod.js @@ -0,0 +1,96 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Fedor Indutny. All rights reserved. MIT license. + +import bignum from "internal:deno_node/polyfills/_crypto/crypto_browserify/bn.js/bn.js"; +import { Node } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/node.js"; +import { + DecoderBuffer, + EncoderBuffer, +} from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/buffer.js"; +import { Reporter } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/base/reporter.js"; +import { DEREncoder } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/der.js"; +import { PEMEncoder } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/encoders/pem.js"; +import { DERDecoder } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/der.js"; +import { PEMDecoder } from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/decoders/pem.js"; +import * as der from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/constants/der.js"; + +export const base = { + DecoderBuffer, + EncoderBuffer, + Node, + Reporter, +}; +export const encoders = { der: DEREncoder, pem: PEMEncoder }; +export const decoders = { der: DERDecoder, pem: PEMDecoder }; +export const constants = { der }; +export { bignum }; + +export function define(name, body) { + return new Entity(name, body); +} + +function Entity(name, body) { + this.name = name; + this.body = body; + + this.decoders = {}; + this.encoders = {}; +} + +Entity.prototype._createNamed = function createNamed(Base) { + const name = this.name; + + function Generated(entity) { + this._initNamed(entity, name); + } + // inherits(Generated, Base); + Generated.prototype = Object.create(Base.prototype, { + constructor: { + value: Generated, + enumerable: false, + writable: true, + configurable: true, + }, + }); + Generated.prototype._initNamed = function _initNamed(entity, name) { + Base.call(this, entity, name); + }; + return new Generated(this); +}; + +Entity.prototype._getDecoder = function _getDecoder(enc) { + enc = enc || "der"; + // Lazily create decoder + // deno-lint-ignore no-prototype-builtins + if (!this.decoders.hasOwnProperty(enc)) { + this.decoders[enc] = this._createNamed(decoders[enc]); + } + return this.decoders[enc]; +}; + +Entity.prototype.decode = function decode(data, enc, options) { + return this._getDecoder(enc).decode(data, options); +}; + +Entity.prototype._getEncoder = function _getEncoder(enc) { + enc = enc || "der"; + // Lazily create encoder + // deno-lint-ignore no-prototype-builtins + if (!this.encoders.hasOwnProperty(enc)) { + this.encoders[enc] = this._createNamed(encoders[enc]); + } + return this.encoders[enc]; +}; + +Entity.prototype.encode = function encode(data, enc, /* internal */ reporter) { + return this._getEncoder(enc).encode(data, reporter); +}; + +export default { + base, + bignum, + constants, + decoders, + define, + encoders, +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/bn.js/bn.js b/ext/node/polyfills/_crypto/crypto_browserify/bn.js/bn.js new file mode 100644 index 0000000000..4e9d1187ec --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/bn.js/bn.js @@ -0,0 +1,3605 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2015 Fedor Indutny. All rights reserved. MIT license. +// deno-lint-ignore-file no-var no-inner-declarations no-this-alias no-unused-vars + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +// Utils +function assert(val, msg) { + if (!val) throw new Error(msg || "Assertion failed"); +} + +// Could use `inherits` module, but don't want to move from single file +// architecture yet. +function inherits(ctor, superCtor) { + ctor.super_ = superCtor; + var TempCtor = function () {}; + TempCtor.prototype = superCtor.prototype; + ctor.prototype = new TempCtor(); + ctor.prototype.constructor = ctor; +} + +// BN +export function BN(number, base, endian) { + if (BN.isBN(number)) { + return number; + } + + this.negative = 0; + this.words = null; + this.length = 0; + + // Reduction context + this.red = null; + + if (number !== null) { + if (base === "le" || base === "be") { + endian = base; + base = 10; + } + + this._init(number || 0, base || 10, endian || "be"); + } +} +export default BN; + +BN.BN = BN; +BN.wordSize = 26; + +BN.isBN = function isBN(num) { + if (num instanceof BN) { + return true; + } + + return num !== null && typeof num === "object" && + num.constructor.wordSize === BN.wordSize && Array.isArray(num.words); +}; + +BN.max = function max(left, right) { + if (left.cmp(right) > 0) return left; + return right; +}; + +BN.min = function min(left, right) { + if (left.cmp(right) < 0) return left; + return right; +}; + +BN.prototype._init = function init(number, base, endian) { + if (typeof number === "number") { + return this._initNumber(number, base, endian); + } + + if (typeof number === "object") { + return this._initArray(number, base, endian); + } + + if (base === "hex") { + base = 16; + } + assert(base === (base | 0) && base >= 2 && base <= 36); + + number = number.toString().replace(/\s+/g, ""); + var start = 0; + if (number[0] === "-") { + start++; + this.negative = 1; + } + + if (start < number.length) { + if (base === 16) { + this._parseHex(number, start, endian); + } else { + this._parseBase(number, base, start); + if (endian === "le") { + this._initArray(this.toArray(), base, endian); + } + } + } +}; + +BN.prototype._initNumber = function _initNumber(number, base, endian) { + if (number < 0) { + this.negative = 1; + number = -number; + } + if (number < 0x4000000) { + this.words = [number & 0x3ffffff]; + this.length = 1; + } else if (number < 0x10000000000000) { + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff, + ]; + this.length = 2; + } else { + assert(number < 0x20000000000000); // 2 ^ 53 (unsafe) + this.words = [ + number & 0x3ffffff, + (number / 0x4000000) & 0x3ffffff, + 1, + ]; + this.length = 3; + } + + if (endian !== "le") return; + + // Reverse the bytes + this._initArray(this.toArray(), base, endian); +}; + +BN.prototype._initArray = function _initArray(number, base, endian) { + // Perhaps a Uint8Array + assert(typeof number.length === "number"); + if (number.length <= 0) { + this.words = [0]; + this.length = 1; + return this; + } + + this.length = Math.ceil(number.length / 3); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + var j, w; + var off = 0; + if (endian === "be") { + for (i = number.length - 1, j = 0; i >= 0; i -= 3) { + w = number[i] | (number[i - 1] << 8) | (number[i - 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } else if (endian === "le") { + for (i = 0, j = 0; i < number.length; i += 3) { + w = number[i] | (number[i + 1] << 8) | (number[i + 2] << 16); + this.words[j] |= (w << off) & 0x3ffffff; + this.words[j + 1] = (w >>> (26 - off)) & 0x3ffffff; + off += 24; + if (off >= 26) { + off -= 26; + j++; + } + } + } + return this._strip(); +}; + +function parseHex4Bits(string, index) { + var c = string.charCodeAt(index); + // '0' - '9' + if (c >= 48 && c <= 57) { + return c - 48; + // 'A' - 'F' + } else if (c >= 65 && c <= 70) { + return c - 55; + // 'a' - 'f' + } else if (c >= 97 && c <= 102) { + return c - 87; + } else { + assert(false, "Invalid character in " + string); + } +} + +function parseHexByte(string, lowerBound, index) { + var r = parseHex4Bits(string, index); + if (index - 1 >= lowerBound) { + r |= parseHex4Bits(string, index - 1) << 4; + } + return r; +} + +BN.prototype._parseHex = function _parseHex(number, start, endian) { + // Create possibly bigger array to ensure that it fits the number + this.length = Math.ceil((number.length - start) / 6); + this.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + this.words[i] = 0; + } + + // 24-bits chunks + var off = 0; + var j = 0; + + var w; + if (endian === "be") { + for (i = number.length - 1; i >= start; i -= 2) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + off -= 18; + j += 1; + this.words[j] |= w >>> 26; + } else { + off += 8; + } + } + } else { + var parseLength = number.length - start; + for ( + i = parseLength % 2 === 0 ? start + 1 : start; + i < number.length; + i += 2 + ) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + off -= 18; + j += 1; + this.words[j] |= w >>> 26; + } else { + off += 8; + } + } + } + + this._strip(); +}; + +function parseBase(str, start, end, mul) { + var r = 0; + var b = 0; + var len = Math.min(str.length, end); + for (var i = start; i < len; i++) { + var c = str.charCodeAt(i) - 48; + + r *= mul; + + // 'a' + if (c >= 49) { + b = c - 49 + 0xa; + + // 'A' + } else if (c >= 17) { + b = c - 17 + 0xa; + + // '0' - '9' + } else { + b = c; + } + assert(c >= 0 && b < mul, "Invalid character"); + r += b; + } + return r; +} + +BN.prototype._parseBase = function _parseBase(number, base, start) { + // Initialize as zero + this.words = [0]; + this.length = 1; + + // Find length of limb in base + for (var limbLen = 0, limbPow = 1; limbPow <= 0x3ffffff; limbPow *= base) { + limbLen++; + } + limbLen--; + limbPow = (limbPow / base) | 0; + + var total = number.length - start; + var mod = total % limbLen; + var end = Math.min(total, total - mod) + start; + + var word = 0; + for (var i = start; i < end; i += limbLen) { + word = parseBase(number, i, i + limbLen, base); + + this.imuln(limbPow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + + if (mod !== 0) { + var pow = 1; + word = parseBase(number, i, number.length, base); + + for (i = 0; i < mod; i++) { + pow *= base; + } + + this.imuln(pow); + if (this.words[0] + word < 0x4000000) { + this.words[0] += word; + } else { + this._iaddn(word); + } + } + + this._strip(); +}; + +BN.prototype.copy = function copy(dest) { + dest.words = new Array(this.length); + for (var i = 0; i < this.length; i++) { + dest.words[i] = this.words[i]; + } + dest.length = this.length; + dest.negative = this.negative; + dest.red = this.red; +}; + +function move(dest, src) { + dest.words = src.words; + dest.length = src.length; + dest.negative = src.negative; + dest.red = src.red; +} + +BN.prototype._move = function _move(dest) { + move(dest, this); +}; + +BN.prototype.clone = function clone() { + var r = new BN(null); + this.copy(r); + return r; +}; + +BN.prototype._expand = function _expand(size) { + while (this.length < size) { + this.words[this.length++] = 0; + } + return this; +}; + +// Remove leading `0` from `this` +BN.prototype._strip = function strip() { + while (this.length > 1 && this.words[this.length - 1] === 0) { + this.length--; + } + return this._normSign(); +}; + +BN.prototype._normSign = function _normSign() { + // -0 = 0 + if (this.length === 1 && this.words[0] === 0) { + this.negative = 0; + } + return this; +}; + +// Check Symbol.for because not everywhere where Symbol defined +// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Browser_compatibility +if (typeof Symbol !== "undefined" && typeof Symbol.for === "function") { + try { + BN.prototype[Symbol.for("nodejs.util.inspect.custom")] = inspect; + } catch (e) { + BN.prototype.inspect = inspect; + } +} else { + BN.prototype.inspect = inspect; +} + +function inspect() { + return (this.red ? ""; +} + +/* + + var zeros = []; + var groupSizes = []; + var groupBases = []; + + var s = ''; + var i = -1; + while (++i < BN.wordSize) { + zeros[i] = s; + s += '0'; + } + groupSizes[0] = 0; + groupSizes[1] = 0; + groupBases[0] = 0; + groupBases[1] = 0; + var base = 2 - 1; + while (++base < 36 + 1) { + var groupSize = 0; + var groupBase = 1; + while (groupBase < (1 << BN.wordSize) / base) { + groupBase *= base; + groupSize += 1; + } + groupSizes[base] = groupSize; + groupBases[base] = groupBase; + } + + */ + +var zeros = [ + "", + "0", + "00", + "000", + "0000", + "00000", + "000000", + "0000000", + "00000000", + "000000000", + "0000000000", + "00000000000", + "000000000000", + "0000000000000", + "00000000000000", + "000000000000000", + "0000000000000000", + "00000000000000000", + "000000000000000000", + "0000000000000000000", + "00000000000000000000", + "000000000000000000000", + "0000000000000000000000", + "00000000000000000000000", + "000000000000000000000000", + "0000000000000000000000000", +]; + +var groupSizes = [ + 0, + 0, + 25, + 16, + 12, + 11, + 10, + 9, + 8, + 8, + 7, + 7, + 7, + 7, + 6, + 6, + 6, + 6, + 6, + 6, + 6, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, + 5, +]; + +var groupBases = [ + 0, + 0, + 33554432, + 43046721, + 16777216, + 48828125, + 60466176, + 40353607, + 16777216, + 43046721, + 10000000, + 19487171, + 35831808, + 62748517, + 7529536, + 11390625, + 16777216, + 24137569, + 34012224, + 47045881, + 64000000, + 4084101, + 5153632, + 6436343, + 7962624, + 9765625, + 11881376, + 14348907, + 17210368, + 20511149, + 24300000, + 28629151, + 33554432, + 39135393, + 45435424, + 52521875, + 60466176, +]; + +BN.prototype.toString = function toString(base, padding) { + base = base || 10; + padding = padding | 0 || 1; + + var out; + if (base === 16 || base === "hex") { + out = ""; + var off = 0; + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = this.words[i]; + var word = (((w << off) | carry) & 0xffffff).toString(16); + carry = (w >>> (24 - off)) & 0xffffff; + if (carry !== 0 || i !== this.length - 1) { + out = zeros[6 - word.length] + word + out; + } else { + out = word + out; + } + off += 2; + if (off >= 26) { + off -= 26; + i--; + } + } + if (carry !== 0) { + out = carry.toString(16) + out; + } + while (out.length % padding !== 0) { + out = "0" + out; + } + if (this.negative !== 0) { + out = "-" + out; + } + return out; + } + + if (base === (base | 0) && base >= 2 && base <= 36) { + // var groupSize = Math.floor(BN.wordSize * Math.LN2 / Math.log(base)); + var groupSize = groupSizes[base]; + // var groupBase = Math.pow(base, groupSize); + var groupBase = groupBases[base]; + out = ""; + var c = this.clone(); + c.negative = 0; + while (!c.isZero()) { + var r = c.modrn(groupBase).toString(base); + c = c.idivn(groupBase); + + if (!c.isZero()) { + out = zeros[groupSize - r.length] + r + out; + } else { + out = r + out; + } + } + if (this.isZero()) { + out = "0" + out; + } + while (out.length % padding !== 0) { + out = "0" + out; + } + if (this.negative !== 0) { + out = "-" + out; + } + return out; + } + + assert(false, "Base should be between 2 and 36"); +}; + +BN.prototype.toNumber = function toNumber() { + var ret = this.words[0]; + if (this.length === 2) { + ret += this.words[1] * 0x4000000; + } else if (this.length === 3 && this.words[2] === 0x01) { + // NOTE: at this stage it is known that the top bit is set + ret += 0x10000000000000 + (this.words[1] * 0x4000000); + } else if (this.length > 2) { + assert(false, "Number can only safely store up to 53 bits"); + } + return (this.negative !== 0) ? -ret : ret; +}; + +BN.prototype.toJSON = function toJSON() { + return this.toString(16, 2); +}; + +if (Buffer) { + BN.prototype.toBuffer = function toBuffer(endian, length) { + return this.toArrayLike(Buffer, endian, length); + }; +} + +BN.prototype.toArray = function toArray(endian, length) { + return this.toArrayLike(Array, endian, length); +}; + +var allocate = function allocate(ArrayType, size) { + if (ArrayType.allocUnsafe) { + return ArrayType.allocUnsafe(size); + } + return new ArrayType(size); +}; + +BN.prototype.toArrayLike = function toArrayLike(ArrayType, endian, length) { + this._strip(); + + var byteLength = this.byteLength(); + var reqLength = length || Math.max(1, byteLength); + assert(byteLength <= reqLength, "byte array longer than desired length"); + assert(reqLength > 0, "Requested array length <= 0"); + + var res = allocate(ArrayType, reqLength); + var postfix = endian === "le" ? "LE" : "BE"; + this["_toArrayLike" + postfix](res, byteLength); + return res; +}; + +BN.prototype._toArrayLikeLE = function _toArrayLikeLE(res, byteLength) { + var position = 0; + var carry = 0; + + for (var i = 0, shift = 0; i < this.length; i++) { + var word = (this.words[i] << shift) | carry; + + res[position++] = word & 0xff; + if (position < res.length) { + res[position++] = (word >> 8) & 0xff; + } + if (position < res.length) { + res[position++] = (word >> 16) & 0xff; + } + + if (shift === 6) { + if (position < res.length) { + res[position++] = (word >> 24) & 0xff; + } + carry = 0; + shift = 0; + } else { + carry = word >>> 24; + shift += 2; + } + } + + if (position < res.length) { + res[position++] = carry; + + while (position < res.length) { + res[position++] = 0; + } + } +}; + +BN.prototype._toArrayLikeBE = function _toArrayLikeBE(res, byteLength) { + var position = res.length - 1; + var carry = 0; + + for (var i = 0, shift = 0; i < this.length; i++) { + var word = (this.words[i] << shift) | carry; + + res[position--] = word & 0xff; + if (position >= 0) { + res[position--] = (word >> 8) & 0xff; + } + if (position >= 0) { + res[position--] = (word >> 16) & 0xff; + } + + if (shift === 6) { + if (position >= 0) { + res[position--] = (word >> 24) & 0xff; + } + carry = 0; + shift = 0; + } else { + carry = word >>> 24; + shift += 2; + } + } + + if (position >= 0) { + res[position--] = carry; + + while (position >= 0) { + res[position--] = 0; + } + } +}; + +if (Math.clz32) { + BN.prototype._countBits = function _countBits(w) { + return 32 - Math.clz32(w); + }; +} else { + BN.prototype._countBits = function _countBits(w) { + var t = w; + var r = 0; + if (t >= 0x1000) { + r += 13; + t >>>= 13; + } + if (t >= 0x40) { + r += 7; + t >>>= 7; + } + if (t >= 0x8) { + r += 4; + t >>>= 4; + } + if (t >= 0x02) { + r += 2; + t >>>= 2; + } + return r + t; + }; +} + +BN.prototype._zeroBits = function _zeroBits(w) { + // Short-cut + if (w === 0) return 26; + + var t = w; + var r = 0; + if ((t & 0x1fff) === 0) { + r += 13; + t >>>= 13; + } + if ((t & 0x7f) === 0) { + r += 7; + t >>>= 7; + } + if ((t & 0xf) === 0) { + r += 4; + t >>>= 4; + } + if ((t & 0x3) === 0) { + r += 2; + t >>>= 2; + } + if ((t & 0x1) === 0) { + r++; + } + return r; +}; + +// Return number of used bits in a BN +BN.prototype.bitLength = function bitLength() { + var w = this.words[this.length - 1]; + var hi = this._countBits(w); + return (this.length - 1) * 26 + hi; +}; + +function toBitArray(num) { + var w = new Array(num.bitLength()); + + for (var bit = 0; bit < w.length; bit++) { + var off = (bit / 26) | 0; + var wbit = bit % 26; + + w[bit] = (num.words[off] >>> wbit) & 0x01; + } + + return w; +} + +// Number of trailing zero bits +BN.prototype.zeroBits = function zeroBits() { + if (this.isZero()) return 0; + + var r = 0; + for (var i = 0; i < this.length; i++) { + var b = this._zeroBits(this.words[i]); + r += b; + if (b !== 26) break; + } + return r; +}; + +BN.prototype.byteLength = function byteLength() { + return Math.ceil(this.bitLength() / 8); +}; + +BN.prototype.toTwos = function toTwos(width) { + if (this.negative !== 0) { + return this.abs().inotn(width).iaddn(1); + } + return this.clone(); +}; + +BN.prototype.fromTwos = function fromTwos(width) { + if (this.testn(width - 1)) { + return this.notn(width).iaddn(1).ineg(); + } + return this.clone(); +}; + +BN.prototype.isNeg = function isNeg() { + return this.negative !== 0; +}; + +// Return negative clone of `this` +BN.prototype.neg = function neg() { + return this.clone().ineg(); +}; + +BN.prototype.ineg = function ineg() { + if (!this.isZero()) { + this.negative ^= 1; + } + + return this; +}; + +// Or `num` with `this` in-place +BN.prototype.iuor = function iuor(num) { + while (this.length < num.length) { + this.words[this.length++] = 0; + } + + for (var i = 0; i < num.length; i++) { + this.words[i] = this.words[i] | num.words[i]; + } + + return this._strip(); +}; + +BN.prototype.ior = function ior(num) { + assert((this.negative | num.negative) === 0); + return this.iuor(num); +}; + +// Or `num` with `this` +BN.prototype.or = function or(num) { + if (this.length > num.length) return this.clone().ior(num); + return num.clone().ior(this); +}; + +BN.prototype.uor = function uor(num) { + if (this.length > num.length) return this.clone().iuor(num); + return num.clone().iuor(this); +}; + +// And `num` with `this` in-place +BN.prototype.iuand = function iuand(num) { + // b = min-length(num, this) + var b; + if (this.length > num.length) { + b = num; + } else { + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = this.words[i] & num.words[i]; + } + + this.length = b.length; + + return this._strip(); +}; + +BN.prototype.iand = function iand(num) { + assert((this.negative | num.negative) === 0); + return this.iuand(num); +}; + +// And `num` with `this` +BN.prototype.and = function and(num) { + if (this.length > num.length) return this.clone().iand(num); + return num.clone().iand(this); +}; + +BN.prototype.uand = function uand(num) { + if (this.length > num.length) return this.clone().iuand(num); + return num.clone().iuand(this); +}; + +// Xor `num` with `this` in-place +BN.prototype.iuxor = function iuxor(num) { + // a.length > b.length + var a; + var b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + for (var i = 0; i < b.length; i++) { + this.words[i] = a.words[i] ^ b.words[i]; + } + + if (this !== a) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = a.length; + + return this._strip(); +}; + +BN.prototype.ixor = function ixor(num) { + assert((this.negative | num.negative) === 0); + return this.iuxor(num); +}; + +// Xor `num` with `this` +BN.prototype.xor = function xor(num) { + if (this.length > num.length) return this.clone().ixor(num); + return num.clone().ixor(this); +}; + +BN.prototype.uxor = function uxor(num) { + if (this.length > num.length) return this.clone().iuxor(num); + return num.clone().iuxor(this); +}; + +// Not ``this`` with ``width`` bitwidth +BN.prototype.inotn = function inotn(width) { + assert(typeof width === "number" && width >= 0); + + var bytesNeeded = Math.ceil(width / 26) | 0; + var bitsLeft = width % 26; + + // Extend the buffer with leading zeroes + this._expand(bytesNeeded); + + if (bitsLeft > 0) { + bytesNeeded--; + } + + // Handle complete words + for (var i = 0; i < bytesNeeded; i++) { + this.words[i] = ~this.words[i] & 0x3ffffff; + } + + // Handle the residue + if (bitsLeft > 0) { + this.words[i] = ~this.words[i] & (0x3ffffff >> (26 - bitsLeft)); + } + + // And remove leading zeroes + return this._strip(); +}; + +BN.prototype.notn = function notn(width) { + return this.clone().inotn(width); +}; + +// Set `bit` of `this` +BN.prototype.setn = function setn(bit, val) { + assert(typeof bit === "number" && bit >= 0); + + var off = (bit / 26) | 0; + var wbit = bit % 26; + + this._expand(off + 1); + + if (val) { + this.words[off] = this.words[off] | (1 << wbit); + } else { + this.words[off] = this.words[off] & ~(1 << wbit); + } + + return this._strip(); +}; + +// Add `num` to `this` in-place +BN.prototype.iadd = function iadd(num) { + var r; + + // negative + positive + if (this.negative !== 0 && num.negative === 0) { + this.negative = 0; + r = this.isub(num); + this.negative ^= 1; + return this._normSign(); + + // positive + negative + } else if (this.negative === 0 && num.negative !== 0) { + num.negative = 0; + r = this.isub(num); + num.negative = 1; + return r._normSign(); + } + + // a.length > b.length + var a, b; + if (this.length > num.length) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) + (b.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + this.words[i] = r & 0x3ffffff; + carry = r >>> 26; + } + + this.length = a.length; + if (carry !== 0) { + this.words[this.length] = carry; + this.length++; + // Copy the rest of the words + } else if (a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + return this; +}; + +// Add `num` to `this` +BN.prototype.add = function add(num) { + var res; + if (num.negative !== 0 && this.negative === 0) { + num.negative = 0; + res = this.sub(num); + num.negative ^= 1; + return res; + } else if (num.negative === 0 && this.negative !== 0) { + this.negative = 0; + res = num.sub(this); + this.negative = 1; + return res; + } + + if (this.length > num.length) return this.clone().iadd(num); + + return num.clone().iadd(this); +}; + +// Subtract `num` from `this` in-place +BN.prototype.isub = function isub(num) { + // this - (-num) = this + num + if (num.negative !== 0) { + num.negative = 0; + var r = this.iadd(num); + num.negative = 1; + return r._normSign(); + + // -this - num = -(this + num) + } else if (this.negative !== 0) { + this.negative = 0; + this.iadd(num); + this.negative = 1; + return this._normSign(); + } + + // At this point both numbers are positive + var cmp = this.cmp(num); + + // Optimization - zeroify + if (cmp === 0) { + this.negative = 0; + this.length = 1; + this.words[0] = 0; + return this; + } + + // a > b + var a, b; + if (cmp > 0) { + a = this; + b = num; + } else { + a = num; + b = this; + } + + var carry = 0; + for (var i = 0; i < b.length; i++) { + r = (a.words[i] | 0) - (b.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + for (; carry !== 0 && i < a.length; i++) { + r = (a.words[i] | 0) + carry; + carry = r >> 26; + this.words[i] = r & 0x3ffffff; + } + + // Copy rest of the words + if (carry === 0 && i < a.length && a !== this) { + for (; i < a.length; i++) { + this.words[i] = a.words[i]; + } + } + + this.length = Math.max(this.length, i); + + if (a !== this) { + this.negative = 1; + } + + return this._strip(); +}; + +// Subtract `num` from `this` +BN.prototype.sub = function sub(num) { + return this.clone().isub(num); +}; + +function smallMulTo(self, num, out) { + out.negative = num.negative ^ self.negative; + var len = (self.length + num.length) | 0; + out.length = len; + len = (len - 1) | 0; + + // Peel one iteration (compiler can't do it, because of code complexity) + var a = self.words[0] | 0; + var b = num.words[0] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + var carry = (r / 0x4000000) | 0; + out.words[0] = lo; + + for (var k = 1; k < len; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = carry >>> 26; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = (k - j) | 0; + a = self.words[i] | 0; + b = num.words[j] | 0; + r = a * b + rword; + ncarry += (r / 0x4000000) | 0; + rword = r & 0x3ffffff; + } + out.words[k] = rword | 0; + carry = ncarry | 0; + } + if (carry !== 0) { + out.words[k] = carry | 0; + } else { + out.length--; + } + + return out._strip(); +} + +// TODO(indutny): it may be reasonable to omit it for users who don't need +// to work with 256-bit numbers, otherwise it gives 20% improvement for 256-bit +// multiplication (like elliptic secp256k1). +var comb10MulTo = function comb10MulTo(self, num, out) { + var a = self.words; + var b = num.words; + var o = out.words; + var c = 0; + var lo; + var mid; + var hi; + var a0 = a[0] | 0; + var al0 = a0 & 0x1fff; + var ah0 = a0 >>> 13; + var a1 = a[1] | 0; + var al1 = a1 & 0x1fff; + var ah1 = a1 >>> 13; + var a2 = a[2] | 0; + var al2 = a2 & 0x1fff; + var ah2 = a2 >>> 13; + var a3 = a[3] | 0; + var al3 = a3 & 0x1fff; + var ah3 = a3 >>> 13; + var a4 = a[4] | 0; + var al4 = a4 & 0x1fff; + var ah4 = a4 >>> 13; + var a5 = a[5] | 0; + var al5 = a5 & 0x1fff; + var ah5 = a5 >>> 13; + var a6 = a[6] | 0; + var al6 = a6 & 0x1fff; + var ah6 = a6 >>> 13; + var a7 = a[7] | 0; + var al7 = a7 & 0x1fff; + var ah7 = a7 >>> 13; + var a8 = a[8] | 0; + var al8 = a8 & 0x1fff; + var ah8 = a8 >>> 13; + var a9 = a[9] | 0; + var al9 = a9 & 0x1fff; + var ah9 = a9 >>> 13; + var b0 = b[0] | 0; + var bl0 = b0 & 0x1fff; + var bh0 = b0 >>> 13; + var b1 = b[1] | 0; + var bl1 = b1 & 0x1fff; + var bh1 = b1 >>> 13; + var b2 = b[2] | 0; + var bl2 = b2 & 0x1fff; + var bh2 = b2 >>> 13; + var b3 = b[3] | 0; + var bl3 = b3 & 0x1fff; + var bh3 = b3 >>> 13; + var b4 = b[4] | 0; + var bl4 = b4 & 0x1fff; + var bh4 = b4 >>> 13; + var b5 = b[5] | 0; + var bl5 = b5 & 0x1fff; + var bh5 = b5 >>> 13; + var b6 = b[6] | 0; + var bl6 = b6 & 0x1fff; + var bh6 = b6 >>> 13; + var b7 = b[7] | 0; + var bl7 = b7 & 0x1fff; + var bh7 = b7 >>> 13; + var b8 = b[8] | 0; + var bl8 = b8 & 0x1fff; + var bh8 = b8 >>> 13; + var b9 = b[9] | 0; + var bl9 = b9 & 0x1fff; + var bh9 = b9 >>> 13; + + out.negative = self.negative ^ num.negative; + out.length = 19; + /* k = 0 */ + lo = Math.imul(al0, bl0); + mid = Math.imul(al0, bh0); + mid = (mid + Math.imul(ah0, bl0)) | 0; + hi = Math.imul(ah0, bh0); + var w0 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w0 >>> 26)) | 0; + w0 &= 0x3ffffff; + /* k = 1 */ + lo = Math.imul(al1, bl0); + mid = Math.imul(al1, bh0); + mid = (mid + Math.imul(ah1, bl0)) | 0; + hi = Math.imul(ah1, bh0); + lo = (lo + Math.imul(al0, bl1)) | 0; + mid = (mid + Math.imul(al0, bh1)) | 0; + mid = (mid + Math.imul(ah0, bl1)) | 0; + hi = (hi + Math.imul(ah0, bh1)) | 0; + var w1 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w1 >>> 26)) | 0; + w1 &= 0x3ffffff; + /* k = 2 */ + lo = Math.imul(al2, bl0); + mid = Math.imul(al2, bh0); + mid = (mid + Math.imul(ah2, bl0)) | 0; + hi = Math.imul(ah2, bh0); + lo = (lo + Math.imul(al1, bl1)) | 0; + mid = (mid + Math.imul(al1, bh1)) | 0; + mid = (mid + Math.imul(ah1, bl1)) | 0; + hi = (hi + Math.imul(ah1, bh1)) | 0; + lo = (lo + Math.imul(al0, bl2)) | 0; + mid = (mid + Math.imul(al0, bh2)) | 0; + mid = (mid + Math.imul(ah0, bl2)) | 0; + hi = (hi + Math.imul(ah0, bh2)) | 0; + var w2 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w2 >>> 26)) | 0; + w2 &= 0x3ffffff; + /* k = 3 */ + lo = Math.imul(al3, bl0); + mid = Math.imul(al3, bh0); + mid = (mid + Math.imul(ah3, bl0)) | 0; + hi = Math.imul(ah3, bh0); + lo = (lo + Math.imul(al2, bl1)) | 0; + mid = (mid + Math.imul(al2, bh1)) | 0; + mid = (mid + Math.imul(ah2, bl1)) | 0; + hi = (hi + Math.imul(ah2, bh1)) | 0; + lo = (lo + Math.imul(al1, bl2)) | 0; + mid = (mid + Math.imul(al1, bh2)) | 0; + mid = (mid + Math.imul(ah1, bl2)) | 0; + hi = (hi + Math.imul(ah1, bh2)) | 0; + lo = (lo + Math.imul(al0, bl3)) | 0; + mid = (mid + Math.imul(al0, bh3)) | 0; + mid = (mid + Math.imul(ah0, bl3)) | 0; + hi = (hi + Math.imul(ah0, bh3)) | 0; + var w3 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w3 >>> 26)) | 0; + w3 &= 0x3ffffff; + /* k = 4 */ + lo = Math.imul(al4, bl0); + mid = Math.imul(al4, bh0); + mid = (mid + Math.imul(ah4, bl0)) | 0; + hi = Math.imul(ah4, bh0); + lo = (lo + Math.imul(al3, bl1)) | 0; + mid = (mid + Math.imul(al3, bh1)) | 0; + mid = (mid + Math.imul(ah3, bl1)) | 0; + hi = (hi + Math.imul(ah3, bh1)) | 0; + lo = (lo + Math.imul(al2, bl2)) | 0; + mid = (mid + Math.imul(al2, bh2)) | 0; + mid = (mid + Math.imul(ah2, bl2)) | 0; + hi = (hi + Math.imul(ah2, bh2)) | 0; + lo = (lo + Math.imul(al1, bl3)) | 0; + mid = (mid + Math.imul(al1, bh3)) | 0; + mid = (mid + Math.imul(ah1, bl3)) | 0; + hi = (hi + Math.imul(ah1, bh3)) | 0; + lo = (lo + Math.imul(al0, bl4)) | 0; + mid = (mid + Math.imul(al0, bh4)) | 0; + mid = (mid + Math.imul(ah0, bl4)) | 0; + hi = (hi + Math.imul(ah0, bh4)) | 0; + var w4 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w4 >>> 26)) | 0; + w4 &= 0x3ffffff; + /* k = 5 */ + lo = Math.imul(al5, bl0); + mid = Math.imul(al5, bh0); + mid = (mid + Math.imul(ah5, bl0)) | 0; + hi = Math.imul(ah5, bh0); + lo = (lo + Math.imul(al4, bl1)) | 0; + mid = (mid + Math.imul(al4, bh1)) | 0; + mid = (mid + Math.imul(ah4, bl1)) | 0; + hi = (hi + Math.imul(ah4, bh1)) | 0; + lo = (lo + Math.imul(al3, bl2)) | 0; + mid = (mid + Math.imul(al3, bh2)) | 0; + mid = (mid + Math.imul(ah3, bl2)) | 0; + hi = (hi + Math.imul(ah3, bh2)) | 0; + lo = (lo + Math.imul(al2, bl3)) | 0; + mid = (mid + Math.imul(al2, bh3)) | 0; + mid = (mid + Math.imul(ah2, bl3)) | 0; + hi = (hi + Math.imul(ah2, bh3)) | 0; + lo = (lo + Math.imul(al1, bl4)) | 0; + mid = (mid + Math.imul(al1, bh4)) | 0; + mid = (mid + Math.imul(ah1, bl4)) | 0; + hi = (hi + Math.imul(ah1, bh4)) | 0; + lo = (lo + Math.imul(al0, bl5)) | 0; + mid = (mid + Math.imul(al0, bh5)) | 0; + mid = (mid + Math.imul(ah0, bl5)) | 0; + hi = (hi + Math.imul(ah0, bh5)) | 0; + var w5 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w5 >>> 26)) | 0; + w5 &= 0x3ffffff; + /* k = 6 */ + lo = Math.imul(al6, bl0); + mid = Math.imul(al6, bh0); + mid = (mid + Math.imul(ah6, bl0)) | 0; + hi = Math.imul(ah6, bh0); + lo = (lo + Math.imul(al5, bl1)) | 0; + mid = (mid + Math.imul(al5, bh1)) | 0; + mid = (mid + Math.imul(ah5, bl1)) | 0; + hi = (hi + Math.imul(ah5, bh1)) | 0; + lo = (lo + Math.imul(al4, bl2)) | 0; + mid = (mid + Math.imul(al4, bh2)) | 0; + mid = (mid + Math.imul(ah4, bl2)) | 0; + hi = (hi + Math.imul(ah4, bh2)) | 0; + lo = (lo + Math.imul(al3, bl3)) | 0; + mid = (mid + Math.imul(al3, bh3)) | 0; + mid = (mid + Math.imul(ah3, bl3)) | 0; + hi = (hi + Math.imul(ah3, bh3)) | 0; + lo = (lo + Math.imul(al2, bl4)) | 0; + mid = (mid + Math.imul(al2, bh4)) | 0; + mid = (mid + Math.imul(ah2, bl4)) | 0; + hi = (hi + Math.imul(ah2, bh4)) | 0; + lo = (lo + Math.imul(al1, bl5)) | 0; + mid = (mid + Math.imul(al1, bh5)) | 0; + mid = (mid + Math.imul(ah1, bl5)) | 0; + hi = (hi + Math.imul(ah1, bh5)) | 0; + lo = (lo + Math.imul(al0, bl6)) | 0; + mid = (mid + Math.imul(al0, bh6)) | 0; + mid = (mid + Math.imul(ah0, bl6)) | 0; + hi = (hi + Math.imul(ah0, bh6)) | 0; + var w6 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w6 >>> 26)) | 0; + w6 &= 0x3ffffff; + /* k = 7 */ + lo = Math.imul(al7, bl0); + mid = Math.imul(al7, bh0); + mid = (mid + Math.imul(ah7, bl0)) | 0; + hi = Math.imul(ah7, bh0); + lo = (lo + Math.imul(al6, bl1)) | 0; + mid = (mid + Math.imul(al6, bh1)) | 0; + mid = (mid + Math.imul(ah6, bl1)) | 0; + hi = (hi + Math.imul(ah6, bh1)) | 0; + lo = (lo + Math.imul(al5, bl2)) | 0; + mid = (mid + Math.imul(al5, bh2)) | 0; + mid = (mid + Math.imul(ah5, bl2)) | 0; + hi = (hi + Math.imul(ah5, bh2)) | 0; + lo = (lo + Math.imul(al4, bl3)) | 0; + mid = (mid + Math.imul(al4, bh3)) | 0; + mid = (mid + Math.imul(ah4, bl3)) | 0; + hi = (hi + Math.imul(ah4, bh3)) | 0; + lo = (lo + Math.imul(al3, bl4)) | 0; + mid = (mid + Math.imul(al3, bh4)) | 0; + mid = (mid + Math.imul(ah3, bl4)) | 0; + hi = (hi + Math.imul(ah3, bh4)) | 0; + lo = (lo + Math.imul(al2, bl5)) | 0; + mid = (mid + Math.imul(al2, bh5)) | 0; + mid = (mid + Math.imul(ah2, bl5)) | 0; + hi = (hi + Math.imul(ah2, bh5)) | 0; + lo = (lo + Math.imul(al1, bl6)) | 0; + mid = (mid + Math.imul(al1, bh6)) | 0; + mid = (mid + Math.imul(ah1, bl6)) | 0; + hi = (hi + Math.imul(ah1, bh6)) | 0; + lo = (lo + Math.imul(al0, bl7)) | 0; + mid = (mid + Math.imul(al0, bh7)) | 0; + mid = (mid + Math.imul(ah0, bl7)) | 0; + hi = (hi + Math.imul(ah0, bh7)) | 0; + var w7 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w7 >>> 26)) | 0; + w7 &= 0x3ffffff; + /* k = 8 */ + lo = Math.imul(al8, bl0); + mid = Math.imul(al8, bh0); + mid = (mid + Math.imul(ah8, bl0)) | 0; + hi = Math.imul(ah8, bh0); + lo = (lo + Math.imul(al7, bl1)) | 0; + mid = (mid + Math.imul(al7, bh1)) | 0; + mid = (mid + Math.imul(ah7, bl1)) | 0; + hi = (hi + Math.imul(ah7, bh1)) | 0; + lo = (lo + Math.imul(al6, bl2)) | 0; + mid = (mid + Math.imul(al6, bh2)) | 0; + mid = (mid + Math.imul(ah6, bl2)) | 0; + hi = (hi + Math.imul(ah6, bh2)) | 0; + lo = (lo + Math.imul(al5, bl3)) | 0; + mid = (mid + Math.imul(al5, bh3)) | 0; + mid = (mid + Math.imul(ah5, bl3)) | 0; + hi = (hi + Math.imul(ah5, bh3)) | 0; + lo = (lo + Math.imul(al4, bl4)) | 0; + mid = (mid + Math.imul(al4, bh4)) | 0; + mid = (mid + Math.imul(ah4, bl4)) | 0; + hi = (hi + Math.imul(ah4, bh4)) | 0; + lo = (lo + Math.imul(al3, bl5)) | 0; + mid = (mid + Math.imul(al3, bh5)) | 0; + mid = (mid + Math.imul(ah3, bl5)) | 0; + hi = (hi + Math.imul(ah3, bh5)) | 0; + lo = (lo + Math.imul(al2, bl6)) | 0; + mid = (mid + Math.imul(al2, bh6)) | 0; + mid = (mid + Math.imul(ah2, bl6)) | 0; + hi = (hi + Math.imul(ah2, bh6)) | 0; + lo = (lo + Math.imul(al1, bl7)) | 0; + mid = (mid + Math.imul(al1, bh7)) | 0; + mid = (mid + Math.imul(ah1, bl7)) | 0; + hi = (hi + Math.imul(ah1, bh7)) | 0; + lo = (lo + Math.imul(al0, bl8)) | 0; + mid = (mid + Math.imul(al0, bh8)) | 0; + mid = (mid + Math.imul(ah0, bl8)) | 0; + hi = (hi + Math.imul(ah0, bh8)) | 0; + var w8 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w8 >>> 26)) | 0; + w8 &= 0x3ffffff; + /* k = 9 */ + lo = Math.imul(al9, bl0); + mid = Math.imul(al9, bh0); + mid = (mid + Math.imul(ah9, bl0)) | 0; + hi = Math.imul(ah9, bh0); + lo = (lo + Math.imul(al8, bl1)) | 0; + mid = (mid + Math.imul(al8, bh1)) | 0; + mid = (mid + Math.imul(ah8, bl1)) | 0; + hi = (hi + Math.imul(ah8, bh1)) | 0; + lo = (lo + Math.imul(al7, bl2)) | 0; + mid = (mid + Math.imul(al7, bh2)) | 0; + mid = (mid + Math.imul(ah7, bl2)) | 0; + hi = (hi + Math.imul(ah7, bh2)) | 0; + lo = (lo + Math.imul(al6, bl3)) | 0; + mid = (mid + Math.imul(al6, bh3)) | 0; + mid = (mid + Math.imul(ah6, bl3)) | 0; + hi = (hi + Math.imul(ah6, bh3)) | 0; + lo = (lo + Math.imul(al5, bl4)) | 0; + mid = (mid + Math.imul(al5, bh4)) | 0; + mid = (mid + Math.imul(ah5, bl4)) | 0; + hi = (hi + Math.imul(ah5, bh4)) | 0; + lo = (lo + Math.imul(al4, bl5)) | 0; + mid = (mid + Math.imul(al4, bh5)) | 0; + mid = (mid + Math.imul(ah4, bl5)) | 0; + hi = (hi + Math.imul(ah4, bh5)) | 0; + lo = (lo + Math.imul(al3, bl6)) | 0; + mid = (mid + Math.imul(al3, bh6)) | 0; + mid = (mid + Math.imul(ah3, bl6)) | 0; + hi = (hi + Math.imul(ah3, bh6)) | 0; + lo = (lo + Math.imul(al2, bl7)) | 0; + mid = (mid + Math.imul(al2, bh7)) | 0; + mid = (mid + Math.imul(ah2, bl7)) | 0; + hi = (hi + Math.imul(ah2, bh7)) | 0; + lo = (lo + Math.imul(al1, bl8)) | 0; + mid = (mid + Math.imul(al1, bh8)) | 0; + mid = (mid + Math.imul(ah1, bl8)) | 0; + hi = (hi + Math.imul(ah1, bh8)) | 0; + lo = (lo + Math.imul(al0, bl9)) | 0; + mid = (mid + Math.imul(al0, bh9)) | 0; + mid = (mid + Math.imul(ah0, bl9)) | 0; + hi = (hi + Math.imul(ah0, bh9)) | 0; + var w9 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w9 >>> 26)) | 0; + w9 &= 0x3ffffff; + /* k = 10 */ + lo = Math.imul(al9, bl1); + mid = Math.imul(al9, bh1); + mid = (mid + Math.imul(ah9, bl1)) | 0; + hi = Math.imul(ah9, bh1); + lo = (lo + Math.imul(al8, bl2)) | 0; + mid = (mid + Math.imul(al8, bh2)) | 0; + mid = (mid + Math.imul(ah8, bl2)) | 0; + hi = (hi + Math.imul(ah8, bh2)) | 0; + lo = (lo + Math.imul(al7, bl3)) | 0; + mid = (mid + Math.imul(al7, bh3)) | 0; + mid = (mid + Math.imul(ah7, bl3)) | 0; + hi = (hi + Math.imul(ah7, bh3)) | 0; + lo = (lo + Math.imul(al6, bl4)) | 0; + mid = (mid + Math.imul(al6, bh4)) | 0; + mid = (mid + Math.imul(ah6, bl4)) | 0; + hi = (hi + Math.imul(ah6, bh4)) | 0; + lo = (lo + Math.imul(al5, bl5)) | 0; + mid = (mid + Math.imul(al5, bh5)) | 0; + mid = (mid + Math.imul(ah5, bl5)) | 0; + hi = (hi + Math.imul(ah5, bh5)) | 0; + lo = (lo + Math.imul(al4, bl6)) | 0; + mid = (mid + Math.imul(al4, bh6)) | 0; + mid = (mid + Math.imul(ah4, bl6)) | 0; + hi = (hi + Math.imul(ah4, bh6)) | 0; + lo = (lo + Math.imul(al3, bl7)) | 0; + mid = (mid + Math.imul(al3, bh7)) | 0; + mid = (mid + Math.imul(ah3, bl7)) | 0; + hi = (hi + Math.imul(ah3, bh7)) | 0; + lo = (lo + Math.imul(al2, bl8)) | 0; + mid = (mid + Math.imul(al2, bh8)) | 0; + mid = (mid + Math.imul(ah2, bl8)) | 0; + hi = (hi + Math.imul(ah2, bh8)) | 0; + lo = (lo + Math.imul(al1, bl9)) | 0; + mid = (mid + Math.imul(al1, bh9)) | 0; + mid = (mid + Math.imul(ah1, bl9)) | 0; + hi = (hi + Math.imul(ah1, bh9)) | 0; + var w10 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w10 >>> 26)) | 0; + w10 &= 0x3ffffff; + /* k = 11 */ + lo = Math.imul(al9, bl2); + mid = Math.imul(al9, bh2); + mid = (mid + Math.imul(ah9, bl2)) | 0; + hi = Math.imul(ah9, bh2); + lo = (lo + Math.imul(al8, bl3)) | 0; + mid = (mid + Math.imul(al8, bh3)) | 0; + mid = (mid + Math.imul(ah8, bl3)) | 0; + hi = (hi + Math.imul(ah8, bh3)) | 0; + lo = (lo + Math.imul(al7, bl4)) | 0; + mid = (mid + Math.imul(al7, bh4)) | 0; + mid = (mid + Math.imul(ah7, bl4)) | 0; + hi = (hi + Math.imul(ah7, bh4)) | 0; + lo = (lo + Math.imul(al6, bl5)) | 0; + mid = (mid + Math.imul(al6, bh5)) | 0; + mid = (mid + Math.imul(ah6, bl5)) | 0; + hi = (hi + Math.imul(ah6, bh5)) | 0; + lo = (lo + Math.imul(al5, bl6)) | 0; + mid = (mid + Math.imul(al5, bh6)) | 0; + mid = (mid + Math.imul(ah5, bl6)) | 0; + hi = (hi + Math.imul(ah5, bh6)) | 0; + lo = (lo + Math.imul(al4, bl7)) | 0; + mid = (mid + Math.imul(al4, bh7)) | 0; + mid = (mid + Math.imul(ah4, bl7)) | 0; + hi = (hi + Math.imul(ah4, bh7)) | 0; + lo = (lo + Math.imul(al3, bl8)) | 0; + mid = (mid + Math.imul(al3, bh8)) | 0; + mid = (mid + Math.imul(ah3, bl8)) | 0; + hi = (hi + Math.imul(ah3, bh8)) | 0; + lo = (lo + Math.imul(al2, bl9)) | 0; + mid = (mid + Math.imul(al2, bh9)) | 0; + mid = (mid + Math.imul(ah2, bl9)) | 0; + hi = (hi + Math.imul(ah2, bh9)) | 0; + var w11 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w11 >>> 26)) | 0; + w11 &= 0x3ffffff; + /* k = 12 */ + lo = Math.imul(al9, bl3); + mid = Math.imul(al9, bh3); + mid = (mid + Math.imul(ah9, bl3)) | 0; + hi = Math.imul(ah9, bh3); + lo = (lo + Math.imul(al8, bl4)) | 0; + mid = (mid + Math.imul(al8, bh4)) | 0; + mid = (mid + Math.imul(ah8, bl4)) | 0; + hi = (hi + Math.imul(ah8, bh4)) | 0; + lo = (lo + Math.imul(al7, bl5)) | 0; + mid = (mid + Math.imul(al7, bh5)) | 0; + mid = (mid + Math.imul(ah7, bl5)) | 0; + hi = (hi + Math.imul(ah7, bh5)) | 0; + lo = (lo + Math.imul(al6, bl6)) | 0; + mid = (mid + Math.imul(al6, bh6)) | 0; + mid = (mid + Math.imul(ah6, bl6)) | 0; + hi = (hi + Math.imul(ah6, bh6)) | 0; + lo = (lo + Math.imul(al5, bl7)) | 0; + mid = (mid + Math.imul(al5, bh7)) | 0; + mid = (mid + Math.imul(ah5, bl7)) | 0; + hi = (hi + Math.imul(ah5, bh7)) | 0; + lo = (lo + Math.imul(al4, bl8)) | 0; + mid = (mid + Math.imul(al4, bh8)) | 0; + mid = (mid + Math.imul(ah4, bl8)) | 0; + hi = (hi + Math.imul(ah4, bh8)) | 0; + lo = (lo + Math.imul(al3, bl9)) | 0; + mid = (mid + Math.imul(al3, bh9)) | 0; + mid = (mid + Math.imul(ah3, bl9)) | 0; + hi = (hi + Math.imul(ah3, bh9)) | 0; + var w12 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w12 >>> 26)) | 0; + w12 &= 0x3ffffff; + /* k = 13 */ + lo = Math.imul(al9, bl4); + mid = Math.imul(al9, bh4); + mid = (mid + Math.imul(ah9, bl4)) | 0; + hi = Math.imul(ah9, bh4); + lo = (lo + Math.imul(al8, bl5)) | 0; + mid = (mid + Math.imul(al8, bh5)) | 0; + mid = (mid + Math.imul(ah8, bl5)) | 0; + hi = (hi + Math.imul(ah8, bh5)) | 0; + lo = (lo + Math.imul(al7, bl6)) | 0; + mid = (mid + Math.imul(al7, bh6)) | 0; + mid = (mid + Math.imul(ah7, bl6)) | 0; + hi = (hi + Math.imul(ah7, bh6)) | 0; + lo = (lo + Math.imul(al6, bl7)) | 0; + mid = (mid + Math.imul(al6, bh7)) | 0; + mid = (mid + Math.imul(ah6, bl7)) | 0; + hi = (hi + Math.imul(ah6, bh7)) | 0; + lo = (lo + Math.imul(al5, bl8)) | 0; + mid = (mid + Math.imul(al5, bh8)) | 0; + mid = (mid + Math.imul(ah5, bl8)) | 0; + hi = (hi + Math.imul(ah5, bh8)) | 0; + lo = (lo + Math.imul(al4, bl9)) | 0; + mid = (mid + Math.imul(al4, bh9)) | 0; + mid = (mid + Math.imul(ah4, bl9)) | 0; + hi = (hi + Math.imul(ah4, bh9)) | 0; + var w13 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w13 >>> 26)) | 0; + w13 &= 0x3ffffff; + /* k = 14 */ + lo = Math.imul(al9, bl5); + mid = Math.imul(al9, bh5); + mid = (mid + Math.imul(ah9, bl5)) | 0; + hi = Math.imul(ah9, bh5); + lo = (lo + Math.imul(al8, bl6)) | 0; + mid = (mid + Math.imul(al8, bh6)) | 0; + mid = (mid + Math.imul(ah8, bl6)) | 0; + hi = (hi + Math.imul(ah8, bh6)) | 0; + lo = (lo + Math.imul(al7, bl7)) | 0; + mid = (mid + Math.imul(al7, bh7)) | 0; + mid = (mid + Math.imul(ah7, bl7)) | 0; + hi = (hi + Math.imul(ah7, bh7)) | 0; + lo = (lo + Math.imul(al6, bl8)) | 0; + mid = (mid + Math.imul(al6, bh8)) | 0; + mid = (mid + Math.imul(ah6, bl8)) | 0; + hi = (hi + Math.imul(ah6, bh8)) | 0; + lo = (lo + Math.imul(al5, bl9)) | 0; + mid = (mid + Math.imul(al5, bh9)) | 0; + mid = (mid + Math.imul(ah5, bl9)) | 0; + hi = (hi + Math.imul(ah5, bh9)) | 0; + var w14 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w14 >>> 26)) | 0; + w14 &= 0x3ffffff; + /* k = 15 */ + lo = Math.imul(al9, bl6); + mid = Math.imul(al9, bh6); + mid = (mid + Math.imul(ah9, bl6)) | 0; + hi = Math.imul(ah9, bh6); + lo = (lo + Math.imul(al8, bl7)) | 0; + mid = (mid + Math.imul(al8, bh7)) | 0; + mid = (mid + Math.imul(ah8, bl7)) | 0; + hi = (hi + Math.imul(ah8, bh7)) | 0; + lo = (lo + Math.imul(al7, bl8)) | 0; + mid = (mid + Math.imul(al7, bh8)) | 0; + mid = (mid + Math.imul(ah7, bl8)) | 0; + hi = (hi + Math.imul(ah7, bh8)) | 0; + lo = (lo + Math.imul(al6, bl9)) | 0; + mid = (mid + Math.imul(al6, bh9)) | 0; + mid = (mid + Math.imul(ah6, bl9)) | 0; + hi = (hi + Math.imul(ah6, bh9)) | 0; + var w15 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w15 >>> 26)) | 0; + w15 &= 0x3ffffff; + /* k = 16 */ + lo = Math.imul(al9, bl7); + mid = Math.imul(al9, bh7); + mid = (mid + Math.imul(ah9, bl7)) | 0; + hi = Math.imul(ah9, bh7); + lo = (lo + Math.imul(al8, bl8)) | 0; + mid = (mid + Math.imul(al8, bh8)) | 0; + mid = (mid + Math.imul(ah8, bl8)) | 0; + hi = (hi + Math.imul(ah8, bh8)) | 0; + lo = (lo + Math.imul(al7, bl9)) | 0; + mid = (mid + Math.imul(al7, bh9)) | 0; + mid = (mid + Math.imul(ah7, bl9)) | 0; + hi = (hi + Math.imul(ah7, bh9)) | 0; + var w16 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w16 >>> 26)) | 0; + w16 &= 0x3ffffff; + /* k = 17 */ + lo = Math.imul(al9, bl8); + mid = Math.imul(al9, bh8); + mid = (mid + Math.imul(ah9, bl8)) | 0; + hi = Math.imul(ah9, bh8); + lo = (lo + Math.imul(al8, bl9)) | 0; + mid = (mid + Math.imul(al8, bh9)) | 0; + mid = (mid + Math.imul(ah8, bl9)) | 0; + hi = (hi + Math.imul(ah8, bh9)) | 0; + var w17 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w17 >>> 26)) | 0; + w17 &= 0x3ffffff; + /* k = 18 */ + lo = Math.imul(al9, bl9); + mid = Math.imul(al9, bh9); + mid = (mid + Math.imul(ah9, bl9)) | 0; + hi = Math.imul(ah9, bh9); + var w18 = (((c + lo) | 0) + ((mid & 0x1fff) << 13)) | 0; + c = (((hi + (mid >>> 13)) | 0) + (w18 >>> 26)) | 0; + w18 &= 0x3ffffff; + o[0] = w0; + o[1] = w1; + o[2] = w2; + o[3] = w3; + o[4] = w4; + o[5] = w5; + o[6] = w6; + o[7] = w7; + o[8] = w8; + o[9] = w9; + o[10] = w10; + o[11] = w11; + o[12] = w12; + o[13] = w13; + o[14] = w14; + o[15] = w15; + o[16] = w16; + o[17] = w17; + o[18] = w18; + if (c !== 0) { + o[19] = c; + out.length++; + } + return out; +}; + +// Polyfill comb +if (!Math.imul) { + comb10MulTo = smallMulTo; +} + +function bigMulTo(self, num, out) { + out.negative = num.negative ^ self.negative; + out.length = self.length + num.length; + + var carry = 0; + var hncarry = 0; + for (var k = 0; k < out.length - 1; k++) { + // Sum all words with the same `i + j = k` and accumulate `ncarry`, + // note that ncarry could be >= 0x3ffffff + var ncarry = hncarry; + hncarry = 0; + var rword = carry & 0x3ffffff; + var maxJ = Math.min(k, num.length - 1); + for (var j = Math.max(0, k - self.length + 1); j <= maxJ; j++) { + var i = k - j; + var a = self.words[i] | 0; + var b = num.words[j] | 0; + var r = a * b; + + var lo = r & 0x3ffffff; + ncarry = (ncarry + ((r / 0x4000000) | 0)) | 0; + lo = (lo + rword) | 0; + rword = lo & 0x3ffffff; + ncarry = (ncarry + (lo >>> 26)) | 0; + + hncarry += ncarry >>> 26; + ncarry &= 0x3ffffff; + } + out.words[k] = rword; + carry = ncarry; + ncarry = hncarry; + } + if (carry !== 0) { + out.words[k] = carry; + } else { + out.length--; + } + + return out._strip(); +} + +function jumboMulTo(self, num, out) { + // Temporary disable, see https://github.com/indutny/bn.js/issues/211 + // var fftm = new FFTM(); + // return fftm.mulp(self, num, out); + return bigMulTo(self, num, out); +} + +BN.prototype.mulTo = function mulTo(num, out) { + var res; + var len = this.length + num.length; + if (this.length === 10 && num.length === 10) { + res = comb10MulTo(this, num, out); + } else if (len < 63) { + res = smallMulTo(this, num, out); + } else if (len < 1024) { + res = bigMulTo(this, num, out); + } else { + res = jumboMulTo(this, num, out); + } + + return res; +}; + +// Cooley-Tukey algorithm for FFT +// slightly revisited to rely on looping instead of recursion + +function FFTM(x, y) { + this.x = x; + this.y = y; +} + +FFTM.prototype.makeRBT = function makeRBT(N) { + var t = new Array(N); + var l = BN.prototype._countBits(N) - 1; + for (var i = 0; i < N; i++) { + t[i] = this.revBin(i, l, N); + } + + return t; +}; + +// Returns binary-reversed representation of `x` +FFTM.prototype.revBin = function revBin(x, l, N) { + if (x === 0 || x === N - 1) return x; + + var rb = 0; + for (var i = 0; i < l; i++) { + rb |= (x & 1) << (l - i - 1); + x >>= 1; + } + + return rb; +}; + +// Performs "tweedling" phase, therefore 'emulating' +// behaviour of the recursive algorithm +FFTM.prototype.permute = function permute(rbt, rws, iws, rtws, itws, N) { + for (var i = 0; i < N; i++) { + rtws[i] = rws[rbt[i]]; + itws[i] = iws[rbt[i]]; + } +}; + +FFTM.prototype.transform = function transform(rws, iws, rtws, itws, N, rbt) { + this.permute(rbt, rws, iws, rtws, itws, N); + + for (var s = 1; s < N; s <<= 1) { + var l = s << 1; + + var rtwdf = Math.cos(2 * Math.PI / l); + var itwdf = Math.sin(2 * Math.PI / l); + + for (var p = 0; p < N; p += l) { + var rtwdf_ = rtwdf; + var itwdf_ = itwdf; + + for (var j = 0; j < s; j++) { + var re = rtws[p + j]; + var ie = itws[p + j]; + + var ro = rtws[p + j + s]; + var io = itws[p + j + s]; + + var rx = rtwdf_ * ro - itwdf_ * io; + + io = rtwdf_ * io + itwdf_ * ro; + ro = rx; + + rtws[p + j] = re + ro; + itws[p + j] = ie + io; + + rtws[p + j + s] = re - ro; + itws[p + j + s] = ie - io; + + /* jshint maxdepth : false */ + if (j !== l) { + rx = rtwdf * rtwdf_ - itwdf * itwdf_; + + itwdf_ = rtwdf * itwdf_ + itwdf * rtwdf_; + rtwdf_ = rx; + } + } + } + } +}; + +FFTM.prototype.guessLen13b = function guessLen13b(n, m) { + var N = Math.max(m, n) | 1; + var odd = N & 1; + var i = 0; + for (N = N / 2 | 0; N; N = N >>> 1) { + i++; + } + + return 1 << i + 1 + odd; +}; + +FFTM.prototype.conjugate = function conjugate(rws, iws, N) { + if (N <= 1) return; + + for (var i = 0; i < N / 2; i++) { + var t = rws[i]; + + rws[i] = rws[N - i - 1]; + rws[N - i - 1] = t; + + t = iws[i]; + + iws[i] = -iws[N - i - 1]; + iws[N - i - 1] = -t; + } +}; + +FFTM.prototype.normalize13b = function normalize13b(ws, N) { + var carry = 0; + for (var i = 0; i < N / 2; i++) { + var w = Math.round(ws[2 * i + 1] / N) * 0x2000 + + Math.round(ws[2 * i] / N) + + carry; + + ws[i] = w & 0x3ffffff; + + if (w < 0x4000000) { + carry = 0; + } else { + carry = w / 0x4000000 | 0; + } + } + + return ws; +}; + +FFTM.prototype.convert13b = function convert13b(ws, len, rws, N) { + var carry = 0; + for (var i = 0; i < len; i++) { + carry = carry + (ws[i] | 0); + + rws[2 * i] = carry & 0x1fff; + carry = carry >>> 13; + rws[2 * i + 1] = carry & 0x1fff; + carry = carry >>> 13; + } + + // Pad with zeroes + for (i = 2 * len; i < N; ++i) { + rws[i] = 0; + } + + assert(carry === 0); + assert((carry & ~0x1fff) === 0); +}; + +FFTM.prototype.stub = function stub(N) { + var ph = new Array(N); + for (var i = 0; i < N; i++) { + ph[i] = 0; + } + + return ph; +}; + +FFTM.prototype.mulp = function mulp(x, y, out) { + var N = 2 * this.guessLen13b(x.length, y.length); + + var rbt = this.makeRBT(N); + + var _ = this.stub(N); + + var rws = new Array(N); + var rwst = new Array(N); + var iwst = new Array(N); + + var nrws = new Array(N); + var nrwst = new Array(N); + var niwst = new Array(N); + + var rmws = out.words; + rmws.length = N; + + this.convert13b(x.words, x.length, rws, N); + this.convert13b(y.words, y.length, nrws, N); + + this.transform(rws, _, rwst, iwst, N, rbt); + this.transform(nrws, _, nrwst, niwst, N, rbt); + + for (var i = 0; i < N; i++) { + var rx = rwst[i] * nrwst[i] - iwst[i] * niwst[i]; + iwst[i] = rwst[i] * niwst[i] + iwst[i] * nrwst[i]; + rwst[i] = rx; + } + + this.conjugate(rwst, iwst, N); + this.transform(rwst, iwst, rmws, _, N, rbt); + this.conjugate(rmws, _, N); + this.normalize13b(rmws, N); + + out.negative = x.negative ^ y.negative; + out.length = x.length + y.length; + return out._strip(); +}; + +// Multiply `this` by `num` +BN.prototype.mul = function mul(num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return this.mulTo(num, out); +}; + +// Multiply employing FFT +BN.prototype.mulf = function mulf(num) { + var out = new BN(null); + out.words = new Array(this.length + num.length); + return jumboMulTo(this, num, out); +}; + +// In-place Multiplication +BN.prototype.imul = function imul(num) { + return this.clone().mulTo(num, this); +}; + +BN.prototype.imuln = function imuln(num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(typeof num === "number"); + assert(num < 0x4000000); + + // Carry + var carry = 0; + for (var i = 0; i < this.length; i++) { + var w = (this.words[i] | 0) * num; + var lo = (w & 0x3ffffff) + (carry & 0x3ffffff); + carry >>= 26; + carry += (w / 0x4000000) | 0; + // NOTE: lo is 27bit maximum + carry += lo >>> 26; + this.words[i] = lo & 0x3ffffff; + } + + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + + return isNegNum ? this.ineg() : this; +}; + +BN.prototype.muln = function muln(num) { + return this.clone().imuln(num); +}; + +// `this` * `this` +BN.prototype.sqr = function sqr() { + return this.mul(this); +}; + +// `this` * `this` in-place +BN.prototype.isqr = function isqr() { + return this.imul(this.clone()); +}; + +// Math.pow(`this`, `num`) +BN.prototype.pow = function pow(num) { + var w = toBitArray(num); + if (w.length === 0) return new BN(1); + + // Skip leading zeroes + var res = this; + for (var i = 0; i < w.length; i++, res = res.sqr()) { + if (w[i] !== 0) break; + } + + if (++i < w.length) { + for (var q = res.sqr(); i < w.length; i++, q = q.sqr()) { + if (w[i] === 0) continue; + + res = res.mul(q); + } + } + + return res; +}; + +// Shift-left in-place +BN.prototype.iushln = function iushln(bits) { + assert(typeof bits === "number" && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + var carryMask = (0x3ffffff >>> (26 - r)) << (26 - r); + var i; + + if (r !== 0) { + var carry = 0; + + for (i = 0; i < this.length; i++) { + var newCarry = this.words[i] & carryMask; + var c = ((this.words[i] | 0) - newCarry) << r; + this.words[i] = c | carry; + carry = newCarry >>> (26 - r); + } + + if (carry) { + this.words[i] = carry; + this.length++; + } + } + + if (s !== 0) { + for (i = this.length - 1; i >= 0; i--) { + this.words[i + s] = this.words[i]; + } + + for (i = 0; i < s; i++) { + this.words[i] = 0; + } + + this.length += s; + } + + return this._strip(); +}; + +BN.prototype.ishln = function ishln(bits) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushln(bits); +}; + +// Shift-right in-place +// NOTE: `hint` is a lowest bit before trailing zeroes +// NOTE: if `extended` is present - it will be filled with destroyed bits +BN.prototype.iushrn = function iushrn(bits, hint, extended) { + assert(typeof bits === "number" && bits >= 0); + var h; + if (hint) { + h = (hint - (hint % 26)) / 26; + } else { + h = 0; + } + + var r = bits % 26; + var s = Math.min((bits - r) / 26, this.length); + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + var maskedWords = extended; + + h -= s; + h = Math.max(0, h); + + // Extended mode, copy masked part + if (maskedWords) { + for (var i = 0; i < s; i++) { + maskedWords.words[i] = this.words[i]; + } + maskedWords.length = s; + } + + if (s === 0) { + // No-op, we should not move anything at all + } else if (this.length > s) { + this.length -= s; + for (i = 0; i < this.length; i++) { + this.words[i] = this.words[i + s]; + } + } else { + this.words[0] = 0; + this.length = 1; + } + + var carry = 0; + for (i = this.length - 1; i >= 0 && (carry !== 0 || i >= h); i--) { + var word = this.words[i] | 0; + this.words[i] = (carry << (26 - r)) | (word >>> r); + carry = word & mask; + } + + // Push carried bits as a mask + if (maskedWords && carry !== 0) { + maskedWords.words[maskedWords.length++] = carry; + } + + if (this.length === 0) { + this.words[0] = 0; + this.length = 1; + } + + return this._strip(); +}; + +BN.prototype.ishrn = function ishrn(bits, hint, extended) { + // TODO(indutny): implement me + assert(this.negative === 0); + return this.iushrn(bits, hint, extended); +}; + +// Shift-left +BN.prototype.shln = function shln(bits) { + return this.clone().ishln(bits); +}; + +BN.prototype.ushln = function ushln(bits) { + return this.clone().iushln(bits); +}; + +// Shift-right +BN.prototype.shrn = function shrn(bits) { + return this.clone().ishrn(bits); +}; + +BN.prototype.ushrn = function ushrn(bits) { + return this.clone().iushrn(bits); +}; + +// Test if n bit is set +BN.prototype.testn = function testn(bit) { + assert(typeof bit === "number" && bit >= 0); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) return false; + + // Check bit and return + var w = this.words[s]; + + return !!(w & q); +}; + +// Return only lowers bits of number (in-place) +BN.prototype.imaskn = function imaskn(bits) { + assert(typeof bits === "number" && bits >= 0); + var r = bits % 26; + var s = (bits - r) / 26; + + assert(this.negative === 0, "imaskn works only with positive numbers"); + + if (this.length <= s) { + return this; + } + + if (r !== 0) { + s++; + } + this.length = Math.min(s, this.length); + + if (r !== 0) { + var mask = 0x3ffffff ^ ((0x3ffffff >>> r) << r); + this.words[this.length - 1] &= mask; + } + + return this._strip(); +}; + +// Return only lowers bits of number +BN.prototype.maskn = function maskn(bits) { + return this.clone().imaskn(bits); +}; + +// Add plain number `num` to `this` +BN.prototype.iaddn = function iaddn(num) { + assert(typeof num === "number"); + assert(num < 0x4000000); + if (num < 0) return this.isubn(-num); + + // Possible sign change + if (this.negative !== 0) { + if (this.length === 1 && (this.words[0] | 0) <= num) { + this.words[0] = num - (this.words[0] | 0); + this.negative = 0; + return this; + } + + this.negative = 0; + this.isubn(num); + this.negative = 1; + return this; + } + + // Add without checks + return this._iaddn(num); +}; + +BN.prototype._iaddn = function _iaddn(num) { + this.words[0] += num; + + // Carry + for (var i = 0; i < this.length && this.words[i] >= 0x4000000; i++) { + this.words[i] -= 0x4000000; + if (i === this.length - 1) { + this.words[i + 1] = 1; + } else { + this.words[i + 1]++; + } + } + this.length = Math.max(this.length, i + 1); + + return this; +}; + +// Subtract plain number `num` from `this` +BN.prototype.isubn = function isubn(num) { + assert(typeof num === "number"); + assert(num < 0x4000000); + if (num < 0) return this.iaddn(-num); + + if (this.negative !== 0) { + this.negative = 0; + this.iaddn(num); + this.negative = 1; + return this; + } + + this.words[0] -= num; + + if (this.length === 1 && this.words[0] < 0) { + this.words[0] = -this.words[0]; + this.negative = 1; + } else { + // Carry + for (var i = 0; i < this.length && this.words[i] < 0; i++) { + this.words[i] += 0x4000000; + this.words[i + 1] -= 1; + } + } + + return this._strip(); +}; + +BN.prototype.addn = function addn(num) { + return this.clone().iaddn(num); +}; + +BN.prototype.subn = function subn(num) { + return this.clone().isubn(num); +}; + +BN.prototype.iabs = function iabs() { + this.negative = 0; + + return this; +}; + +BN.prototype.abs = function abs() { + return this.clone().iabs(); +}; + +BN.prototype._ishlnsubmul = function _ishlnsubmul(num, mul, shift) { + var len = num.length + shift; + var i; + + this._expand(len); + + var w; + var carry = 0; + for (i = 0; i < num.length; i++) { + w = (this.words[i + shift] | 0) + carry; + var right = (num.words[i] | 0) * mul; + w -= right & 0x3ffffff; + carry = (w >> 26) - ((right / 0x4000000) | 0); + this.words[i + shift] = w & 0x3ffffff; + } + for (; i < this.length - shift; i++) { + w = (this.words[i + shift] | 0) + carry; + carry = w >> 26; + this.words[i + shift] = w & 0x3ffffff; + } + + if (carry === 0) return this._strip(); + + // Subtraction overflow + assert(carry === -1); + carry = 0; + for (i = 0; i < this.length; i++) { + w = -(this.words[i] | 0) + carry; + carry = w >> 26; + this.words[i] = w & 0x3ffffff; + } + this.negative = 1; + + return this._strip(); +}; + +BN.prototype._wordDiv = function _wordDiv(num, mode) { + var shift = this.length - num.length; + + var a = this.clone(); + var b = num; + + // Normalize + var bhi = b.words[b.length - 1] | 0; + var bhiBits = this._countBits(bhi); + shift = 26 - bhiBits; + if (shift !== 0) { + b = b.ushln(shift); + a.iushln(shift); + bhi = b.words[b.length - 1] | 0; + } + + // Initialize quotient + var m = a.length - b.length; + var q; + + if (mode !== "mod") { + q = new BN(null); + q.length = m + 1; + q.words = new Array(q.length); + for (var i = 0; i < q.length; i++) { + q.words[i] = 0; + } + } + + var diff = a.clone()._ishlnsubmul(b, 1, m); + if (diff.negative === 0) { + a = diff; + if (q) { + q.words[m] = 1; + } + } + + for (var j = m - 1; j >= 0; j--) { + var qj = (a.words[b.length + j] | 0) * 0x4000000 + + (a.words[b.length + j - 1] | 0); + + // NOTE: (qj / bhi) is (0x3ffffff * 0x4000000 + 0x3ffffff) / 0x2000000 max + // (0x7ffffff) + qj = Math.min((qj / bhi) | 0, 0x3ffffff); + + a._ishlnsubmul(b, qj, j); + while (a.negative !== 0) { + qj--; + a.negative = 0; + a._ishlnsubmul(b, 1, j); + if (!a.isZero()) { + a.negative ^= 1; + } + } + if (q) { + q.words[j] = qj; + } + } + if (q) { + q._strip(); + } + a._strip(); + + // Denormalize + if (mode !== "div" && shift !== 0) { + a.iushrn(shift); + } + + return { + div: q || null, + mod: a, + }; +}; + +// NOTE: 1) `mode` can be set to `mod` to request mod only, +// to `div` to request div only, or be absent to +// request both div & mod +// 2) `positive` is true if unsigned mod is requested +BN.prototype.divmod = function divmod(num, mode, positive) { + assert(!num.isZero()); + + if (this.isZero()) { + return { + div: new BN(0), + mod: new BN(0), + }; + } + + var div, mod, res; + if (this.negative !== 0 && num.negative === 0) { + res = this.neg().divmod(num, mode); + + if (mode !== "mod") { + div = res.div.neg(); + } + + if (mode !== "div") { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.iadd(num); + } + } + + return { + div: div, + mod: mod, + }; + } + + if (this.negative === 0 && num.negative !== 0) { + res = this.divmod(num.neg(), mode); + + if (mode !== "mod") { + div = res.div.neg(); + } + + return { + div: div, + mod: res.mod, + }; + } + + if ((this.negative & num.negative) !== 0) { + res = this.neg().divmod(num.neg(), mode); + + if (mode !== "div") { + mod = res.mod.neg(); + if (positive && mod.negative !== 0) { + mod.isub(num); + } + } + + return { + div: res.div, + mod: mod, + }; + } + + // Both numbers are positive at this point + + // Strip both numbers to approximate shift value + if (num.length > this.length || this.cmp(num) < 0) { + return { + div: new BN(0), + mod: this, + }; + } + + // Very short reduction + if (num.length === 1) { + if (mode === "div") { + return { + div: this.divn(num.words[0]), + mod: null, + }; + } + + if (mode === "mod") { + return { + div: null, + mod: new BN(this.modrn(num.words[0])), + }; + } + + return { + div: this.divn(num.words[0]), + mod: new BN(this.modrn(num.words[0])), + }; + } + + return this._wordDiv(num, mode); +}; + +// Find `this` / `num` +BN.prototype.div = function div(num) { + return this.divmod(num, "div", false).div; +}; + +// Find `this` % `num` +BN.prototype.mod = function mod(num) { + return this.divmod(num, "mod", false).mod; +}; + +BN.prototype.umod = function umod(num) { + return this.divmod(num, "mod", true).mod; +}; + +// Find Round(`this` / `num`) +BN.prototype.divRound = function divRound(num) { + var dm = this.divmod(num); + + // Fast case - exact division + if (dm.mod.isZero()) return dm.div; + + var mod = dm.div.negative !== 0 ? dm.mod.isub(num) : dm.mod; + + var half = num.ushrn(1); + var r2 = num.andln(1); + var cmp = mod.cmp(half); + + // Round down + if (cmp < 0 || (r2 === 1 && cmp === 0)) return dm.div; + + // Round up + return dm.div.negative !== 0 ? dm.div.isubn(1) : dm.div.iaddn(1); +}; + +BN.prototype.modrn = function modrn(num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(num <= 0x3ffffff); + var p = (1 << 26) % num; + + var acc = 0; + for (var i = this.length - 1; i >= 0; i--) { + acc = (p * acc + (this.words[i] | 0)) % num; + } + + return isNegNum ? -acc : acc; +}; + +// WARNING: DEPRECATED +BN.prototype.modn = function modn(num) { + return this.modrn(num); +}; + +// In-place division by number +BN.prototype.idivn = function idivn(num) { + var isNegNum = num < 0; + if (isNegNum) num = -num; + + assert(num <= 0x3ffffff); + + var carry = 0; + for (var i = this.length - 1; i >= 0; i--) { + var w = (this.words[i] | 0) + carry * 0x4000000; + this.words[i] = (w / num) | 0; + carry = w % num; + } + + this._strip(); + return isNegNum ? this.ineg() : this; +}; + +BN.prototype.divn = function divn(num) { + return this.clone().idivn(num); +}; + +BN.prototype.egcd = function egcd(p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var x = this; + var y = p.clone(); + + if (x.negative !== 0) { + x = x.umod(p); + } else { + x = x.clone(); + } + + // A * x + B * y = x + var A = new BN(1); + var B = new BN(0); + + // C * x + D * y = y + var C = new BN(0); + var D = new BN(1); + + var g = 0; + + while (x.isEven() && y.isEven()) { + x.iushrn(1); + y.iushrn(1); + ++g; + } + + var yp = y.clone(); + var xp = x.clone(); + + while (!x.isZero()) { + for (var i = 0, im = 1; (x.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + x.iushrn(i); + while (i-- > 0) { + if (A.isOdd() || B.isOdd()) { + A.iadd(yp); + B.isub(xp); + } + + A.iushrn(1); + B.iushrn(1); + } + } + + for (var j = 0, jm = 1; (y.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + y.iushrn(j); + while (j-- > 0) { + if (C.isOdd() || D.isOdd()) { + C.iadd(yp); + D.isub(xp); + } + + C.iushrn(1); + D.iushrn(1); + } + } + + if (x.cmp(y) >= 0) { + x.isub(y); + A.isub(C); + B.isub(D); + } else { + y.isub(x); + C.isub(A); + D.isub(B); + } + } + + return { + a: C, + b: D, + gcd: y.iushln(g), + }; +}; + +// This is reduced incarnation of the binary EEA +// above, designated to invert members of the +// _prime_ fields F(p) at a maximal speed +BN.prototype._invmp = function _invmp(p) { + assert(p.negative === 0); + assert(!p.isZero()); + + var a = this; + var b = p.clone(); + + if (a.negative !== 0) { + a = a.umod(p); + } else { + a = a.clone(); + } + + var x1 = new BN(1); + var x2 = new BN(0); + + var delta = b.clone(); + + while (a.cmpn(1) > 0 && b.cmpn(1) > 0) { + for (var i = 0, im = 1; (a.words[0] & im) === 0 && i < 26; ++i, im <<= 1); + if (i > 0) { + a.iushrn(i); + while (i-- > 0) { + if (x1.isOdd()) { + x1.iadd(delta); + } + + x1.iushrn(1); + } + } + + for (var j = 0, jm = 1; (b.words[0] & jm) === 0 && j < 26; ++j, jm <<= 1); + if (j > 0) { + b.iushrn(j); + while (j-- > 0) { + if (x2.isOdd()) { + x2.iadd(delta); + } + + x2.iushrn(1); + } + } + + if (a.cmp(b) >= 0) { + a.isub(b); + x1.isub(x2); + } else { + b.isub(a); + x2.isub(x1); + } + } + + var res; + if (a.cmpn(1) === 0) { + res = x1; + } else { + res = x2; + } + + if (res.cmpn(0) < 0) { + res.iadd(p); + } + + return res; +}; + +BN.prototype.gcd = function gcd(num) { + if (this.isZero()) return num.abs(); + if (num.isZero()) return this.abs(); + + var a = this.clone(); + var b = num.clone(); + a.negative = 0; + b.negative = 0; + + // Remove common factor of two + for (var shift = 0; a.isEven() && b.isEven(); shift++) { + a.iushrn(1); + b.iushrn(1); + } + + do { + while (a.isEven()) { + a.iushrn(1); + } + while (b.isEven()) { + b.iushrn(1); + } + + var r = a.cmp(b); + if (r < 0) { + // Swap `a` and `b` to make `a` always bigger than `b` + var t = a; + a = b; + b = t; + } else if (r === 0 || b.cmpn(1) === 0) { + break; + } + + a.isub(b); + } while (true); + + return b.iushln(shift); +}; + +// Invert number in the field F(num) +BN.prototype.invm = function invm(num) { + return this.egcd(num).a.umod(num); +}; + +BN.prototype.isEven = function isEven() { + return (this.words[0] & 1) === 0; +}; + +BN.prototype.isOdd = function isOdd() { + return (this.words[0] & 1) === 1; +}; + +// And first word and num +BN.prototype.andln = function andln(num) { + return this.words[0] & num; +}; + +// Increment at the bit position in-line +BN.prototype.bincn = function bincn(bit) { + assert(typeof bit === "number"); + var r = bit % 26; + var s = (bit - r) / 26; + var q = 1 << r; + + // Fast case: bit is much higher than all existing words + if (this.length <= s) { + this._expand(s + 1); + this.words[s] |= q; + return this; + } + + // Add bit and propagate, if needed + var carry = q; + for (var i = s; carry !== 0 && i < this.length; i++) { + var w = this.words[i] | 0; + w += carry; + carry = w >>> 26; + w &= 0x3ffffff; + this.words[i] = w; + } + if (carry !== 0) { + this.words[i] = carry; + this.length++; + } + return this; +}; + +BN.prototype.isZero = function isZero() { + return this.length === 1 && this.words[0] === 0; +}; + +BN.prototype.cmpn = function cmpn(num) { + var negative = num < 0; + + if (this.negative !== 0 && !negative) return -1; + if (this.negative === 0 && negative) return 1; + + this._strip(); + + var res; + if (this.length > 1) { + res = 1; + } else { + if (negative) { + num = -num; + } + + assert(num <= 0x3ffffff, "Number is too big"); + + var w = this.words[0] | 0; + res = w === num ? 0 : w < num ? -1 : 1; + } + if (this.negative !== 0) return -res | 0; + return res; +}; + +// Compare two numbers and return: +// 1 - if `this` > `num` +// 0 - if `this` == `num` +// -1 - if `this` < `num` +BN.prototype.cmp = function cmp(num) { + if (this.negative !== 0 && num.negative === 0) return -1; + if (this.negative === 0 && num.negative !== 0) return 1; + + var res = this.ucmp(num); + if (this.negative !== 0) return -res | 0; + return res; +}; + +// Unsigned comparison +BN.prototype.ucmp = function ucmp(num) { + // At this point both numbers have the same sign + if (this.length > num.length) return 1; + if (this.length < num.length) return -1; + + var res = 0; + for (var i = this.length - 1; i >= 0; i--) { + var a = this.words[i] | 0; + var b = num.words[i] | 0; + + if (a === b) continue; + if (a < b) { + res = -1; + } else if (a > b) { + res = 1; + } + break; + } + return res; +}; + +BN.prototype.gtn = function gtn(num) { + return this.cmpn(num) === 1; +}; + +BN.prototype.gt = function gt(num) { + return this.cmp(num) === 1; +}; + +BN.prototype.gten = function gten(num) { + return this.cmpn(num) >= 0; +}; + +BN.prototype.gte = function gte(num) { + return this.cmp(num) >= 0; +}; + +BN.prototype.ltn = function ltn(num) { + return this.cmpn(num) === -1; +}; + +BN.prototype.lt = function lt(num) { + return this.cmp(num) === -1; +}; + +BN.prototype.lten = function lten(num) { + return this.cmpn(num) <= 0; +}; + +BN.prototype.lte = function lte(num) { + return this.cmp(num) <= 0; +}; + +BN.prototype.eqn = function eqn(num) { + return this.cmpn(num) === 0; +}; + +BN.prototype.eq = function eq(num) { + return this.cmp(num) === 0; +}; + +// +// A reduce context, could be using montgomery or something better, depending +// on the `m` itself. +// +BN.red = function red(num) { + return new Red(num); +}; + +BN.prototype.toRed = function toRed(ctx) { + assert(!this.red, "Already a number in reduction context"); + assert(this.negative === 0, "red works only with positives"); + return ctx.convertTo(this)._forceRed(ctx); +}; + +BN.prototype.fromRed = function fromRed() { + assert(this.red, "fromRed works only with numbers in reduction context"); + return this.red.convertFrom(this); +}; + +BN.prototype._forceRed = function _forceRed(ctx) { + this.red = ctx; + return this; +}; + +BN.prototype.forceRed = function forceRed(ctx) { + assert(!this.red, "Already a number in reduction context"); + return this._forceRed(ctx); +}; + +BN.prototype.redAdd = function redAdd(num) { + assert(this.red, "redAdd works only with red numbers"); + return this.red.add(this, num); +}; + +BN.prototype.redIAdd = function redIAdd(num) { + assert(this.red, "redIAdd works only with red numbers"); + return this.red.iadd(this, num); +}; + +BN.prototype.redSub = function redSub(num) { + assert(this.red, "redSub works only with red numbers"); + return this.red.sub(this, num); +}; + +BN.prototype.redISub = function redISub(num) { + assert(this.red, "redISub works only with red numbers"); + return this.red.isub(this, num); +}; + +BN.prototype.redShl = function redShl(num) { + assert(this.red, "redShl works only with red numbers"); + return this.red.shl(this, num); +}; + +BN.prototype.redMul = function redMul(num) { + assert(this.red, "redMul works only with red numbers"); + this.red._verify2(this, num); + return this.red.mul(this, num); +}; + +BN.prototype.redIMul = function redIMul(num) { + assert(this.red, "redMul works only with red numbers"); + this.red._verify2(this, num); + return this.red.imul(this, num); +}; + +BN.prototype.redSqr = function redSqr() { + assert(this.red, "redSqr works only with red numbers"); + this.red._verify1(this); + return this.red.sqr(this); +}; + +BN.prototype.redISqr = function redISqr() { + assert(this.red, "redISqr works only with red numbers"); + this.red._verify1(this); + return this.red.isqr(this); +}; + +// Square root over p +BN.prototype.redSqrt = function redSqrt() { + assert(this.red, "redSqrt works only with red numbers"); + this.red._verify1(this); + return this.red.sqrt(this); +}; + +BN.prototype.redInvm = function redInvm() { + assert(this.red, "redInvm works only with red numbers"); + this.red._verify1(this); + return this.red.invm(this); +}; + +// Return negative clone of `this` % `red modulo` +BN.prototype.redNeg = function redNeg() { + assert(this.red, "redNeg works only with red numbers"); + this.red._verify1(this); + return this.red.neg(this); +}; + +BN.prototype.redPow = function redPow(num) { + assert(this.red && !num.red, "redPow(normalNum)"); + this.red._verify1(this); + return this.red.pow(this, num); +}; + +// Prime numbers with efficient reduction +var primes = { + k256: null, + p224: null, + p192: null, + p25519: null, +}; + +// Pseudo-Mersenne prime +function MPrime(name, p) { + // P = 2 ^ N - K + this.name = name; + this.p = new BN(p, 16); + this.n = this.p.bitLength(); + this.k = new BN(1).iushln(this.n).isub(this.p); + + this.tmp = this._tmp(); +} + +MPrime.prototype._tmp = function _tmp() { + var tmp = new BN(null); + tmp.words = new Array(Math.ceil(this.n / 13)); + return tmp; +}; + +MPrime.prototype.ireduce = function ireduce(num) { + // Assumes that `num` is less than `P^2` + // num = HI * (2 ^ N - K) + HI * K + LO = HI * K + LO (mod P) + var r = num; + var rlen; + + do { + this.split(r, this.tmp); + r = this.imulK(r); + r = r.iadd(this.tmp); + rlen = r.bitLength(); + } while (rlen > this.n); + + var cmp = rlen < this.n ? -1 : r.ucmp(this.p); + if (cmp === 0) { + r.words[0] = 0; + r.length = 1; + } else if (cmp > 0) { + r.isub(this.p); + } else { + if (r.strip !== undefined) { + // r is a BN v4 instance + r.strip(); + } else { + // r is a BN v5 instance + r._strip(); + } + } + + return r; +}; + +MPrime.prototype.split = function split(input, out) { + input.iushrn(this.n, 0, out); +}; + +MPrime.prototype.imulK = function imulK(num) { + return num.imul(this.k); +}; + +function K256() { + MPrime.call( + this, + "k256", + "ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe fffffc2f", + ); +} +inherits(K256, MPrime); + +K256.prototype.split = function split(input, output) { + // 256 = 9 * 26 + 22 + var mask = 0x3fffff; + + var outLen = Math.min(input.length, 9); + for (var i = 0; i < outLen; i++) { + output.words[i] = input.words[i]; + } + output.length = outLen; + + if (input.length <= 9) { + input.words[0] = 0; + input.length = 1; + return; + } + + // Shift by 9 limbs + var prev = input.words[9]; + output.words[output.length++] = prev & mask; + + for (i = 10; i < input.length; i++) { + var next = input.words[i] | 0; + input.words[i - 10] = ((next & mask) << 4) | (prev >>> 22); + prev = next; + } + prev >>>= 22; + input.words[i - 10] = prev; + if (prev === 0 && input.length > 10) { + input.length -= 10; + } else { + input.length -= 9; + } +}; + +K256.prototype.imulK = function imulK(num) { + // K = 0x1000003d1 = [ 0x40, 0x3d1 ] + num.words[num.length] = 0; + num.words[num.length + 1] = 0; + num.length += 2; + + // bounded at: 0x40 * 0x3ffffff + 0x3d0 = 0x100000390 + var lo = 0; + for (var i = 0; i < num.length; i++) { + var w = num.words[i] | 0; + lo += w * 0x3d1; + num.words[i] = lo & 0x3ffffff; + lo = w * 0x40 + ((lo / 0x4000000) | 0); + } + + // Fast length reduction + if (num.words[num.length - 1] === 0) { + num.length--; + if (num.words[num.length - 1] === 0) { + num.length--; + } + } + return num; +}; + +function P224() { + MPrime.call( + this, + "p224", + "ffffffff ffffffff ffffffff ffffffff 00000000 00000000 00000001", + ); +} +inherits(P224, MPrime); + +function P192() { + MPrime.call( + this, + "p192", + "ffffffff ffffffff ffffffff fffffffe ffffffff ffffffff", + ); +} +inherits(P192, MPrime); + +function P25519() { + // 2 ^ 255 - 19 + MPrime.call( + this, + "25519", + "7fffffffffffffff ffffffffffffffff ffffffffffffffff ffffffffffffffed", + ); +} +inherits(P25519, MPrime); + +P25519.prototype.imulK = function imulK(num) { + // K = 0x13 + var carry = 0; + for (var i = 0; i < num.length; i++) { + var hi = (num.words[i] | 0) * 0x13 + carry; + var lo = hi & 0x3ffffff; + hi >>>= 26; + + num.words[i] = lo; + carry = hi; + } + if (carry !== 0) { + num.words[num.length++] = carry; + } + return num; +}; + +// Exported mostly for testing purposes, use plain name instead +BN._prime = function prime(name) { + // Cached version of prime + if (primes[name]) return primes[name]; + + var prime; + if (name === "k256") { + prime = new K256(); + } else if (name === "p224") { + prime = new P224(); + } else if (name === "p192") { + prime = new P192(); + } else if (name === "p25519") { + prime = new P25519(); + } else { + throw new Error("Unknown prime " + name); + } + primes[name] = prime; + + return prime; +}; + +// +// Base reduction engine +// +function Red(m) { + if (typeof m === "string") { + var prime = BN._prime(m); + this.m = prime.p; + this.prime = prime; + } else { + assert(m.gtn(1), "modulus must be greater than 1"); + this.m = m; + this.prime = null; + } +} + +Red.prototype._verify1 = function _verify1(a) { + assert(a.negative === 0, "red works only with positives"); + assert(a.red, "red works only with red numbers"); +}; + +Red.prototype._verify2 = function _verify2(a, b) { + assert((a.negative | b.negative) === 0, "red works only with positives"); + assert(a.red && a.red === b.red, "red works only with red numbers"); +}; + +Red.prototype.imod = function imod(a) { + if (this.prime) return this.prime.ireduce(a)._forceRed(this); + + move(a, a.umod(this.m)._forceRed(this)); + return a; +}; + +Red.prototype.neg = function neg(a) { + if (a.isZero()) { + return a.clone(); + } + + return this.m.sub(a)._forceRed(this); +}; + +Red.prototype.add = function add(a, b) { + this._verify2(a, b); + + var res = a.add(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res._forceRed(this); +}; + +Red.prototype.iadd = function iadd(a, b) { + this._verify2(a, b); + + var res = a.iadd(b); + if (res.cmp(this.m) >= 0) { + res.isub(this.m); + } + return res; +}; + +Red.prototype.sub = function sub(a, b) { + this._verify2(a, b); + + var res = a.sub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res._forceRed(this); +}; + +Red.prototype.isub = function isub(a, b) { + this._verify2(a, b); + + var res = a.isub(b); + if (res.cmpn(0) < 0) { + res.iadd(this.m); + } + return res; +}; + +Red.prototype.shl = function shl(a, num) { + this._verify1(a); + return this.imod(a.ushln(num)); +}; + +Red.prototype.imul = function imul(a, b) { + this._verify2(a, b); + return this.imod(a.imul(b)); +}; + +Red.prototype.mul = function mul(a, b) { + this._verify2(a, b); + return this.imod(a.mul(b)); +}; + +Red.prototype.isqr = function isqr(a) { + return this.imul(a, a.clone()); +}; + +Red.prototype.sqr = function sqr(a) { + return this.mul(a, a); +}; + +Red.prototype.sqrt = function sqrt(a) { + if (a.isZero()) return a.clone(); + + var mod3 = this.m.andln(3); + assert(mod3 % 2 === 1); + + // Fast case + if (mod3 === 3) { + var pow = this.m.add(new BN(1)).iushrn(2); + return this.pow(a, pow); + } + + // Tonelli-Shanks algorithm (Totally unoptimized and slow) + // + // Find Q and S, that Q * 2 ^ S = (P - 1) + var q = this.m.subn(1); + var s = 0; + while (!q.isZero() && q.andln(1) === 0) { + s++; + q.iushrn(1); + } + assert(!q.isZero()); + + var one = new BN(1).toRed(this); + var nOne = one.redNeg(); + + // Find quadratic non-residue + // NOTE: Max is such because of generalized Riemann hypothesis. + var lpow = this.m.subn(1).iushrn(1); + var z = this.m.bitLength(); + z = new BN(2 * z * z).toRed(this); + + while (this.pow(z, lpow).cmp(nOne) !== 0) { + z.redIAdd(nOne); + } + + var c = this.pow(z, q); + var r = this.pow(a, q.addn(1).iushrn(1)); + var t = this.pow(a, q); + var m = s; + while (t.cmp(one) !== 0) { + var tmp = t; + for (var i = 0; tmp.cmp(one) !== 0; i++) { + tmp = tmp.redSqr(); + } + assert(i < m); + var b = this.pow(c, new BN(1).iushln(m - i - 1)); + + r = r.redMul(b); + c = b.redSqr(); + t = t.redMul(c); + m = i; + } + + return r; +}; + +Red.prototype.invm = function invm(a) { + var inv = a._invmp(this.m); + if (inv.negative !== 0) { + inv.negative = 0; + return this.imod(inv).redNeg(); + } else { + return this.imod(inv); + } +}; + +Red.prototype.pow = function pow(a, num) { + if (num.isZero()) return new BN(1).toRed(this); + if (num.cmpn(1) === 0) return a.clone(); + + var windowSize = 4; + var wnd = new Array(1 << windowSize); + wnd[0] = new BN(1).toRed(this); + wnd[1] = a; + for (var i = 2; i < wnd.length; i++) { + wnd[i] = this.mul(wnd[i - 1], a); + } + + var res = wnd[0]; + var current = 0; + var currentLen = 0; + var start = num.bitLength() % 26; + if (start === 0) { + start = 26; + } + + for (i = num.length - 1; i >= 0; i--) { + var word = num.words[i]; + for (var j = start - 1; j >= 0; j--) { + var bit = (word >> j) & 1; + if (res !== wnd[0]) { + res = this.sqr(res); + } + + if (bit === 0 && current === 0) { + currentLen = 0; + continue; + } + + current <<= 1; + current |= bit; + currentLen++; + if (currentLen !== windowSize && (i !== 0 || j !== 0)) continue; + + res = this.mul(res, wnd[current]); + currentLen = 0; + current = 0; + } + start = 26; + } + + return res; +}; + +Red.prototype.convertTo = function convertTo(num) { + var r = num.umod(this.m); + + return r === num ? r.clone() : r; +}; + +Red.prototype.convertFrom = function convertFrom(num) { + var res = num.clone(); + res.red = null; + return res; +}; + +// +// Montgomery method engine +// + +BN.mont = function mont(num) { + return new Mont(num); +}; + +function Mont(m) { + Red.call(this, m); + + this.shift = this.m.bitLength(); + if (this.shift % 26 !== 0) { + this.shift += 26 - (this.shift % 26); + } + + this.r = new BN(1).iushln(this.shift); + this.r2 = this.imod(this.r.sqr()); + this.rinv = this.r._invmp(this.m); + + this.minv = this.rinv.mul(this.r).isubn(1).div(this.m); + this.minv = this.minv.umod(this.r); + this.minv = this.r.sub(this.minv); +} +inherits(Mont, Red); + +Mont.prototype.convertTo = function convertTo(num) { + return this.imod(num.ushln(this.shift)); +}; + +Mont.prototype.convertFrom = function convertFrom(num) { + var r = this.imod(num.mul(this.rinv)); + r.red = null; + return r; +}; + +Mont.prototype.imul = function imul(a, b) { + if (a.isZero() || b.isZero()) { + a.words[0] = 0; + a.length = 1; + return a; + } + + var t = a.imul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); +}; + +Mont.prototype.mul = function mul(a, b) { + if (a.isZero() || b.isZero()) return new BN(0)._forceRed(this); + + var t = a.mul(b); + var c = t.maskn(this.shift).mul(this.minv).imaskn(this.shift).mul(this.m); + var u = t.isub(c).iushrn(this.shift); + var res = u; + if (u.cmp(this.m) >= 0) { + res = u.isub(this.m); + } else if (u.cmpn(0) < 0) { + res = u.iadd(this.m); + } + + return res._forceRed(this); +}; + +Mont.prototype.invm = function invm(a) { + // (AR)^-1 * R^2 = (A^-1 * R^-1) * R^2 = A^-1 * R + var res = this.imod(a._invmp(this.m).mul(this.r2)); + return res._forceRed(this); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/aes.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/aes.js new file mode 100644 index 0000000000..991b238fe8 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/aes.js @@ -0,0 +1,244 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +// based on the aes implimentation in triple sec +// https://github.com/keybase/triplesec +// which is in turn based on the one from crypto-js +// https://code.google.com/p/crypto-js/ + +// deno-lint-ignore-file no-var no-inner-declarations + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function asUInt32Array(buf) { + if (!Buffer.isBuffer(buf)) buf = Buffer.from(buf); + + var len = (buf.length / 4) | 0; + var out = new Array(len); + + for (var i = 0; i < len; i++) { + out[i] = buf.readUInt32BE(i * 4); + } + + return out; +} + +function scrubVec(v) { + for (var i = 0; i < v.length; v++) { + v[i] = 0; + } +} + +function cryptBlock(M, keySchedule, SUB_MIX, SBOX, nRounds) { + var SUB_MIX0 = SUB_MIX[0]; + var SUB_MIX1 = SUB_MIX[1]; + var SUB_MIX2 = SUB_MIX[2]; + var SUB_MIX3 = SUB_MIX[3]; + + var s0 = M[0] ^ keySchedule[0]; + var s1 = M[1] ^ keySchedule[1]; + var s2 = M[2] ^ keySchedule[2]; + var s3 = M[3] ^ keySchedule[3]; + var t0, t1, t2, t3; + var ksRow = 4; + + for (var round = 1; round < nRounds; round++) { + t0 = SUB_MIX0[s0 >>> 24] ^ SUB_MIX1[(s1 >>> 16) & 0xff] ^ + SUB_MIX2[(s2 >>> 8) & 0xff] ^ SUB_MIX3[s3 & 0xff] ^ keySchedule[ksRow++]; + t1 = SUB_MIX0[s1 >>> 24] ^ SUB_MIX1[(s2 >>> 16) & 0xff] ^ + SUB_MIX2[(s3 >>> 8) & 0xff] ^ SUB_MIX3[s0 & 0xff] ^ keySchedule[ksRow++]; + t2 = SUB_MIX0[s2 >>> 24] ^ SUB_MIX1[(s3 >>> 16) & 0xff] ^ + SUB_MIX2[(s0 >>> 8) & 0xff] ^ SUB_MIX3[s1 & 0xff] ^ keySchedule[ksRow++]; + t3 = SUB_MIX0[s3 >>> 24] ^ SUB_MIX1[(s0 >>> 16) & 0xff] ^ + SUB_MIX2[(s1 >>> 8) & 0xff] ^ SUB_MIX3[s2 & 0xff] ^ keySchedule[ksRow++]; + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + } + + t0 = ((SBOX[s0 >>> 24] << 24) | (SBOX[(s1 >>> 16) & 0xff] << 16) | + (SBOX[(s2 >>> 8) & 0xff] << 8) | SBOX[s3 & 0xff]) ^ keySchedule[ksRow++]; + t1 = ((SBOX[s1 >>> 24] << 24) | (SBOX[(s2 >>> 16) & 0xff] << 16) | + (SBOX[(s3 >>> 8) & 0xff] << 8) | SBOX[s0 & 0xff]) ^ keySchedule[ksRow++]; + t2 = ((SBOX[s2 >>> 24] << 24) | (SBOX[(s3 >>> 16) & 0xff] << 16) | + (SBOX[(s0 >>> 8) & 0xff] << 8) | SBOX[s1 & 0xff]) ^ keySchedule[ksRow++]; + t3 = ((SBOX[s3 >>> 24] << 24) | (SBOX[(s0 >>> 16) & 0xff] << 16) | + (SBOX[(s1 >>> 8) & 0xff] << 8) | SBOX[s2 & 0xff]) ^ keySchedule[ksRow++]; + t0 = t0 >>> 0; + t1 = t1 >>> 0; + t2 = t2 >>> 0; + t3 = t3 >>> 0; + + return [t0, t1, t2, t3]; +} + +// AES constants +var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; +var G = (function () { + // Compute double table + var d = new Array(256); + for (var j = 0; j < 256; j++) { + if (j < 128) { + d[j] = j << 1; + } else { + d[j] = (j << 1) ^ 0x11b; + } + } + + var SBOX = []; + var INV_SBOX = []; + var SUB_MIX = [[], [], [], []]; + var INV_SUB_MIX = [[], [], [], []]; + + // Walk GF(2^8) + var x = 0; + var xi = 0; + for (var i = 0; i < 256; ++i) { + // Compute sbox + var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); + sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; + SBOX[x] = sx; + INV_SBOX[sx] = x; + + // Compute multiplication + var x2 = d[x]; + var x4 = d[x2]; + var x8 = d[x4]; + + // Compute sub bytes, mix columns tables + var t = (d[sx] * 0x101) ^ (sx * 0x1010100); + SUB_MIX[0][x] = (t << 24) | (t >>> 8); + SUB_MIX[1][x] = (t << 16) | (t >>> 16); + SUB_MIX[2][x] = (t << 8) | (t >>> 24); + SUB_MIX[3][x] = t; + + // Compute inv sub bytes, inv mix columns tables + t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); + INV_SUB_MIX[0][sx] = (t << 24) | (t >>> 8); + INV_SUB_MIX[1][sx] = (t << 16) | (t >>> 16); + INV_SUB_MIX[2][sx] = (t << 8) | (t >>> 24); + INV_SUB_MIX[3][sx] = t; + + if (x === 0) { + x = xi = 1; + } else { + x = x2 ^ d[d[d[x8 ^ x2]]]; + xi ^= d[d[xi]]; + } + } + + return { + SBOX: SBOX, + INV_SBOX: INV_SBOX, + SUB_MIX: SUB_MIX, + INV_SUB_MIX: INV_SUB_MIX, + }; +})(); + +export function AES(key) { + this._key = asUInt32Array(key); + this._reset(); +} + +AES.blockSize = 4 * 4; +AES.keySize = 256 / 8; +AES.prototype.blockSize = AES.blockSize; +AES.prototype.keySize = AES.keySize; +AES.prototype._reset = function () { + var keyWords = this._key; + var keySize = keyWords.length; + var nRounds = keySize + 6; + var ksRows = (nRounds + 1) * 4; + + var keySchedule = []; + for (var k = 0; k < keySize; k++) { + keySchedule[k] = keyWords[k]; + } + + for (k = keySize; k < ksRows; k++) { + var t = keySchedule[k - 1]; + + if (k % keySize === 0) { + t = (t << 8) | (t >>> 24); + t = (G.SBOX[t >>> 24] << 24) | + (G.SBOX[(t >>> 16) & 0xff] << 16) | + (G.SBOX[(t >>> 8) & 0xff] << 8) | + (G.SBOX[t & 0xff]); + + t ^= RCON[(k / keySize) | 0] << 24; + } else if (keySize > 6 && k % keySize === 4) { + t = (G.SBOX[t >>> 24] << 24) | + (G.SBOX[(t >>> 16) & 0xff] << 16) | + (G.SBOX[(t >>> 8) & 0xff] << 8) | + (G.SBOX[t & 0xff]); + } + + keySchedule[k] = keySchedule[k - keySize] ^ t; + } + + var invKeySchedule = []; + for (var ik = 0; ik < ksRows; ik++) { + var ksR = ksRows - ik; + var tt = keySchedule[ksR - (ik % 4 ? 0 : 4)]; + + if (ik < 4 || ksR <= 4) { + invKeySchedule[ik] = tt; + } else { + invKeySchedule[ik] = G.INV_SUB_MIX[0][G.SBOX[tt >>> 24]] ^ + G.INV_SUB_MIX[1][G.SBOX[(tt >>> 16) & 0xff]] ^ + G.INV_SUB_MIX[2][G.SBOX[(tt >>> 8) & 0xff]] ^ + G.INV_SUB_MIX[3][G.SBOX[tt & 0xff]]; + } + } + + this._nRounds = nRounds; + this._keySchedule = keySchedule; + this._invKeySchedule = invKeySchedule; +}; + +AES.prototype.encryptBlockRaw = function (M) { + M = asUInt32Array(M); + return cryptBlock(M, this._keySchedule, G.SUB_MIX, G.SBOX, this._nRounds); +}; + +AES.prototype.encryptBlock = function (M) { + var out = this.encryptBlockRaw(M); + var buf = Buffer.allocUnsafe(16); + buf.writeUInt32BE(out[0], 0); + buf.writeUInt32BE(out[1], 4); + buf.writeUInt32BE(out[2], 8); + buf.writeUInt32BE(out[3], 12); + return buf; +}; + +AES.prototype.decryptBlock = function (M) { + M = asUInt32Array(M); + + // swap + var m1 = M[1]; + M[1] = M[3]; + M[3] = m1; + + var out = cryptBlock( + M, + this._invKeySchedule, + G.INV_SUB_MIX, + G.INV_SBOX, + this._nRounds, + ); + var buf = Buffer.allocUnsafe(16); + buf.writeUInt32BE(out[0], 0); + buf.writeUInt32BE(out[3], 4); + buf.writeUInt32BE(out[2], 8); + buf.writeUInt32BE(out[1], 12); + return buf; +}; + +AES.prototype.scrub = function () { + scrubVec(this._keySchedule); + scrubVec(this._invKeySchedule); + scrubVec(this._key); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/auth_cipher.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/auth_cipher.js new file mode 100644 index 0000000000..c4bd5ebe43 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/auth_cipher.js @@ -0,0 +1,146 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +// deno-lint-ignore-file no-var no-inner-declarations + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import * as aes from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/aes.js"; +import Transform from "internal:deno_node/polyfills/_crypto/crypto_browserify/cipher_base.js"; +import { GHASH } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/ghash.js"; +import { xor } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts"; +import { incr32 } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/incr32.js"; + +function xorTest(a, b) { + var out = 0; + if (a.length !== b.length) out++; + + var len = Math.min(a.length, b.length); + for (var i = 0; i < len; ++i) { + out += a[i] ^ b[i]; + } + + return out; +} + +function calcIv(self, iv, ck) { + if (iv.length === 12) { + self._finID = Buffer.concat([iv, Buffer.from([0, 0, 0, 1])]); + return Buffer.concat([iv, Buffer.from([0, 0, 0, 2])]); + } + var ghash = new GHASH(ck); + var len = iv.length; + var toPad = len % 16; + ghash.update(iv); + if (toPad) { + toPad = 16 - toPad; + ghash.update(Buffer.alloc(toPad, 0)); + } + ghash.update(Buffer.alloc(8, 0)); + var ivBits = len * 8; + var tail = Buffer.alloc(8); + // Fixed from the original + // https://github.com/crypto-browserify/browserify-aes/issues/58#issuecomment-451778917 + tail.writeUIntBE(ivBits, 2, 6); + ghash.update(tail); + self._finID = ghash.state; + var out = Buffer.from(self._finID); + incr32(out); + return out; +} +export function StreamCipher(mode, key, iv, decrypt) { + Transform.call(this); + + var h = Buffer.alloc(4, 0); + + this._cipher = new aes.AES(key); + var ck = this._cipher.encryptBlock(h); + this._ghash = new GHASH(ck); + iv = calcIv(this, iv, ck); + + this._prev = Buffer.from(iv); + this._cache = Buffer.allocUnsafe(0); + this._secCache = Buffer.allocUnsafe(0); + this._decrypt = decrypt; + this._alen = 0; + this._len = 0; + this._mode = mode; + + this._authTag = null; + this._called = false; +} + +// StreamCipher inherts Transform +StreamCipher.prototype = Object.create(Transform.prototype, { + constructor: { + value: StreamCipher, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +StreamCipher.prototype._update = function (chunk) { + if (!this._called && this._alen) { + var rump = 16 - (this._alen % 16); + if (rump < 16) { + rump = Buffer.alloc(rump, 0); + this._ghash.update(rump); + } + } + + this._called = true; + var out = this._mode.encrypt(this, chunk); + if (this._decrypt) { + this._ghash.update(chunk); + } else { + this._ghash.update(out); + } + this._len += chunk.length; + return out; +}; + +StreamCipher.prototype._final = function () { + if (this._decrypt && !this._authTag) { + throw new Error("Unsupported state or unable to authenticate data"); + } + + var tag = xor( + this._ghash.final(this._alen * 8, this._len * 8), + this._cipher.encryptBlock(this._finID), + ); + if (this._decrypt && xorTest(tag, this._authTag)) { + throw new Error("Unsupported state or unable to authenticate data"); + } + + this._authTag = tag; + this._cipher.scrub(); +}; + +StreamCipher.prototype.getAuthTag = function getAuthTag() { + if (this._decrypt || !Buffer.isBuffer(this._authTag)) { + throw new Error("Attempting to get auth tag in unsupported state"); + } + + return this._authTag; +}; + +StreamCipher.prototype.setAuthTag = function setAuthTag(tag) { + if (!this._decrypt) { + throw new Error("Attempting to set auth tag in unsupported state"); + } + + this._authTag = tag; +}; + +StreamCipher.prototype.setAAD = function setAAD(buf) { + if (this._called) { + throw new Error("Attempting to set AAD in unsupported state"); + } + + this._ghash.update(buf); + this._alen += buf.length; +}; + +export default StreamCipher; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/decrypter.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/decrypter.js new file mode 100644 index 0000000000..6a92a4574a --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/decrypter.js @@ -0,0 +1,138 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +// deno-lint-ignore-file no-var + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import AuthCipher from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/auth_cipher.js"; +import StreamCipher from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/stream_cipher.js"; +import Transform from "internal:deno_node/polyfills/_crypto/crypto_browserify/cipher_base.js"; +import * as aes from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/aes.js"; +import ebtk from "internal:deno_node/polyfills/_crypto/crypto_browserify/evp_bytes_to_key.ts"; +import { MODES } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/mod.js"; + +function Decipher(mode, key, iv) { + Transform.call(this); + + this._cache = new Splitter(); + this._last = void 0; + this._cipher = new aes.AES(key); + this._prev = Buffer.from(iv); + this._mode = mode; + this._autopadding = true; +} + +Decipher.prototype = Object.create(Transform.prototype, { + constructor: { + value: Decipher, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +Decipher.prototype._update = function (data) { + this._cache.add(data); + var chunk; + var thing; + var out = []; + while ((chunk = this._cache.get(this._autopadding))) { + thing = this._mode.decrypt(this, chunk); + out.push(thing); + } + return Buffer.concat(out); +}; + +Decipher.prototype._final = function () { + var chunk = this._cache.flush(); + if (this._autopadding) { + return unpad(this._mode.decrypt(this, chunk)); + } else if (chunk) { + throw new Error("data not multiple of block length"); + } +}; + +Decipher.prototype.setAutoPadding = function (setTo) { + this._autopadding = !!setTo; + return this; +}; + +function Splitter() { + this.cache = Buffer.allocUnsafe(0); +} + +Splitter.prototype.add = function (data) { + this.cache = Buffer.concat([this.cache, data]); +}; + +Splitter.prototype.get = function (autoPadding) { + var out; + if (autoPadding) { + if (this.cache.length > 16) { + out = this.cache.slice(0, 16); + this.cache = this.cache.slice(16); + return out; + } + } else { + if (this.cache.length >= 16) { + out = this.cache.slice(0, 16); + this.cache = this.cache.slice(16); + return out; + } + } + + return null; +}; + +Splitter.prototype.flush = function () { + if (this.cache.length) return this.cache; +}; + +function unpad(last) { + var padded = last[15]; + if (padded < 1 || padded > 16) { + throw new Error("unable to decrypt data"); + } + var i = -1; + while (++i < padded) { + if (last[i + (16 - padded)] !== padded) { + throw new Error("unable to decrypt data"); + } + } + if (padded === 16) return; + + return last.slice(0, 16 - padded); +} + +export function createDecipheriv(suite, password, iv) { + var config = MODES[suite.toLowerCase()]; + if (!config) throw new TypeError("invalid suite type"); + + if (typeof iv === "string") iv = Buffer.from(iv); + if (config.mode !== "GCM" && iv.length !== config.iv) { + throw new TypeError("invalid iv length " + iv.length); + } + + if (typeof password === "string") password = Buffer.from(password); + if (password.length !== config.key / 8) { + throw new TypeError("invalid key length " + password.length); + } + + if (config.type === "stream") { + return new StreamCipher(config.module, password, iv, true); + } else if (config.type === "auth") { + return new AuthCipher(config.module, password, iv, true); + } + + return new Decipher(config.module, password, iv); +} + +export function createDecipher(suite, password) { + var config = MODES[suite.toLowerCase()]; + if (!config) throw new TypeError("invalid suite type"); + + var keys = ebtk(password, false, config.key, config.iv); + return createDecipheriv(suite, keys.key, keys.iv); +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/encrypter.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/encrypter.js new file mode 100644 index 0000000000..6a95a719c1 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/encrypter.js @@ -0,0 +1,128 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +// deno-lint-ignore-file no-var + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import AuthCipher from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/auth_cipher.js"; +import StreamCipher from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/stream_cipher.js"; +import Transform from "internal:deno_node/polyfills/_crypto/crypto_browserify/cipher_base.js"; +import * as aes from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/aes.js"; +import ebtk from "internal:deno_node/polyfills/_crypto/crypto_browserify/evp_bytes_to_key.ts"; +import { MODES } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/mod.js"; + +function Cipher(mode, key, iv) { + Transform.call(this); + + this._cache = new Splitter(); + this._cipher = new aes.AES(key); + this._prev = Buffer.from(iv); + this._mode = mode; + this._autopadding = true; +} + +Cipher.prototype = Object.create(Transform.prototype, { + constructor: { + value: Cipher, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +Cipher.prototype._update = function (data) { + this._cache.add(data); + var chunk; + var thing; + var out = []; + + while ((chunk = this._cache.get())) { + thing = this._mode.encrypt(this, chunk); + out.push(thing); + } + + return Buffer.concat(out); +}; + +var PADDING = Buffer.alloc(16, 0x10); + +Cipher.prototype._final = function () { + var chunk = this._cache.flush(); + if (this._autopadding) { + chunk = this._mode.encrypt(this, chunk); + this._cipher.scrub(); + return chunk; + } + + if (!chunk.equals(PADDING)) { + this._cipher.scrub(); + throw new Error("data not multiple of block length"); + } +}; + +Cipher.prototype.setAutoPadding = function (setTo) { + this._autopadding = !!setTo; + return this; +}; + +function Splitter() { + this.cache = Buffer.allocUnsafe(0); +} + +Splitter.prototype.add = function (data) { + this.cache = Buffer.concat([this.cache, data]); +}; + +Splitter.prototype.get = function () { + if (this.cache.length > 15) { + const out = this.cache.slice(0, 16); + this.cache = this.cache.slice(16); + return out; + } + return null; +}; + +Splitter.prototype.flush = function () { + var len = 16 - this.cache.length; + var padBuff = Buffer.allocUnsafe(len); + + var i = -1; + while (++i < len) { + padBuff.writeUInt8(len, i); + } + + return Buffer.concat([this.cache, padBuff]); +}; + +export function createCipheriv(suite, password, iv) { + var config = MODES[suite.toLowerCase()]; + if (!config) throw new TypeError("invalid suite type"); + + if (typeof password === "string") password = Buffer.from(password); + if (password.length !== config.key / 8) { + throw new TypeError("invalid key length " + password.length); + } + + if (typeof iv === "string") iv = Buffer.from(iv); + if (config.mode !== "GCM" && iv.length !== config.iv) { + throw new TypeError("invalid iv length " + iv.length); + } + + if (config.type === "stream") { + return new StreamCipher(config.module, password, iv); + } else if (config.type === "auth") { + return new AuthCipher(config.module, password, iv); + } + + return new Cipher(config.module, password, iv); +} + +export function createCipher(suite, password) { + var config = MODES[suite.toLowerCase()]; + if (!config) throw new TypeError("invalid suite type"); + + var keys = ebtk(password, false, config.key, config.iv); + return createCipheriv(suite, keys.key, keys.iv); +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/ghash.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/ghash.js new file mode 100644 index 0000000000..ac896f9212 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/ghash.js @@ -0,0 +1,96 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. +// Copyright 2009-2015, Emily Stark, Mike Hamburg and Dan Boneh at Stanford University. All rights reserved. + +// deno-lint-ignore-file no-var + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +var ZEROES = Buffer.alloc(16, 0); + +function toArray(buf) { + return [ + buf.readUInt32BE(0), + buf.readUInt32BE(4), + buf.readUInt32BE(8), + buf.readUInt32BE(12), + ]; +} + +function fromArray(out) { + var buf = Buffer.allocUnsafe(16); + buf.writeUInt32BE(out[0] >>> 0, 0); + buf.writeUInt32BE(out[1] >>> 0, 4); + buf.writeUInt32BE(out[2] >>> 0, 8); + buf.writeUInt32BE(out[3] >>> 0, 12); + return buf; +} + +export function GHASH(key) { + this.h = key; + this.state = Buffer.alloc(16, 0); + this.cache = Buffer.allocUnsafe(0); +} + +// from http://bitwiseshiftleft.github.io/sjcl/doc/symbols/src/core_gcm.js.html +// by Juho Vähä-Herttua +GHASH.prototype.ghash = function (block) { + var i = -1; + while (++i < block.length) { + this.state[i] ^= block[i]; + } + this._multiply(); +}; + +GHASH.prototype._multiply = function () { + var Vi = toArray(this.h); + var Zi = [0, 0, 0, 0]; + var j, xi, lsbVi; + var i = -1; + while (++i < 128) { + xi = (this.state[~~(i / 8)] & (1 << (7 - (i % 8)))) !== 0; + if (xi) { + // Z_i+1 = Z_i ^ V_i + Zi[0] ^= Vi[0]; + Zi[1] ^= Vi[1]; + Zi[2] ^= Vi[2]; + Zi[3] ^= Vi[3]; + } + + // Store the value of LSB(V_i) + lsbVi = (Vi[3] & 1) !== 0; + + // V_i+1 = V_i >> 1 + for (j = 3; j > 0; j--) { + Vi[j] = (Vi[j] >>> 1) | ((Vi[j - 1] & 1) << 31); + } + Vi[0] = Vi[0] >>> 1; + + // If LSB(V_i) is 1, V_i+1 = (V_i >> 1) ^ R + if (lsbVi) { + Vi[0] = Vi[0] ^ (0xe1 << 24); + } + } + this.state = fromArray(Zi); +}; + +GHASH.prototype.update = function (buf) { + this.cache = Buffer.concat([this.cache, buf]); + var chunk; + while (this.cache.length >= 16) { + chunk = this.cache.slice(0, 16); + this.cache = this.cache.slice(16); + this.ghash(chunk); + } +}; + +GHASH.prototype.final = function (abl, bl) { + if (this.cache.length) { + this.ghash(Buffer.concat([this.cache, ZEROES], 16)); + } + + this.ghash(fromArray([0, abl, 0, bl])); + return this.state; +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/incr32.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/incr32.js new file mode 100644 index 0000000000..65172eefbe --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/incr32.js @@ -0,0 +1,19 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +export function incr32(iv) { + let len = iv.length; + let item; + while (len--) { + item = iv.readUInt8(len); + if (item === 255) { + iv.writeUInt8(0, len); + } else { + item++; + iv.writeUInt8(item, len); + break; + } + } +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/mod.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/mod.js new file mode 100644 index 0000000000..abf5bde5ff --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/mod.js @@ -0,0 +1,13 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { MODES } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/mod.js"; + +export * from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/encrypter.js"; +export * from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/decrypter.js"; + +export function getCiphers() { + return Object.keys(MODES); +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cbc.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cbc.js new file mode 100644 index 0000000000..837adf32fc --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cbc.js @@ -0,0 +1,22 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { xor } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts"; + +export const encrypt = function (self, block) { + const data = xor(block, self._prev); + + self._prev = self._cipher.encryptBlock(data); + return self._prev; +}; + +export const decrypt = function (self, block) { + const pad = self._prev; + + self._prev = block; + const out = self._cipher.decryptBlock(block); + + return xor(out, pad); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb.js new file mode 100644 index 0000000000..8bfd814636 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb.js @@ -0,0 +1,41 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { xor } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function encryptStart(self, data, decrypt) { + const len = data.length; + const out = xor(data, self._cache); + self._cache = self._cache.slice(len); + self._prev = Buffer.concat([self._prev, decrypt ? data : out]); + return out; +} + +export const encrypt = function (self, data, decrypt) { + let out = Buffer.allocUnsafe(0); + let len; + + while (data.length) { + if (self._cache.length === 0) { + self._cache = self._cipher.encryptBlock(self._prev); + self._prev = Buffer.allocUnsafe(0); + } + + if (self._cache.length <= data.length) { + len = self._cache.length; + out = Buffer.concat([ + out, + encryptStart(self, data.slice(0, len), decrypt), + ]); + data = data.slice(len); + } else { + out = Buffer.concat([out, encryptStart(self, data, decrypt)]); + break; + } + } + + return out; +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb1.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb1.js new file mode 100644 index 0000000000..2af8824f0f --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb1.js @@ -0,0 +1,47 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function encryptByte(self, byteParam, decrypt) { + let pad; + let i = -1; + const len = 8; + let out = 0; + let bit, value; + while (++i < len) { + pad = self._cipher.encryptBlock(self._prev); + bit = (byteParam & (1 << (7 - i))) ? 0x80 : 0; + value = pad[0] ^ bit; + out += (value & 0x80) >> (i % 8); + self._prev = shiftIn(self._prev, decrypt ? bit : value); + } + return out; +} + +function shiftIn(buffer, value) { + const len = buffer.length; + let i = -1; + const out = Buffer.allocUnsafe(buffer.length); + buffer = Buffer.concat([buffer, Buffer.from([value])]); + + while (++i < len) { + out[i] = buffer[i] << 1 | buffer[i + 1] >> (7); + } + + return out; +} + +export const encrypt = function (self, chunk, decrypt) { + const len = chunk.length; + const out = Buffer.allocUnsafe(len); + let i = -1; + + while (++i < len) { + out[i] = encryptByte(self, chunk[i], decrypt); + } + + return out; +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb8.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb8.js new file mode 100644 index 0000000000..262494a813 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb8.js @@ -0,0 +1,30 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function encryptByte(self, byteParam, decrypt) { + const pad = self._cipher.encryptBlock(self._prev); + const out = pad[0] ^ byteParam; + + self._prev = Buffer.concat([ + self._prev.slice(1), + Buffer.from([decrypt ? byteParam : out]), + ]); + + return out; +} + +export const encrypt = function (self, chunk, decrypt) { + const len = chunk.length; + const out = Buffer.allocUnsafe(len); + let i = -1; + + while (++i < len) { + out[i] = encryptByte(self, chunk[i], decrypt); + } + + return out; +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ctr.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ctr.js new file mode 100644 index 0000000000..fd1781ab2f --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ctr.js @@ -0,0 +1,35 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { xor } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { incr32 } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/incr32.js"; + +function getBlock(self) { + const out = self._cipher.encryptBlockRaw(self._prev); + incr32(self._prev); + return out; +} + +const blockSize = 16; +export const encrypt = function (self, chunk) { + const chunkNum = Math.ceil(chunk.length / blockSize); + const start = self._cache.length; + self._cache = Buffer.concat([ + self._cache, + Buffer.allocUnsafe(chunkNum * blockSize), + ]); + for (let i = 0; i < chunkNum; i++) { + const out = getBlock(self); + const offset = start + i * blockSize; + self._cache.writeUInt32BE(out[0], offset + 0); + self._cache.writeUInt32BE(out[1], offset + 4); + self._cache.writeUInt32BE(out[2], offset + 8); + self._cache.writeUInt32BE(out[3], offset + 12); + } + const pad = self._cache.slice(0, chunk.length); + self._cache = self._cache.slice(chunk.length); + return xor(chunk, pad); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ecb.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ecb.js new file mode 100644 index 0000000000..b4f99cffb0 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ecb.js @@ -0,0 +1,12 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +export const encrypt = function (self, block) { + return self._cipher.encryptBlock(block); +}; + +export const decrypt = function (self, block) { + return self._cipher.decryptBlock(block); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/mod.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/mod.js new file mode 100644 index 0000000000..b30ed0b258 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/mod.js @@ -0,0 +1,221 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import * as ECB from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ecb.js"; +import * as CBC from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cbc.js"; +import * as CFB from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb.js"; +import * as CFB8 from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb8.js"; +import * as CFB1 from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/cfb1.js"; +import * as OFB from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ofb.js"; +import * as CTR from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ctr.js"; + +const GCM = CTR; + +const modeModules = { + ECB, + CBC, + CFB, + CFB8, + CFB1, + OFB, + CTR, + GCM, +}; + +export const MODES = { + "aes-128-ecb": { + "cipher": "AES", + "key": 128, + "iv": 0, + "mode": "ECB", + "type": "block", + }, + "aes-192-ecb": { + "cipher": "AES", + "key": 192, + "iv": 0, + "mode": "ECB", + "type": "block", + }, + "aes-256-ecb": { + "cipher": "AES", + "key": 256, + "iv": 0, + "mode": "ECB", + "type": "block", + }, + "aes-128-cbc": { + "cipher": "AES", + "key": 128, + "iv": 16, + "mode": "CBC", + "type": "block", + }, + "aes-192-cbc": { + "cipher": "AES", + "key": 192, + "iv": 16, + "mode": "CBC", + "type": "block", + }, + "aes-256-cbc": { + "cipher": "AES", + "key": 256, + "iv": 16, + "mode": "CBC", + "type": "block", + }, + "aes128": { + "cipher": "AES", + "key": 128, + "iv": 16, + "mode": "CBC", + "type": "block", + }, + "aes192": { + "cipher": "AES", + "key": 192, + "iv": 16, + "mode": "CBC", + "type": "block", + }, + "aes256": { + "cipher": "AES", + "key": 256, + "iv": 16, + "mode": "CBC", + "type": "block", + }, + "aes-128-cfb": { + "cipher": "AES", + "key": 128, + "iv": 16, + "mode": "CFB", + "type": "stream", + }, + "aes-192-cfb": { + "cipher": "AES", + "key": 192, + "iv": 16, + "mode": "CFB", + "type": "stream", + }, + "aes-256-cfb": { + "cipher": "AES", + "key": 256, + "iv": 16, + "mode": "CFB", + "type": "stream", + }, + "aes-128-cfb8": { + "cipher": "AES", + "key": 128, + "iv": 16, + "mode": "CFB8", + "type": "stream", + }, + "aes-192-cfb8": { + "cipher": "AES", + "key": 192, + "iv": 16, + "mode": "CFB8", + "type": "stream", + }, + "aes-256-cfb8": { + "cipher": "AES", + "key": 256, + "iv": 16, + "mode": "CFB8", + "type": "stream", + }, + "aes-128-cfb1": { + "cipher": "AES", + "key": 128, + "iv": 16, + "mode": "CFB1", + "type": "stream", + }, + "aes-192-cfb1": { + "cipher": "AES", + "key": 192, + "iv": 16, + "mode": "CFB1", + "type": "stream", + }, + "aes-256-cfb1": { + "cipher": "AES", + "key": 256, + "iv": 16, + "mode": "CFB1", + "type": "stream", + }, + "aes-128-ofb": { + "cipher": "AES", + "key": 128, + "iv": 16, + "mode": "OFB", + "type": "stream", + }, + "aes-192-ofb": { + "cipher": "AES", + "key": 192, + "iv": 16, + "mode": "OFB", + "type": "stream", + }, + "aes-256-ofb": { + "cipher": "AES", + "key": 256, + "iv": 16, + "mode": "OFB", + "type": "stream", + }, + "aes-128-ctr": { + "cipher": "AES", + "key": 128, + "iv": 16, + "mode": "CTR", + "type": "stream", + }, + "aes-192-ctr": { + "cipher": "AES", + "key": 192, + "iv": 16, + "mode": "CTR", + "type": "stream", + }, + "aes-256-ctr": { + "cipher": "AES", + "key": 256, + "iv": 16, + "mode": "CTR", + "type": "stream", + }, + "aes-128-gcm": { + "cipher": "AES", + "key": 128, + "iv": 12, + "mode": "GCM", + "type": "auth", + }, + "aes-192-gcm": { + "cipher": "AES", + "key": 192, + "iv": 12, + "mode": "GCM", + "type": "auth", + }, + "aes-256-gcm": { + "cipher": "AES", + "key": 256, + "iv": 12, + "mode": "GCM", + "type": "auth", + }, +}; + +for (const mode of Object.values(MODES)) { + mode.module = modeModules[mode.mode]; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ofb.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ofb.js new file mode 100644 index 0000000000..20ccdf0151 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/modes/ofb.js @@ -0,0 +1,22 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { xor } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function getBlock(self) { + self._prev = self._cipher.encryptBlock(self._prev); + return self._prev; +} + +export const encrypt = function (self, chunk) { + while (self._cache.length < chunk.length) { + self._cache = Buffer.concat([self._cache, getBlock(self)]); + } + + const pad = self._cache.slice(0, chunk.length); + self._cache = self._cache.slice(chunk.length); + return xor(chunk, pad); +}; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/stream_cipher.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/stream_cipher.js new file mode 100644 index 0000000000..432730f915 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/stream_cipher.js @@ -0,0 +1,40 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +import * as aes from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/aes.js"; +import Transform from "internal:deno_node/polyfills/_crypto/crypto_browserify/cipher_base.js"; + +export function StreamCipher(mode, key, iv, decrypt) { + Transform.call(this); + + this._cipher = new aes.AES(key); + this._prev = Buffer.from(iv); + this._cache = Buffer.allocUnsafe(0); + this._secCache = Buffer.allocUnsafe(0); + this._decrypt = decrypt; + this._mode = mode; +} + +// StreamCipher inherits Transform +StreamCipher.prototype = Object.create(Transform.prototype, { + constructor: { + value: StreamCipher, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +StreamCipher.prototype._update = function (chunk) { + return this._mode.encrypt(this, chunk, this._decrypt); +}; + +StreamCipher.prototype._final = function () { + this._cipher.scrub(); +}; + +export default StreamCipher; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts new file mode 100644 index 0000000000..6b9327ebc7 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_aes/xor.ts @@ -0,0 +1,17 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2014-2017 browserify-aes contributors. All rights reserved. MIT license. +// Copyright 2013 Maxwell Krohn. All rights reserved. MIT license. +// Copyright 2009-2013 Jeff Mott. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export function xor(a: Buffer, b: Buffer): Buffer { + const length = Math.min(a.length, b.length); + const buffer = Buffer.allocUnsafe(length); + + for (let i = 0; i < length; ++i) { + buffer[i] = a[i] ^ b[i]; + } + + return buffer; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/browserify_rsa.js b/ext/node/polyfills/_crypto/crypto_browserify/browserify_rsa.js new file mode 100644 index 0000000000..b158462b85 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/browserify_rsa.js @@ -0,0 +1,47 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. + +import { BN } from "internal:deno_node/polyfills/_crypto/crypto_browserify/bn.js/bn.js"; +import { randomBytes } from "internal:deno_node/polyfills/_crypto/crypto_browserify/randombytes.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function blind(priv) { + const r = getr(priv); + const blinder = r.toRed(BN.mont(priv.modulus)).redPow( + new BN(priv.publicExponent), + ).fromRed(); + return { blinder: blinder, unblinder: r.invm(priv.modulus) }; +} + +function getr(priv) { + const len = priv.modulus.byteLength(); + let r; + do { + r = new BN(randomBytes(len)); + } while ( + r.cmp(priv.modulus) >= 0 || !r.umod(priv.prime1) || !r.umod(priv.prime2) + ); + return r; +} + +function crt(msg, priv) { + const blinds = blind(priv); + const len = priv.modulus.byteLength(); + const blinded = new BN(msg).mul(blinds.blinder).umod(priv.modulus); + const c1 = blinded.toRed(BN.mont(priv.prime1)); + const c2 = blinded.toRed(BN.mont(priv.prime2)); + const qinv = priv.coefficient; + const p = priv.prime1; + const q = priv.prime2; + const m1 = c1.redPow(priv.exponent1).fromRed(); + const m2 = c2.redPow(priv.exponent2).fromRed(); + const h = m1.isub(m2).imul(qinv).umod(p).imul(q); + return m2.iadd(h).imul(blinds.unblinder).umod(priv.modulus).toArrayLike( + Buffer, + "be", + len, + ); +} +crt.getr = getr; + +export default crt; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/cipher_base.js b/ext/node/polyfills/_crypto/crypto_browserify/cipher_base.js new file mode 100644 index 0000000000..ceb143fc51 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/cipher_base.js @@ -0,0 +1,110 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. +// deno-lint-ignore-file no-var + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { Transform } from "internal:deno_node/polyfills/stream.ts"; +import { StringDecoder } from "internal:deno_node/polyfills/string_decoder.ts"; + +export function CipherBase(hashMode) { + Transform.call(this); + this.hashMode = typeof hashMode === "string"; + if (this.hashMode) { + this[hashMode] = this._finalOrDigest; + } else { + this.final = this._finalOrDigest; + } + if (this._final) { + this.__final = this._final; + this._final = null; + } + this._decoder = null; + this._encoding = null; +} +// inherits(CipherBase, Transform) +CipherBase.prototype = Object.create(Transform.prototype, { + constructor: { + value: CipherBase, + enumerable: false, + writable: true, + configurable: true, + }, +}); + +CipherBase.prototype.update = function (data, inputEnc, outputEnc) { + if (typeof data === "string") { + data = Buffer.from(data, inputEnc); + } + + var outData = this._update(data); + if (this.hashMode) return this; + + if (outputEnc) { + outData = this._toString(outData, outputEnc); + } + + return outData; +}; + +CipherBase.prototype.setAutoPadding = function () {}; +CipherBase.prototype.getAuthTag = function () { + throw new Error("trying to get auth tag in unsupported state"); +}; + +CipherBase.prototype.setAuthTag = function () { + throw new Error("trying to set auth tag in unsupported state"); +}; + +CipherBase.prototype.setAAD = function () { + throw new Error("trying to set aad in unsupported state"); +}; + +CipherBase.prototype._transform = function (data, _, next) { + var err; + try { + if (this.hashMode) { + this._update(data); + } else { + this.push(this._update(data)); + } + } catch (e) { + err = e; + } finally { + next(err); + } +}; +CipherBase.prototype._flush = function (done) { + var err; + try { + this.push(this.__final()); + } catch (e) { + err = e; + } + + done(err); +}; +CipherBase.prototype._finalOrDigest = function (outputEnc) { + var outData = this.__final() || Buffer.alloc(0); + if (outputEnc) { + outData = this._toString(outData, outputEnc, true); + } + return outData; +}; + +CipherBase.prototype._toString = function (value, enc, fin) { + if (!this._decoder) { + this._decoder = new StringDecoder(enc); + this._encoding = enc; + } + + if (this._encoding !== enc) throw new Error("can't switch encodings"); + + var out = this._decoder.write(value); + if (fin) { + out += this._decoder.end(); + } + + return out; +}; + +export default CipherBase; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/evp_bytes_to_key.ts b/ext/node/polyfills/_crypto/crypto_browserify/evp_bytes_to_key.ts new file mode 100644 index 0000000000..a8599651a3 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/evp_bytes_to_key.ts @@ -0,0 +1,55 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { createHash } from "internal:deno_node/polyfills/internal/crypto/hash.ts"; + +// deno-lint-ignore camelcase +export function EVP_BytesToKey( + password: string | Buffer, + salt: string | Buffer, + keyBits: number, + ivLen: number, +) { + if (!Buffer.isBuffer(password)) password = Buffer.from(password, "binary"); + if (salt) { + if (!Buffer.isBuffer(salt)) salt = Buffer.from(salt, "binary"); + if (salt.length !== 8) { + throw new RangeError("salt should be Buffer with 8 byte length"); + } + } + + let keyLen = keyBits / 8; + const key = Buffer.alloc(keyLen); + const iv = Buffer.alloc(ivLen || 0); + let tmp = Buffer.alloc(0); + + while (keyLen > 0 || ivLen > 0) { + const hash = createHash("md5"); + hash.update(tmp); + hash.update(password); + if (salt) hash.update(salt); + tmp = hash.digest() as Buffer; + + let used = 0; + + if (keyLen > 0) { + const keyStart = key.length - keyLen; + used = Math.min(keyLen, tmp.length); + tmp.copy(key, keyStart, 0, used); + keyLen -= used; + } + + if (used < tmp.length && ivLen > 0) { + const ivStart = iv.length - ivLen; + const length = Math.min(ivLen, tmp.length - used); + tmp.copy(iv, ivStart, used, used + length); + ivLen -= length; + } + } + + tmp.fill(0); + return { key, iv }; +} + +export default EVP_BytesToKey; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/package.json b/ext/node/polyfills/_crypto/crypto_browserify/package.json new file mode 100644 index 0000000000..db986192c4 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/package.json @@ -0,0 +1,4 @@ +{ + "//": "Sets type module to make compat mode interpret .js as ESM", + "type": "module" +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/asn1.js b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/asn1.js new file mode 100644 index 0000000000..9023cf2595 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/asn1.js @@ -0,0 +1,117 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. +// from https://github.com/crypto-browserify/parse-asn1/blob/fbd70dca8670d17955893e083ca69118908570be/asn1.js + +import asn1 from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/mod.js"; +import certificate from "internal:deno_node/polyfills/_crypto/crypto_browserify/parse_asn1/certificate.js"; +export { certificate }; + +export const RSAPrivateKey = asn1.define("RSAPrivateKey", function () { + this.seq().obj( + this.key("version").int(), + this.key("modulus").int(), + this.key("publicExponent").int(), + this.key("privateExponent").int(), + this.key("prime1").int(), + this.key("prime2").int(), + this.key("exponent1").int(), + this.key("exponent2").int(), + this.key("coefficient").int(), + ); +}); + +export const RSAPublicKey = asn1.define("RSAPublicKey", function () { + this.seq().obj( + this.key("modulus").int(), + this.key("publicExponent").int(), + ); +}); + +export const PublicKey = asn1.define("SubjectPublicKeyInfo", function () { + this.seq().obj( + this.key("algorithm").use(AlgorithmIdentifier), + this.key("subjectPublicKey").bitstr(), + ); +}); + +const AlgorithmIdentifier = asn1.define("AlgorithmIdentifier", function () { + this.seq().obj( + this.key("algorithm").objid(), + this.key("none").null_().optional(), + this.key("curve").objid().optional(), + this.key("params").seq().obj( + this.key("p").int(), + this.key("q").int(), + this.key("g").int(), + ).optional(), + ); +}); + +export const PrivateKey = asn1.define("PrivateKeyInfo", function () { + this.seq().obj( + this.key("version").int(), + this.key("algorithm").use(AlgorithmIdentifier), + this.key("subjectPrivateKey").octstr(), + ); +}); +export const EncryptedPrivateKey = asn1.define( + "EncryptedPrivateKeyInfo", + function () { + this.seq().obj( + this.key("algorithm").seq().obj( + this.key("id").objid(), + this.key("decrypt").seq().obj( + this.key("kde").seq().obj( + this.key("id").objid(), + this.key("kdeparams").seq().obj( + this.key("salt").octstr(), + this.key("iters").int(), + ), + ), + this.key("cipher").seq().obj( + this.key("algo").objid(), + this.key("iv").octstr(), + ), + ), + ), + this.key("subjectPrivateKey").octstr(), + ); + }, +); + +export const DSAPrivateKey = asn1.define("DSAPrivateKey", function () { + this.seq().obj( + this.key("version").int(), + this.key("p").int(), + this.key("q").int(), + this.key("g").int(), + this.key("pub_key").int(), + this.key("priv_key").int(), + ); +}); + +export const DSAparam = asn1.define("DSAparam", function () { + this.int(); +}); + +export const ECPrivateKey = asn1.define("ECPrivateKey", function () { + this.seq().obj( + this.key("version").int(), + this.key("privateKey").octstr(), + this.key("parameters").optional().explicit(0).use(ECParameters), + this.key("publicKey").optional().explicit(1).bitstr(), + ); +}); + +const ECParameters = asn1.define("ECParameters", function () { + this.choice({ + namedCurve: this.objid(), + }); +}); + +export const signature = asn1.define("signature", function () { + this.seq().obj( + this.key("r").int(), + this.key("s").int(), + ); +}); diff --git a/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/certificate.js b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/certificate.js new file mode 100644 index 0000000000..484aa41c46 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/certificate.js @@ -0,0 +1,91 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. +// from https://github.com/crypto-browserify/parse-asn1/blob/fbd70dca8670d17955893e083ca69118908570be/certificate.js + +import * as asn from "internal:deno_node/polyfills/_crypto/crypto_browserify/asn1.js/mod.js"; + +const Time = asn.define("Time", function () { + this.choice({ + utcTime: this.utctime(), + generalTime: this.gentime(), + }); +}); + +const AttributeTypeValue = asn.define("AttributeTypeValue", function () { + this.seq().obj( + this.key("type").objid(), + this.key("value").any(), + ); +}); + +const AlgorithmIdentifier = asn.define("AlgorithmIdentifier", function () { + this.seq().obj( + this.key("algorithm").objid(), + this.key("parameters").optional(), + this.key("curve").objid().optional(), + ); +}); + +const SubjectPublicKeyInfo = asn.define("SubjectPublicKeyInfo", function () { + this.seq().obj( + this.key("algorithm").use(AlgorithmIdentifier), + this.key("subjectPublicKey").bitstr(), + ); +}); + +const RelativeDistinguishedName = asn.define( + "RelativeDistinguishedName", + function () { + this.setof(AttributeTypeValue); + }, +); + +const RDNSequence = asn.define("RDNSequence", function () { + this.seqof(RelativeDistinguishedName); +}); + +const Name = asn.define("Name", function () { + this.choice({ + rdnSequence: this.use(RDNSequence), + }); +}); + +const Validity = asn.define("Validity", function () { + this.seq().obj( + this.key("notBefore").use(Time), + this.key("notAfter").use(Time), + ); +}); + +const Extension = asn.define("Extension", function () { + this.seq().obj( + this.key("extnID").objid(), + this.key("critical").bool().def(false), + this.key("extnValue").octstr(), + ); +}); + +const TBSCertificate = asn.define("TBSCertificate", function () { + this.seq().obj( + this.key("version").explicit(0).int().optional(), + this.key("serialNumber").int(), + this.key("signature").use(AlgorithmIdentifier), + this.key("issuer").use(Name), + this.key("validity").use(Validity), + this.key("subject").use(Name), + this.key("subjectPublicKeyInfo").use(SubjectPublicKeyInfo), + this.key("issuerUniqueID").implicit(1).bitstr().optional(), + this.key("subjectUniqueID").implicit(2).bitstr().optional(), + this.key("extensions").explicit(3).seqof(Extension).optional(), + ); +}); + +export const X509Certificate = asn.define("X509Certificate", function () { + this.seq().obj( + this.key("tbsCertificate").use(TBSCertificate), + this.key("signatureAlgorithm").use(AlgorithmIdentifier), + this.key("signatureValue").bitstr(), + ); +}); + +export default X509Certificate; diff --git a/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/fix_proc.js b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/fix_proc.js new file mode 100644 index 0000000000..9c78f7bb27 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/fix_proc.js @@ -0,0 +1,37 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. +// from https://github.com/crypto-browserify/parse-asn1/blob/fbd70dca8670d17955893e083ca69118908570be/fixProc.js + +import evp from "internal:deno_node/polyfills/_crypto/crypto_browserify/evp_bytes_to_key.ts"; +import * as ciphers from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/mod.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +const findProc = + /Proc-Type: 4,ENCRYPTED[\n\r]+DEK-Info: AES-((?:128)|(?:192)|(?:256))-CBC,([0-9A-H]+)[\n\r]+([0-9A-z\n\r+/=]+)[\n\r]+/m; +const startRegex = /^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----/m; +const fullRegex = + /^-----BEGIN ((?:.*? KEY)|CERTIFICATE)-----([0-9A-z\n\r+/=]+)-----END \1-----$/m; +export default function (okey, password) { + const key = okey.toString(); + const match = key.match(findProc); + let decrypted; + if (!match) { + const match2 = key.match(fullRegex); + decrypted = Buffer.from(match2[2].replace(/[\r\n]/g, ""), "base64"); + } else { + const suite = "aes" + match[1]; + const iv = Buffer.from(match[2], "hex"); + const cipherText = Buffer.from(match[3].replace(/[\r\n]/g, ""), "base64"); + const cipherKey = evp(password, iv.slice(0, 8), parseInt(match[1], 10)).key; + const out = []; + const cipher = ciphers.createDecipheriv(suite, cipherKey, iv); + out.push(cipher.update(cipherText)); + out.push(cipher.final()); + decrypted = Buffer.concat(out); + } + const tag = key.match(startRegex)[1]; + return { + tag: tag, + data: decrypted, + }; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/mod.js b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/mod.js new file mode 100644 index 0000000000..66aa2227f2 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/mod.js @@ -0,0 +1,138 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. +// from https://github.com/crypto-browserify/parse-asn1/blob/fbd70dca8670d17955893e083ca69118908570be/index.js + +import * as asn1 from "internal:deno_node/polyfills/_crypto/crypto_browserify/parse_asn1/asn1.js"; +import fixProc from "internal:deno_node/polyfills/_crypto/crypto_browserify/parse_asn1/fix_proc.js"; +import * as ciphers from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/mod.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { pbkdf2Sync } from "internal:deno_node/polyfills/internal/crypto/pbkdf2.ts"; + +const aesid = { + "2.16.840.1.101.3.4.1.1": "aes-128-ecb", + "2.16.840.1.101.3.4.1.2": "aes-128-cbc", + "2.16.840.1.101.3.4.1.3": "aes-128-ofb", + "2.16.840.1.101.3.4.1.4": "aes-128-cfb", + "2.16.840.1.101.3.4.1.21": "aes-192-ecb", + "2.16.840.1.101.3.4.1.22": "aes-192-cbc", + "2.16.840.1.101.3.4.1.23": "aes-192-ofb", + "2.16.840.1.101.3.4.1.24": "aes-192-cfb", + "2.16.840.1.101.3.4.1.41": "aes-256-ecb", + "2.16.840.1.101.3.4.1.42": "aes-256-cbc", + "2.16.840.1.101.3.4.1.43": "aes-256-ofb", + "2.16.840.1.101.3.4.1.44": "aes-256-cfb", +}; +export function parseKeys(buffer) { + let password; + if (typeof buffer === "object" && !Buffer.isBuffer(buffer)) { + password = buffer.passphrase; + buffer = buffer.key; + } + if (typeof buffer === "string") { + buffer = Buffer.from(buffer); + } + + const stripped = fixProc(buffer, password); + + const type = stripped.tag; + let data = stripped.data; + let subtype, ndata; + switch (type) { + case "CERTIFICATE": + ndata = asn1.certificate.decode(data, "der").tbsCertificate + .subjectPublicKeyInfo; + // falls through + case "PUBLIC KEY": + if (!ndata) { + ndata = asn1.PublicKey.decode(data, "der"); + } + subtype = ndata.algorithm.algorithm.join("."); + switch (subtype) { + case "1.2.840.113549.1.1.1": + return asn1.RSAPublicKey.decode(ndata.subjectPublicKey.data, "der"); + case "1.2.840.10045.2.1": + ndata.subjectPrivateKey = ndata.subjectPublicKey; + return { + type: "ec", + data: ndata, + }; + case "1.2.840.10040.4.1": + ndata.algorithm.params.pub_key = asn1.DSAparam.decode( + ndata.subjectPublicKey.data, + "der", + ); + return { + type: "dsa", + data: ndata.algorithm.params, + }; + default: + throw new Error("unknown key id " + subtype); + } + // throw new Error('unknown key type ' + type) + case "ENCRYPTED PRIVATE KEY": + data = asn1.EncryptedPrivateKey.decode(data, "der"); + data = decrypt(data, password); + // falls through + case "PRIVATE KEY": + ndata = asn1.PrivateKey.decode(data, "der"); + subtype = ndata.algorithm.algorithm.join("."); + switch (subtype) { + case "1.2.840.113549.1.1.1": + return asn1.RSAPrivateKey.decode(ndata.subjectPrivateKey, "der"); + case "1.2.840.10045.2.1": + return { + curve: ndata.algorithm.curve, + privateKey: asn1.ECPrivateKey.decode(ndata.subjectPrivateKey, "der") + .privateKey, + }; + case "1.2.840.10040.4.1": + ndata.algorithm.params.priv_key = asn1.DSAparam.decode( + ndata.subjectPrivateKey, + "der", + ); + return { + type: "dsa", + params: ndata.algorithm.params, + }; + default: + throw new Error("unknown key id " + subtype); + } + // throw new Error('unknown key type ' + type) + case "RSA PUBLIC KEY": + return asn1.RSAPublicKey.decode(data, "der"); + case "RSA PRIVATE KEY": + return asn1.RSAPrivateKey.decode(data, "der"); + case "DSA PRIVATE KEY": + return { + type: "dsa", + params: asn1.DSAPrivateKey.decode(data, "der"), + }; + case "EC PRIVATE KEY": + data = asn1.ECPrivateKey.decode(data, "der"); + return { + curve: data.parameters.value, + privateKey: data.privateKey, + }; + default: + throw new Error("unknown key type " + type); + } +} +export default parseKeys; +parseKeys.signature = asn1.signature; +function decrypt(data, password) { + const salt = data.algorithm.decrypt.kde.kdeparams.salt; + const iters = parseInt( + data.algorithm.decrypt.kde.kdeparams.iters.toString(), + 10, + ); + const algo = aesid[data.algorithm.decrypt.cipher.algo.join(".")]; + const iv = data.algorithm.decrypt.cipher.iv; + const cipherText = data.subjectPrivateKey; + const keylen = parseInt(algo.split("-")[1], 10) / 8; + const key = pbkdf2Sync(password, salt, iters, keylen, "sha1"); + const cipher = ciphers.createDecipheriv(algo, key, iv); + const out = []; + out.push(cipher.update(cipherText)); + out.push(cipher.final()); + return Buffer.concat(out); +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/mgf.js b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/mgf.js new file mode 100644 index 0000000000..5bb41f8963 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/mgf.js @@ -0,0 +1,22 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Calvin Metcalf. All rights reserved. MIT license. + +import { createHash } from "internal:deno_node/polyfills/internal/crypto/hash.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export default function (seed, len) { + let t = Buffer.alloc(0); + let i = 0; + let c; + while (t.length < len) { + c = i2ops(i++); + t = Buffer.concat([t, createHash("sha1").update(seed).update(c).digest()]); + } + return t.slice(0, len); +} + +function i2ops(c) { + const out = Buffer.allocUnsafe(4); + out.writeUInt32BE(c, 0); + return out; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/mod.js b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/mod.js new file mode 100644 index 0000000000..a91197aef5 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/mod.js @@ -0,0 +1,15 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Calvin Metcalf. All rights reserved. MIT license. + +import { publicEncrypt } from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/public_encrypt.js"; +import { privateDecrypt } from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/private_decrypt.js"; + +export { privateDecrypt, publicEncrypt }; + +export function privateEncrypt(key, buf) { + return publicEncrypt(key, buf, true); +} + +export function publicDecrypt(key, buf) { + return privateDecrypt(key, buf, true); +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/private_decrypt.js b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/private_decrypt.js new file mode 100644 index 0000000000..9b485b3db4 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/private_decrypt.js @@ -0,0 +1,111 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Calvin Metcalf. All rights reserved. MIT license. + +import parseKeys from "internal:deno_node/polyfills/_crypto/crypto_browserify/parse_asn1/mod.js"; +import { createHash } from "internal:deno_node/polyfills/internal/crypto/hash.ts"; +import mgf from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/mgf.js"; +import { xor } from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/xor.js"; +import { BN } from "internal:deno_node/polyfills/_crypto/crypto_browserify/bn.js/bn.js"; +import { withPublic } from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/with_public.js"; +import crt from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_rsa.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export function privateDecrypt(privateKey, enc, reverse) { + let padding; + if (privateKey.padding) { + padding = privateKey.padding; + } else if (reverse) { + padding = 1; + } else { + padding = 4; + } + + const key = parseKeys(privateKey); + const k = key.modulus.byteLength(); + if (enc.length > k || new BN(enc).cmp(key.modulus) >= 0) { + throw new Error("decryption error"); + } + let msg; + if (reverse) { + msg = withPublic(new BN(enc), key); + } else { + msg = crt(enc, key); + } + const zBuffer = Buffer.alloc(k - msg.length); + msg = Buffer.concat([zBuffer, msg], k); + if (padding === 4) { + return oaep(key, msg); + } else if (padding === 1) { + return pkcs1(key, msg, reverse); + } else if (padding === 3) { + return msg; + } else { + throw new Error("unknown padding"); + } +} + +function oaep(key, msg) { + const k = key.modulus.byteLength(); + const iHash = createHash("sha1").update(Buffer.alloc(0)).digest(); + const hLen = iHash.length; + if (msg[0] !== 0) { + throw new Error("decryption error"); + } + const maskedSeed = msg.slice(1, hLen + 1); + const maskedDb = msg.slice(hLen + 1); + const seed = xor(maskedSeed, mgf(maskedDb, hLen)); + const db = xor(maskedDb, mgf(seed, k - hLen - 1)); + if (compare(iHash, db.slice(0, hLen))) { + throw new Error("decryption error"); + } + let i = hLen; + while (db[i] === 0) { + i++; + } + if (db[i++] !== 1) { + throw new Error("decryption error"); + } + return db.slice(i); +} + +function pkcs1(_key, msg, reverse) { + const p1 = msg.slice(0, 2); + let i = 2; + let status = 0; + while (msg[i++] !== 0) { + if (i >= msg.length) { + status++; + break; + } + } + const ps = msg.slice(2, i - 1); + + if ( + (p1.toString("hex") !== "0002" && !reverse) || + (p1.toString("hex") !== "0001" && reverse) + ) { + status++; + } + if (ps.length < 8) { + status++; + } + if (status) { + throw new Error("decryption error"); + } + return msg.slice(i); +} +function compare(a, b) { + a = Buffer.from(a); + b = Buffer.from(b); + let dif = 0; + let len = a.length; + if (a.length !== b.length) { + dif++; + len = Math.min(a.length, b.length); + } + let i = -1; + while (++i < len) { + dif += a[i] ^ b[i]; + } + return dif; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/public_encrypt.js b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/public_encrypt.js new file mode 100644 index 0000000000..9642128ba1 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/public_encrypt.js @@ -0,0 +1,104 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Calvin Metcalf. All rights reserved. MIT license. + +import parseKeys from "internal:deno_node/polyfills/_crypto/crypto_browserify/parse_asn1/mod.js"; +import { randomBytes } from "internal:deno_node/polyfills/_crypto/crypto_browserify/randombytes.ts"; +import { createHash } from "internal:deno_node/polyfills/internal/crypto/hash.ts"; +import mgf from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/mgf.js"; +import { xor } from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/xor.js"; +import { BN } from "internal:deno_node/polyfills/_crypto/crypto_browserify/bn.js/bn.js"; +import { withPublic } from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/with_public.js"; +import crt from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_rsa.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export function publicEncrypt(publicKey, msg, reverse) { + let padding; + if (publicKey.padding) { + padding = publicKey.padding; + } else if (reverse) { + padding = 1; + } else { + padding = 4; + } + const key = parseKeys(publicKey); + let paddedMsg; + if (padding === 4) { + paddedMsg = oaep(key, msg); + } else if (padding === 1) { + paddedMsg = pkcs1(key, msg, reverse); + } else if (padding === 3) { + paddedMsg = new BN(msg); + if (paddedMsg.cmp(key.modulus) >= 0) { + throw new Error("data too long for modulus"); + } + } else { + throw new Error("unknown padding"); + } + if (reverse) { + return crt(paddedMsg, key); + } else { + return withPublic(paddedMsg, key); + } +} + +function oaep(key, msg) { + const k = key.modulus.byteLength(); + const mLen = msg.length; + const iHash = createHash("sha1").update(Buffer.alloc(0)).digest(); + const hLen = iHash.length; + const hLen2 = 2 * hLen; + if (mLen > k - hLen2 - 2) { + throw new Error("message too long"); + } + const ps = Buffer.alloc(k - mLen - hLen2 - 2); + const dblen = k - hLen - 1; + const seed = randomBytes(hLen); + const maskedDb = xor( + Buffer.concat([iHash, ps, Buffer.alloc(1, 1), msg], dblen), + mgf(seed, dblen), + ); + const maskedSeed = xor(seed, mgf(maskedDb, hLen)); + return new BN(Buffer.concat([Buffer.alloc(1), maskedSeed, maskedDb], k)); +} +function pkcs1(key, msg, reverse) { + const mLen = msg.length; + const k = key.modulus.byteLength(); + if (mLen > k - 11) { + throw new Error("message too long"); + } + let ps; + if (reverse) { + ps = Buffer.alloc(k - mLen - 3, 0xff); + } else { + ps = nonZero(k - mLen - 3); + } + return new BN( + Buffer.concat([ + Buffer.from([ + 0, + reverse ? 1 : 2, + ]), + ps, + Buffer.alloc(1), + msg, + ], k), + ); +} +function nonZero(len) { + const out = Buffer.allocUnsafe(len); + let i = 0; + let cache = randomBytes(len * 2); + let cur = 0; + let num; + while (i < len) { + if (cur === cache.length) { + cache = randomBytes(len * 2); + cur = 0; + } + num = cache[cur++]; + if (num) { + out[i++] = num; + } + } + return out; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/with_public.js b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/with_public.js new file mode 100644 index 0000000000..5e94c7bc90 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/with_public.js @@ -0,0 +1,15 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Calvin Metcalf. All rights reserved. MIT license. + +import { BN } from "internal:deno_node/polyfills/_crypto/crypto_browserify/bn.js/bn.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export function withPublic(paddedMsg, key) { + return Buffer.from( + paddedMsg + .toRed(BN.mont(key.modulus)) + .redPow(new BN(key.publicExponent)) + .fromRed() + .toArray(), + ); +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/xor.js b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/xor.js new file mode 100644 index 0000000000..25c4b8a40c --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/public_encrypt/xor.js @@ -0,0 +1,11 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 Calvin Metcalf. All rights reserved. MIT license. + +export function xor(a, b) { + const len = a.length; + let i = -1; + while (++i < len) { + a[i] ^= b[i]; + } + return a; +} diff --git a/ext/node/polyfills/_crypto/crypto_browserify/randombytes.ts b/ext/node/polyfills/_crypto/crypto_browserify/randombytes.ts new file mode 100644 index 0000000000..d70ada2828 --- /dev/null +++ b/ext/node/polyfills/_crypto/crypto_browserify/randombytes.ts @@ -0,0 +1,47 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright 2017 crypto-browserify. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; + +// limit of Crypto.getRandomValues() +// https://developer.mozilla.org/en-US/docs/Web/API/Crypto/getRandomValues +const MAX_BYTES = 65536; + +// Node supports requesting up to this number of bytes +// https://github.com/nodejs/node/blob/master/lib/internal/crypto/random.js#L48 +const MAX_UINT32 = 4294967295; + +export function randomBytes( + size: number, + cb?: (err: Error | null, b: Buffer) => void, +) { + // phantomjs needs to throw + if (size > MAX_UINT32) { + throw new RangeError("requested too many random bytes"); + } + + const bytes = Buffer.allocUnsafe(size); + + if (size > 0) { // getRandomValues fails on IE if size == 0 + if (size > MAX_BYTES) { // this is the max bytes crypto.getRandomValues + // can do at once see https://developer.mozilla.org/en-US/docs/Web/API/window.crypto.getRandomValues + for (let generated = 0; generated < size; generated += MAX_BYTES) { + // buffer.slice automatically checks if the end is past the end of + // the buffer so we don't have to here + globalThis.crypto.getRandomValues( + bytes.slice(generated, generated + MAX_BYTES), + ); + } + } else { + globalThis.crypto.getRandomValues(bytes); + } + } + + if (typeof cb === "function") { + return nextTick(function () { + cb(null, bytes); + }); + } + + return bytes; +} diff --git a/ext/node/polyfills/_events.d.ts b/ext/node/polyfills/_events.d.ts new file mode 100644 index 0000000000..c4d89bfa48 --- /dev/null +++ b/ext/node/polyfills/_events.d.ts @@ -0,0 +1,826 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file no-explicit-any + +// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/9b9cd671114a2a5178809798d8e7f4d8ca6c2671/types/node/events.d.ts + +export const captureRejectionSymbol: unique symbol; +export const defaultMaxListeners: number; +export const errorMonitor: unique symbol; + +export interface Abortable { + /** + * When provided the corresponding `AbortController` can be used to cancel an asynchronous action. + */ + signal?: AbortSignal | undefined; +} +/** + * Returns a copy of the array of listeners for the event named `eventName`. + * + * For `EventEmitter`s this behaves exactly the same as calling `.listeners` on + * the emitter. + * + * For `EventTarget`s this is the only way to get the event listeners for the + * event target. This is useful for debugging and diagnostic purposes. + * + * ```js + * const { getEventListeners, EventEmitter } = require('events'); + * + * { + * const ee = new EventEmitter(); + * const listener = () => console.log('Events are fun'); + * ee.on('foo', listener); + * getEventListeners(ee, 'foo'); // [listener] + * } + * { + * const et = new EventTarget(); + * const listener = () => console.log('Events are fun'); + * et.addEventListener('foo', listener); + * getEventListeners(et, 'foo'); // [listener] + * } + * ``` + * @since v15.2.0 + */ +export function getEventListeners( + emitter: EventTarget | EventEmitter, + name: string | symbol, + // deno-lint-ignore ban-types +): Function[]; + +/** + * ```js + * const { on, EventEmitter } = require('events'); + * + * (async () => { + * const ee = new EventEmitter(); + * + * // Emit later on + * process.nextTick(() => { + * ee.emit('foo', 'bar'); + * ee.emit('foo', 42); + * }); + * + * for await (const event of on(ee, 'foo')) { + * // The execution of this inner block is synchronous and it + * // processes one event at a time (even with await). Do not use + * // if concurrent execution is required. + * console.log(event); // prints ['bar'] [42] + * } + * // Unreachable here + * })(); + * ``` + * + * Returns an `AsyncIterator` that iterates `eventName` events. It will throw + * if the `EventEmitter` emits `'error'`. It removes all listeners when + * exiting the loop. The `value` returned by each iteration is an array + * composed of the emitted event arguments. + * + * An `AbortSignal` can be used to cancel waiting on events: + * + * ```js + * const { on, EventEmitter } = require('events'); + * const ac = new AbortController(); + * + * (async () => { + * const ee = new EventEmitter(); + * + * // Emit later on + * process.nextTick(() => { + * ee.emit('foo', 'bar'); + * ee.emit('foo', 42); + * }); + * + * for await (const event of on(ee, 'foo', { signal: ac.signal })) { + * // The execution of this inner block is synchronous and it + * // processes one event at a time (even with await). Do not use + * // if concurrent execution is required. + * console.log(event); // prints ['bar'] [42] + * } + * // Unreachable here + * })(); + * + * process.nextTick(() => ac.abort()); + * ``` + * @since v13.6.0, v12.16.0 + * @param eventName The name of the event being listened for + * @return that iterates `eventName` events emitted by the `emitter` + */ +export function on( + emitter: EventEmitter, + eventName: string, + options?: StaticEventEmitterOptions, +): AsyncIterableIterator; + +/** + * Creates a `Promise` that is fulfilled when the `EventEmitter` emits the given + * event or that is rejected if the `EventEmitter` emits `'error'` while waiting. + * The `Promise` will resolve with an array of all the arguments emitted to the + * given event. + * + * This method is intentionally generic and works with the web platform [EventTarget](https://dom.spec.whatwg.org/#interface-eventtarget) interface, which has no special`'error'` event + * semantics and does not listen to the `'error'` event. + * + * ```js + * const { once, EventEmitter } = require('events'); + * + * async function run() { + * const ee = new EventEmitter(); + * + * process.nextTick(() => { + * ee.emit('myevent', 42); + * }); + * + * const [value] = await once(ee, 'myevent'); + * console.log(value); + * + * const err = new Error('kaboom'); + * process.nextTick(() => { + * ee.emit('error', err); + * }); + * + * try { + * await once(ee, 'myevent'); + * } catch (err) { + * console.log('error happened', err); + * } + * } + * + * run(); + * ``` + * + * The special handling of the `'error'` event is only used when `events.once()`is used to wait for another event. If `events.once()` is used to wait for the + * '`error'` event itself, then it is treated as any other kind of event without + * special handling: + * + * ```js + * const { EventEmitter, once } = require('events'); + * + * const ee = new EventEmitter(); + * + * once(ee, 'error') + * .then(([err]) => console.log('ok', err.message)) + * .catch((err) => console.log('error', err.message)); + * + * ee.emit('error', new Error('boom')); + * + * // Prints: ok boom + * ``` + * + * An `AbortSignal` can be used to cancel waiting for the event: + * + * ```js + * const { EventEmitter, once } = require('events'); + * + * const ee = new EventEmitter(); + * const ac = new AbortController(); + * + * async function foo(emitter, event, signal) { + * try { + * await once(emitter, event, { signal }); + * console.log('event emitted!'); + * } catch (error) { + * if (error.name === 'AbortError') { + * console.error('Waiting for the event was canceled!'); + * } else { + * console.error('There was an error', error.message); + * } + * } + * } + * + * foo(ee, 'foo', ac.signal); + * ac.abort(); // Abort waiting for the event + * ee.emit('foo'); // Prints: Waiting for the event was canceled! + * ``` + * @since v11.13.0, v10.16.0 + */ +export function once( + emitter: NodeEventTarget, + eventName: string | symbol, + options?: StaticEventEmitterOptions, +): Promise; +export function once( + emitter: EventTarget, + eventName: string, + options?: StaticEventEmitterOptions, +): Promise; + +/** + * `n` {number} A non-negative number. The maximum number of listeners per `EventTarget` event. + * `...eventsTargets` {EventTarget\[]|EventEmitter\[]} Zero or more {EventTarget} + * or {EventEmitter} instances. If none are specified, `n` is set as the default + * max for all newly created {EventTarget} and {EventEmitter} objects. + */ +export function setMaxListeners(n: number): EventEmitter; + +/** + * A class method that returns the number of listeners for the given `eventName`registered on the given `emitter`. + * + * ```js + * const { EventEmitter, listenerCount } = require('events'); + * const myEmitter = new EventEmitter(); + * myEmitter.on('event', () => {}); + * myEmitter.on('event', () => {}); + * console.log(listenerCount(myEmitter, 'event')); + * // Prints: 2 + * ``` + * @since v0.9.12 + * @deprecated Since v3.2.0 - Use `listenerCount` instead. + * @param emitter The emitter to query + * @param eventName The event name + */ +export function listenerCount( + emitter: EventEmitter, + eventName: string | symbol, +): number; + +interface EventEmitterOptions { + /** + * Enables automatic capturing of promise rejection. + */ + captureRejections?: boolean | undefined; +} +interface NodeEventTarget { + once(eventName: string | symbol, listener: (...args: any[]) => void): this; +} +interface EventTarget { + addEventListener( + eventName: string, + listener: (...args: any[]) => void, + opts?: { + once: boolean; + }, + ): any; +} +interface StaticEventEmitterOptions { + signal?: AbortSignal | undefined; +} +/** + * The `EventEmitter` class is defined and exposed by the `events` module: + * + * ```js + * const EventEmitter = require('events'); + * ``` + * + * All `EventEmitter`s emit the event `'newListener'` when new listeners are + * added and `'removeListener'` when existing listeners are removed. + * + * It supports the following option: + * @since v0.1.26 + */ +export class EventEmitter { + /** + * Alias for `emitter.on(eventName, listener)`. + * @since v0.1.26 + */ + addListener( + eventName: string | symbol, + listener: (...args: any[]) => void, + ): this; + /** + * Adds the `listener` function to the end of the listeners array for the + * event named `eventName`. No checks are made to see if the `listener` has + * already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple + * times. + * + * ```js + * server.on('connection', (stream) => { + * console.log('someone connected!'); + * }); + * ``` + * + * Returns a reference to the `EventEmitter`, so that calls can be chained. + * + * By default, event listeners are invoked in the order they are added. The`emitter.prependListener()` method can be used as an alternative to add the + * event listener to the beginning of the listeners array. + * + * ```js + * const myEE = new EventEmitter(); + * myEE.on('foo', () => console.log('a')); + * myEE.prependListener('foo', () => console.log('b')); + * myEE.emit('foo'); + * // Prints: + * // b + * // a + * ``` + * @since v0.1.101 + * @param eventName The name of the event. + * @param listener The callback function + */ + on(eventName: string | symbol, listener: (...args: any[]) => void): this; + /** + * Adds a **one-time**`listener` function for the event named `eventName`. The + * next time `eventName` is triggered, this listener is removed and then invoked. + * + * ```js + * server.once('connection', (stream) => { + * console.log('Ah, we have our first user!'); + * }); + * ``` + * + * Returns a reference to the `EventEmitter`, so that calls can be chained. + * + * By default, event listeners are invoked in the order they are added. The`emitter.prependOnceListener()` method can be used as an alternative to add the + * event listener to the beginning of the listeners array. + * + * ```js + * const myEE = new EventEmitter(); + * myEE.once('foo', () => console.log('a')); + * myEE.prependOnceListener('foo', () => console.log('b')); + * myEE.emit('foo'); + * // Prints: + * // b + * // a + * ``` + * @since v0.3.0 + * @param eventName The name of the event. + * @param listener The callback function + */ + once(eventName: string | symbol, listener: (...args: any[]) => void): this; + /** + * Removes the specified `listener` from the listener array for the event named`eventName`. + * + * ```js + * const callback = (stream) => { + * console.log('someone connected!'); + * }; + * server.on('connection', callback); + * // ... + * server.removeListener('connection', callback); + * ``` + * + * `removeListener()` will remove, at most, one instance of a listener from the + * listener array. If any single listener has been added multiple times to the + * listener array for the specified `eventName`, then `removeListener()` must be + * called multiple times to remove each instance. + * + * Once an event is emitted, all listeners attached to it at the + * time of emitting are called in order. This implies that any`removeListener()` or `removeAllListeners()` calls _after_ emitting and_before_ the last listener finishes execution will + * not remove them from`emit()` in progress. Subsequent events behave as expected. + * + * ```js + * const myEmitter = new MyEmitter(); + * + * const callbackA = () => { + * console.log('A'); + * myEmitter.removeListener('event', callbackB); + * }; + * + * const callbackB = () => { + * console.log('B'); + * }; + * + * myEmitter.on('event', callbackA); + * + * myEmitter.on('event', callbackB); + * + * // callbackA removes listener callbackB but it will still be called. + * // Internal listener array at time of emit [callbackA, callbackB] + * myEmitter.emit('event'); + * // Prints: + * // A + * // B + * + * // callbackB is now removed. + * // Internal listener array [callbackA] + * myEmitter.emit('event'); + * // Prints: + * // A + * ``` + * + * Because listeners are managed using an internal array, calling this will + * change the position indices of any listener registered _after_ the listener + * being removed. This will not impact the order in which listeners are called, + * but it means that any copies of the listener array as returned by + * the `emitter.listeners()` method will need to be recreated. + * + * When a single function has been added as a handler multiple times for a single + * event (as in the example below), `removeListener()` will remove the most + * recently added instance. In the example the `once('ping')`listener is removed: + * + * ```js + * const ee = new EventEmitter(); + * + * function pong() { + * console.log('pong'); + * } + * + * ee.on('ping', pong); + * ee.once('ping', pong); + * ee.removeListener('ping', pong); + * + * ee.emit('ping'); + * ee.emit('ping'); + * ``` + * + * Returns a reference to the `EventEmitter`, so that calls can be chained. + * @since v0.1.26 + */ + removeListener( + eventName: string | symbol, + listener: (...args: any[]) => void, + ): this; + /** + * Alias for `emitter.removeListener()`. + * @since v10.0.0 + */ + off(eventName: string | symbol, listener: (...args: any[]) => void): this; + /** + * Removes all listeners, or those of the specified `eventName`. + * + * It is bad practice to remove listeners added elsewhere in the code, + * particularly when the `EventEmitter` instance was created by some other + * component or module (e.g. sockets or file streams). + * + * Returns a reference to the `EventEmitter`, so that calls can be chained. + * @since v0.1.26 + */ + removeAllListeners(event?: string | symbol): this; + /** + * By default `EventEmitter`s will print a warning if more than `10` listeners are + * added for a particular event. This is a useful default that helps finding + * memory leaks. The `emitter.setMaxListeners()` method allows the limit to be + * modified for this specific `EventEmitter` instance. The value can be set to`Infinity` (or `0`) to indicate an unlimited number of listeners. + * + * Returns a reference to the `EventEmitter`, so that calls can be chained. + * @since v0.3.5 + */ + setMaxListeners(n: number): this; + /** + * Returns the current max listener value for the `EventEmitter` which is either + * set by `emitter.setMaxListeners(n)` or defaults to {@link defaultMaxListeners}. + * @since v1.0.0 + */ + getMaxListeners(): number; + /** + * Returns a copy of the array of listeners for the event named `eventName`. + * + * ```js + * server.on('connection', (stream) => { + * console.log('someone connected!'); + * }); + * console.log(util.inspect(server.listeners('connection'))); + * // Prints: [ [Function] ] + * ``` + * @since v0.1.26 + */ + // deno-lint-ignore ban-types + listeners(eventName: string | symbol): Function[]; + /** + * Returns a copy of the array of listeners for the event named `eventName`, + * including any wrappers (such as those created by `.once()`). + * + * ```js + * const emitter = new EventEmitter(); + * emitter.once('log', () => console.log('log once')); + * + * // Returns a new Array with a function `onceWrapper` which has a property + * // `listener` which contains the original listener bound above + * const listeners = emitter.rawListeners('log'); + * const logFnWrapper = listeners[0]; + * + * // Logs "log once" to the console and does not unbind the `once` event + * logFnWrapper.listener(); + * + * // Logs "log once" to the console and removes the listener + * logFnWrapper(); + * + * emitter.on('log', () => console.log('log persistently')); + * // Will return a new Array with a single function bound by `.on()` above + * const newListeners = emitter.rawListeners('log'); + * + * // Logs "log persistently" twice + * newListeners[0](); + * emitter.emit('log'); + * ``` + * @since v9.4.0 + */ + // deno-lint-ignore ban-types + rawListeners(eventName: string | symbol): Function[]; + /** + * Synchronously calls each of the listeners registered for the event named`eventName`, in the order they were registered, passing the supplied arguments + * to each. + * + * Returns `true` if the event had listeners, `false` otherwise. + * + * ```js + * const EventEmitter = require('events'); + * const myEmitter = new EventEmitter(); + * + * // First listener + * myEmitter.on('event', function firstListener() { + * console.log('Helloooo! first listener'); + * }); + * // Second listener + * myEmitter.on('event', function secondListener(arg1, arg2) { + * console.log(`event with parameters ${arg1}, ${arg2} in second listener`); + * }); + * // Third listener + * myEmitter.on('event', function thirdListener(...args) { + * const parameters = args.join(', '); + * console.log(`event with parameters ${parameters} in third listener`); + * }); + * + * console.log(myEmitter.listeners('event')); + * + * myEmitter.emit('event', 1, 2, 3, 4, 5); + * + * // Prints: + * // [ + * // [Function: firstListener], + * // [Function: secondListener], + * // [Function: thirdListener] + * // ] + * // Helloooo! first listener + * // event with parameters 1, 2 in second listener + * // event with parameters 1, 2, 3, 4, 5 in third listener + * ``` + * @since v0.1.26 + */ + emit(eventName: string | symbol, ...args: any[]): boolean; + /** + * Returns the number of listeners listening to the event named `eventName`. + * @since v3.2.0 + * @param eventName The name of the event being listened for + */ + listenerCount(eventName: string | symbol): number; + /** + * Adds the `listener` function to the _beginning_ of the listeners array for the + * event named `eventName`. No checks are made to see if the `listener` has + * already been added. Multiple calls passing the same combination of `eventName`and `listener` will result in the `listener` being added, and called, multiple + * times. + * + * ```js + * server.prependListener('connection', (stream) => { + * console.log('someone connected!'); + * }); + * ``` + * + * Returns a reference to the `EventEmitter`, so that calls can be chained. + * @since v6.0.0 + * @param eventName The name of the event. + * @param listener The callback function + */ + prependListener( + eventName: string | symbol, + listener: (...args: any[]) => void, + ): this; + /** + * Adds a **one-time**`listener` function for the event named `eventName` to the_beginning_ of the listeners array. The next time `eventName` is triggered, this + * listener is removed, and then invoked. + * + * ```js + * server.prependOnceListener('connection', (stream) => { + * console.log('Ah, we have our first user!'); + * }); + * ``` + * + * Returns a reference to the `EventEmitter`, so that calls can be chained. + * @since v6.0.0 + * @param eventName The name of the event. + * @param listener The callback function + */ + prependOnceListener( + eventName: string | symbol, + listener: (...args: any[]) => void, + ): this; + /** + * Returns an array listing the events for which the emitter has registered + * listeners. The values in the array are strings or `Symbol`s. + * + * ```js + * const EventEmitter = require('events'); + * const myEE = new EventEmitter(); + * myEE.on('foo', () => {}); + * myEE.on('bar', () => {}); + * + * const sym = Symbol('symbol'); + * myEE.on(sym, () => {}); + * + * console.log(myEE.eventNames()); + * // Prints: [ 'foo', 'bar', Symbol(symbol) ] + * ``` + * @since v6.0.0 + */ + eventNames(): Array; + + constructor(options?: EventEmitterOptions); + /** + * Creates a `Promise` that is fulfilled when the `EventEmitter` emits the given + * event or that is rejected if the `EventEmitter` emits `'error'` while waiting. + * The `Promise` will resolve with an array of all the arguments emitted to the + * given event. + * + * This method is intentionally generic and works with the web platform [EventTarget](https://dom.spec.whatwg.org/#interface-eventtarget) interface, which has no special`'error'` event + * semantics and does not listen to the `'error'` event. + * + * ```js + * const { once, EventEmitter } = require('events'); + * + * async function run() { + * const ee = new EventEmitter(); + * + * process.nextTick(() => { + * ee.emit('myevent', 42); + * }); + * + * const [value] = await once(ee, 'myevent'); + * console.log(value); + * + * const err = new Error('kaboom'); + * process.nextTick(() => { + * ee.emit('error', err); + * }); + * + * try { + * await once(ee, 'myevent'); + * } catch (err) { + * console.log('error happened', err); + * } + * } + * + * run(); + * ``` + * + * The special handling of the `'error'` event is only used when `events.once()`is used to wait for another event. If `events.once()` is used to wait for the + * '`error'` event itself, then it is treated as any other kind of event without + * special handling: + * + * ```js + * const { EventEmitter, once } = require('events'); + * + * const ee = new EventEmitter(); + * + * once(ee, 'error') + * .then(([err]) => console.log('ok', err.message)) + * .catch((err) => console.log('error', err.message)); + * + * ee.emit('error', new Error('boom')); + * + * // Prints: ok boom + * ``` + * + * An `AbortSignal` can be used to cancel waiting for the event: + * + * ```js + * const { EventEmitter, once } = require('events'); + * + * const ee = new EventEmitter(); + * const ac = new AbortController(); + * + * async function foo(emitter, event, signal) { + * try { + * await once(emitter, event, { signal }); + * console.log('event emitted!'); + * } catch (error) { + * if (error.name === 'AbortError') { + * console.error('Waiting for the event was canceled!'); + * } else { + * console.error('There was an error', error.message); + * } + * } + * } + * + * foo(ee, 'foo', ac.signal); + * ac.abort(); // Abort waiting for the event + * ee.emit('foo'); // Prints: Waiting for the event was canceled! + * ``` + * @since v11.13.0, v10.16.0 + */ + static once( + emitter: NodeEventTarget, + eventName: string | symbol, + options?: StaticEventEmitterOptions, + ): Promise; + static once( + emitter: EventTarget, + eventName: string, + options?: StaticEventEmitterOptions, + ): Promise; + /** + * ```js + * const { on, EventEmitter } = require('events'); + * + * (async () => { + * const ee = new EventEmitter(); + * + * // Emit later on + * process.nextTick(() => { + * ee.emit('foo', 'bar'); + * ee.emit('foo', 42); + * }); + * + * for await (const event of on(ee, 'foo')) { + * // The execution of this inner block is synchronous and it + * // processes one event at a time (even with await). Do not use + * // if concurrent execution is required. + * console.log(event); // prints ['bar'] [42] + * } + * // Unreachable here + * })(); + * ``` + * + * Returns an `AsyncIterator` that iterates `eventName` events. It will throw + * if the `EventEmitter` emits `'error'`. It removes all listeners when + * exiting the loop. The `value` returned by each iteration is an array + * composed of the emitted event arguments. + * + * An `AbortSignal` can be used to cancel waiting on events: + * + * ```js + * const { on, EventEmitter } = require('events'); + * const ac = new AbortController(); + * + * (async () => { + * const ee = new EventEmitter(); + * + * // Emit later on + * process.nextTick(() => { + * ee.emit('foo', 'bar'); + * ee.emit('foo', 42); + * }); + * + * for await (const event of on(ee, 'foo', { signal: ac.signal })) { + * // The execution of this inner block is synchronous and it + * // processes one event at a time (even with await). Do not use + * // if concurrent execution is required. + * console.log(event); // prints ['bar'] [42] + * } + * // Unreachable here + * })(); + * + * process.nextTick(() => ac.abort()); + * ``` + * @since v13.6.0, v12.16.0 + * @param eventName The name of the event being listened for + * @return that iterates `eventName` events emitted by the `emitter` + */ + static on: typeof on; + /** + * A class method that returns the number of listeners for the given `eventName`registered on the given `emitter`. + * + * ```js + * const { EventEmitter, listenerCount } = require('events'); + * const myEmitter = new EventEmitter(); + * myEmitter.on('event', () => {}); + * myEmitter.on('event', () => {}); + * console.log(listenerCount(myEmitter, 'event')); + * // Prints: 2 + * ``` + * @since v0.9.12 + * @deprecated Since v3.2.0 - Use `listenerCount` instead. + * @param emitter The emitter to query + * @param eventName The event name + */ + static listenerCount( + emitter: EventEmitter, + eventName: string | symbol, + ): number; + + /** + * Returns a copy of the array of listeners for the event named `eventName`. + * + * For `EventEmitter`s this behaves exactly the same as calling `.listeners` on + * the emitter. + * + * For `EventTarget`s this is the only way to get the event listeners for the + * event target. This is useful for debugging and diagnostic purposes. + * + * ```js + * const { getEventListeners, EventEmitter } = require('events'); + * + * { + * const ee = new EventEmitter(); + * const listener = () => console.log('Events are fun'); + * ee.on('foo', listener); + * getEventListeners(ee, 'foo'); // [listener] + * } + * { + * const et = new EventTarget(); + * const listener = () => console.log('Events are fun'); + * et.addEventListener('foo', listener); + * getEventListeners(et, 'foo'); // [listener] + * } + * ``` + * @since v15.2.0 + */ + static getEventListeners: typeof getEventListeners; + + /** + * This symbol shall be used to install a listener for only monitoring `'error'` + * events. Listeners installed using this symbol are called before the regular + * `'error'` listeners are called. + * + * Installing a listener using this symbol does not change the behavior once an + * `'error'` event is emitted, therefore the process will still crash if no + * regular `'error'` listener is installed. + */ + static readonly errorMonitor: unique symbol; + static readonly captureRejectionSymbol: unique symbol; + /** + * Sets or gets the default captureRejection value for all emitters. + */ + // TODO(@bartlomieju): These should be described using static getter/setter pairs: + static captureRejections: boolean; + static defaultMaxListeners: number; +} + +export default EventEmitter; diff --git a/ext/node/polyfills/_events.mjs b/ext/node/polyfills/_events.mjs new file mode 100644 index 0000000000..cd9871a6ca --- /dev/null +++ b/ext/node/polyfills/_events.mjs @@ -0,0 +1,1033 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +"use strict"; + +const kRejection = Symbol.for("nodejs.rejection"); + +import { inspect } from "internal:deno_node/polyfills/internal/util/inspect.mjs"; +import { + AbortError, + // kEnhanceStackBeforeInspector, + ERR_INVALID_ARG_TYPE, + ERR_OUT_OF_RANGE, + ERR_UNHANDLED_ERROR, +} from "internal:deno_node/polyfills/internal/errors.ts"; + +import { + validateAbortSignal, + validateBoolean, + validateFunction, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { spliceOne } from "internal:deno_node/polyfills/_utils.ts"; + +const kCapture = Symbol("kCapture"); +const kErrorMonitor = Symbol("events.errorMonitor"); +const kMaxEventTargetListeners = Symbol("events.maxEventTargetListeners"); +const kMaxEventTargetListenersWarned = Symbol( + "events.maxEventTargetListenersWarned", +); + +/** + * Creates a new `EventEmitter` instance. + * @param {{ captureRejections?: boolean; }} [opts] + * @returns {EventEmitter} + */ +export function EventEmitter(opts) { + EventEmitter.init.call(this, opts); +} +export default EventEmitter; +EventEmitter.on = on; +EventEmitter.once = once; +EventEmitter.getEventListeners = getEventListeners; +EventEmitter.setMaxListeners = setMaxListeners; +EventEmitter.listenerCount = listenerCount; +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; +EventEmitter.usingDomains = false; + +EventEmitter.captureRejectionSymbol = kRejection; +export const captureRejectionSymbol = EventEmitter.captureRejectionSymbol; +export const errorMonitor = EventEmitter.errorMonitor; + +Object.defineProperty(EventEmitter, "captureRejections", { + get() { + return EventEmitter.prototype[kCapture]; + }, + set(value) { + validateBoolean(value, "EventEmitter.captureRejections"); + + EventEmitter.prototype[kCapture] = value; + }, + enumerable: true, +}); + +EventEmitter.errorMonitor = kErrorMonitor; + +// The default for captureRejections is false +Object.defineProperty(EventEmitter.prototype, kCapture, { + value: false, + writable: true, + enumerable: false, +}); + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._eventsCount = 0; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +export let defaultMaxListeners = 10; + +function checkListener(listener) { + validateFunction(listener, "listener"); +} + +Object.defineProperty(EventEmitter, "defaultMaxListeners", { + enumerable: true, + get: function () { + return defaultMaxListeners; + }, + set: function (arg) { + if (typeof arg !== "number" || arg < 0 || Number.isNaN(arg)) { + throw new ERR_OUT_OF_RANGE( + "defaultMaxListeners", + "a non-negative number", + arg, + ); + } + defaultMaxListeners = arg; + }, +}); + +Object.defineProperties(EventEmitter, { + kMaxEventTargetListeners: { + value: kMaxEventTargetListeners, + enumerable: false, + configurable: false, + writable: false, + }, + kMaxEventTargetListenersWarned: { + value: kMaxEventTargetListenersWarned, + enumerable: false, + configurable: false, + writable: false, + }, +}); + +/** + * Sets the max listeners. + * @param {number} n + * @param {EventTarget[] | EventEmitter[]} [eventTargets] + * @returns {void} + */ +export function setMaxListeners( + n = defaultMaxListeners, + ...eventTargets +) { + if (typeof n !== "number" || n < 0 || Number.isNaN(n)) { + throw new ERR_OUT_OF_RANGE("n", "a non-negative number", n); + } + if (eventTargets.length === 0) { + defaultMaxListeners = n; + } else { + for (let i = 0; i < eventTargets.length; i++) { + const target = eventTargets[i]; + if (target instanceof EventTarget) { + target[kMaxEventTargetListeners] = n; + target[kMaxEventTargetListenersWarned] = false; + } else if (typeof target.setMaxListeners === "function") { + target.setMaxListeners(n); + } else { + throw new ERR_INVALID_ARG_TYPE( + "eventTargets", + ["EventEmitter", "EventTarget"], + target, + ); + } + } + } +} + +EventEmitter.init = function (opts) { + if ( + this._events === undefined || + this._events === Object.getPrototypeOf(this)._events + ) { + this._events = Object.create(null); + this._eventsCount = 0; + } + + this._maxListeners = this._maxListeners || undefined; + + if (opts?.captureRejections) { + validateBoolean(opts.captureRejections, "options.captureRejections"); + this[kCapture] = Boolean(opts.captureRejections); + } else { + // Assigning the kCapture property directly saves an expensive + // prototype lookup in a very sensitive hot path. + this[kCapture] = EventEmitter.prototype[kCapture]; + } +}; + +function addCatch(that, promise, type, args) { + if (!that[kCapture]) { + return; + } + + // Handle Promises/A+ spec, then could be a getter + // that throws on second use. + try { + const then = promise.then; + + if (typeof then === "function") { + then.call(promise, undefined, function (err) { + // The callback is called with nextTick to avoid a follow-up + // rejection from this promise. + process.nextTick(emitUnhandledRejectionOrErr, that, err, type, args); + }); + } + } catch (err) { + that.emit("error", err); + } +} + +function emitUnhandledRejectionOrErr(ee, err, type, args) { + if (typeof ee[kRejection] === "function") { + ee[kRejection](err, type, ...args); + } else { + // We have to disable the capture rejections mechanism, otherwise + // we might end up in an infinite loop. + const prev = ee[kCapture]; + + // If the error handler throws, it is not catcheable and it + // will end up in 'uncaughtException'. We restore the previous + // value of kCapture in case the uncaughtException is present + // and the exception is handled. + try { + ee[kCapture] = false; + ee.emit("error", err); + } finally { + ee[kCapture] = prev; + } + } +} + +/** + * Increases the max listeners of the event emitter. + * @param {number} n + * @returns {EventEmitter} + */ +EventEmitter.prototype.setMaxListeners = function setMaxListeners(n) { + if (typeof n !== "number" || n < 0 || Number.isNaN(n)) { + throw new ERR_OUT_OF_RANGE("n", "a non-negative number", n); + } + this._maxListeners = n; + return this; +}; + +function _getMaxListeners(that) { + if (that._maxListeners === undefined) { + return EventEmitter.defaultMaxListeners; + } + return that._maxListeners; +} + +/** + * Returns the current max listener value for the event emitter. + * @returns {number} + */ +EventEmitter.prototype.getMaxListeners = function getMaxListeners() { + return _getMaxListeners(this); +}; + +// Returns the length and line number of the first sequence of `a` that fully +// appears in `b` with a length of at least 4. +function identicalSequenceRange(a, b) { + for (let i = 0; i < a.length - 3; i++) { + // Find the first entry of b that matches the current entry of a. + const pos = b.indexOf(a[i]); + if (pos !== -1) { + const rest = b.length - pos; + if (rest > 3) { + let len = 1; + const maxLen = Math.min(a.length - i, rest); + // Count the number of consecutive entries. + while (maxLen > len && a[i + len] === b[pos + len]) { + len++; + } + if (len > 3) { + return [len, i]; + } + } + } + } + + return [0, 0]; +} + +// deno-lint-ignore no-unused-vars +function enhanceStackTrace(err, own) { + let ctorInfo = ""; + try { + const { name } = this.constructor; + if (name !== "EventEmitter") { + ctorInfo = ` on ${name} instance`; + } + } catch { + // pass + } + const sep = `\nEmitted 'error' event${ctorInfo} at:\n`; + + const errStack = err.stack.split("\n").slice(1); + const ownStack = own.stack.split("\n").slice(1); + + const { 0: len, 1: off } = identicalSequenceRange(ownStack, errStack); + if (len > 0) { + ownStack.splice( + off + 1, + len - 2, + " [... lines matching original stack trace ...]", + ); + } + + return err.stack + sep + ownStack.join("\n"); +} + +/** + * Synchronously calls each of the listeners registered + * for the event. + * @param {string | symbol} type + * @param {...any} [args] + * @returns {boolean} + */ +EventEmitter.prototype.emit = function emit(type, ...args) { + let doError = type === "error"; + + const events = this._events; + if (events !== undefined) { + if (doError && events[kErrorMonitor] !== undefined) { + this.emit(kErrorMonitor, ...args); + } + doError = doError && events.error === undefined; + } else if (!doError) { + return false; + } + + // If there is no 'error' event listener then throw. + if (doError) { + let er; + if (args.length > 0) { + er = args[0]; + } + if (er instanceof Error) { + try { + const capture = {}; + Error.captureStackTrace(capture, EventEmitter.prototype.emit); + // Object.defineProperty(er, kEnhanceStackBeforeInspector, { + // value: enhanceStackTrace.bind(this, er, capture), + // configurable: true + // }); + } catch { + // pass + } + + // Note: The comments on the `throw` lines are intentional, they show + // up in Node's output if this results in an unhandled exception. + throw er; // Unhandled 'error' event + } + + let stringifiedEr; + try { + stringifiedEr = inspect(er); + } catch { + stringifiedEr = er; + } + + // At least give some kind of context to the user + const err = new ERR_UNHANDLED_ERROR(stringifiedEr); + err.context = er; + throw err; // Unhandled 'error' event + } + + const handler = events[type]; + + if (handler === undefined) { + return false; + } + + if (typeof handler === "function") { + const result = handler.apply(this, args); + + // We check if result is undefined first because that + // is the most common case so we do not pay any perf + // penalty + if (result !== undefined && result !== null) { + addCatch(this, result, type, args); + } + } else { + const len = handler.length; + const listeners = arrayClone(handler); + for (let i = 0; i < len; ++i) { + const result = listeners[i].apply(this, args); + + // We check if result is undefined first because that + // is the most common case so we do not pay any perf + // penalty. + // This code is duplicated because extracting it away + // would make it non-inlineable. + if (result !== undefined && result !== null) { + addCatch(this, result, type, args); + } + } + } + + return true; +}; + +function _addListener(target, type, listener, prepend) { + let m; + let events; + let existing; + + checkListener(listener); + + events = target._events; + if (events === undefined) { + events = target._events = Object.create(null); + target._eventsCount = 0; + } else { + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (events.newListener !== undefined) { + target.emit("newListener", type, listener.listener ?? listener); + + // Re-assign `events` because a newListener handler could have caused the + // this._events to be assigned to a new object + events = target._events; + } + existing = events[type]; + } + + if (existing === undefined) { + // Optimize the case of one listener. Don't need the extra array object. + events[type] = listener; + ++target._eventsCount; + } else { + if (typeof existing === "function") { + // Adding the second element, need to change to array. + existing = events[type] = prepend + ? [listener, existing] + : [existing, listener]; + // If we've already got an array, just append. + } else if (prepend) { + existing.unshift(listener); + } else { + existing.push(listener); + } + + // Check for listener leak + m = _getMaxListeners(target); + if (m > 0 && existing.length > m && !existing.warned) { + existing.warned = true; + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + const w = new Error( + "Possible EventEmitter memory leak detected. " + + `${existing.length} ${String(type)} listeners ` + + `added to ${inspect(target, { depth: -1 })}. Use ` + + "emitter.setMaxListeners() to increase limit", + ); + w.name = "MaxListenersExceededWarning"; + w.emitter = target; + w.type = type; + w.count = existing.length; + process.emitWarning(w); + } + } + + return target; +} + +/** + * Adds a listener to the event emitter. + * @param {string | symbol} type + * @param {Function} listener + * @returns {EventEmitter} + */ +EventEmitter.prototype.addListener = function addListener(type, listener) { + return _addListener(this, type, listener, false); +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +/** + * Adds the `listener` function to the beginning of + * the listeners array. + * @param {string | symbol} type + * @param {Function} listener + * @returns {EventEmitter} + */ +EventEmitter.prototype.prependListener = function prependListener( + type, + listener, +) { + return _addListener(this, type, listener, true); +}; + +function onceWrapper() { + if (!this.fired) { + this.target.removeListener(this.type, this.wrapFn); + this.fired = true; + if (arguments.length === 0) { + return this.listener.call(this.target); + } + return this.listener.apply(this.target, arguments); + } +} + +function _onceWrap(target, type, listener) { + const state = { fired: false, wrapFn: undefined, target, type, listener }; + const wrapped = onceWrapper.bind(state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; +} + +/** + * Adds a one-time `listener` function to the event emitter. + * @param {string | symbol} type + * @param {Function} listener + * @returns {EventEmitter} + */ +EventEmitter.prototype.once = function once(type, listener) { + checkListener(listener); + + this.on(type, _onceWrap(this, type, listener)); + return this; +}; + +/** + * Adds a one-time `listener` function to the beginning of + * the listeners array. + * @param {string | symbol} type + * @param {Function} listener + * @returns {EventEmitter} + */ +EventEmitter.prototype.prependOnceListener = function prependOnceListener( + type, + listener, +) { + checkListener(listener); + + this.prependListener(type, _onceWrap(this, type, listener)); + return this; +}; + +/** + * Removes the specified `listener` from the listeners array. + * @param {string | symbol} type + * @param {Function} listener + * @returns {EventEmitter} + */ +EventEmitter.prototype.removeListener = function removeListener( + type, + listener, +) { + checkListener(listener); + + const events = this._events; + if (events === undefined) { + return this; + } + + const list = events[type]; + if (list === undefined) { + return this; + } + + if (list === listener || list.listener === listener) { + if (--this._eventsCount === 0) { + this._events = Object.create(null); + } else { + delete events[type]; + if (events.removeListener) { + this.emit("removeListener", type, list.listener || listener); + } + } + } else if (typeof list !== "function") { + let position = -1; + + for (let i = list.length - 1; i >= 0; i--) { + if (list[i] === listener || list[i].listener === listener) { + position = i; + break; + } + } + + if (position < 0) { + return this; + } + + if (position === 0) { + list.shift(); + } else { + spliceOne(list, position); + } + + if (list.length === 1) { + events[type] = list[0]; + } + + if (events.removeListener !== undefined) { + this.emit("removeListener", type, listener); + } + } + + return this; +}; + +EventEmitter.prototype.off = EventEmitter.prototype.removeListener; + +/** + * Removes all listeners from the event emitter. (Only + * removes listeners for a specific event name if specified + * as `type`). + * @param {string | symbol} [type] + * @returns {EventEmitter} + */ +EventEmitter.prototype.removeAllListeners = function removeAllListeners(type) { + const events = this._events; + if (events === undefined) { + return this; + } + + // Not listening for removeListener, no need to emit + if (events.removeListener === undefined) { + if (arguments.length === 0) { + this._events = Object.create(null); + this._eventsCount = 0; + } else if (events[type] !== undefined) { + if (--this._eventsCount === 0) { + this._events = Object.create(null); + } else { + delete events[type]; + } + } + return this; + } + + // Emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (const key of Reflect.ownKeys(events)) { + if (key === "removeListener") continue; + this.removeAllListeners(key); + } + this.removeAllListeners("removeListener"); + this._events = Object.create(null); + this._eventsCount = 0; + return this; + } + + const listeners = events[type]; + + if (typeof listeners === "function") { + this.removeListener(type, listeners); + } else if (listeners !== undefined) { + // LIFO order + for (let i = listeners.length - 1; i >= 0; i--) { + this.removeListener(type, listeners[i]); + } + } + + return this; +}; + +function _listeners(target, type, unwrap) { + const events = target._events; + + if (events === undefined) { + return []; + } + + const evlistener = events[type]; + if (evlistener === undefined) { + return []; + } + + if (typeof evlistener === "function") { + return unwrap ? [evlistener.listener || evlistener] : [evlistener]; + } + + return unwrap ? unwrapListeners(evlistener) : arrayClone(evlistener); +} + +/** + * Returns a copy of the array of listeners for the event name + * specified as `type`. + * @param {string | symbol} type + * @returns {Function[]} + */ +EventEmitter.prototype.listeners = function listeners(type) { + return _listeners(this, type, true); +}; + +/** + * Returns a copy of the array of listeners and wrappers for + * the event name specified as `type`. + * @param {string | symbol} type + * @returns {Function[]} + */ +EventEmitter.prototype.rawListeners = function rawListeners(type) { + return _listeners(this, type, false); +}; + +/** + * Returns the number of listeners listening to event name + * specified as `type`. + * @param {string | symbol} type + * @returns {number} + */ +const _listenerCount = function listenerCount(type) { + const events = this._events; + + if (events !== undefined) { + const evlistener = events[type]; + + if (typeof evlistener === "function") { + return 1; + } else if (evlistener !== undefined) { + return evlistener.length; + } + } + + return 0; +}; + +EventEmitter.prototype.listenerCount = _listenerCount; + +/** + * Returns the number of listeners listening to the event name + * specified as `type`. + * @deprecated since v3.2.0 + * @param {EventEmitter} emitter + * @param {string | symbol} type + * @returns {number} + */ +export function listenerCount(emitter, type) { + if (typeof emitter.listenerCount === "function") { + return emitter.listenerCount(type); + } + return _listenerCount.call(emitter, type); +} + +/** + * Returns an array listing the events for which + * the emitter has registered listeners. + * @returns {any[]} + */ +EventEmitter.prototype.eventNames = function eventNames() { + return this._eventsCount > 0 ? Reflect.ownKeys(this._events) : []; +}; + +function arrayClone(arr) { + // At least since V8 8.3, this implementation is faster than the previous + // which always used a simple for-loop + switch (arr.length) { + case 2: + return [arr[0], arr[1]]; + case 3: + return [arr[0], arr[1], arr[2]]; + case 4: + return [arr[0], arr[1], arr[2], arr[3]]; + case 5: + return [arr[0], arr[1], arr[2], arr[3], arr[4]]; + case 6: + return [arr[0], arr[1], arr[2], arr[3], arr[4], arr[5]]; + } + return arr.slice(); +} + +function unwrapListeners(arr) { + const ret = arrayClone(arr); + for (let i = 0; i < ret.length; ++i) { + const orig = ret[i].listener; + if (typeof orig === "function") { + ret[i] = orig; + } + } + return ret; +} + +/** + * Returns a copy of the array of listeners for the event name + * specified as `type`. + * @param {EventEmitter | EventTarget} emitterOrTarget + * @param {string | symbol} type + * @returns {Function[]} + */ +export function getEventListeners(emitterOrTarget, type) { + // First check if EventEmitter + if (typeof emitterOrTarget.listeners === "function") { + return emitterOrTarget.listeners(type); + } + if (emitterOrTarget instanceof EventTarget) { + // TODO: kEvents is not defined + const root = emitterOrTarget[kEvents].get(type); + const listeners = []; + let handler = root?.next; + while (handler?.listener !== undefined) { + const listener = handler.listener?.deref + ? handler.listener.deref() + : handler.listener; + listeners.push(listener); + handler = handler.next; + } + return listeners; + } + throw new ERR_INVALID_ARG_TYPE( + "emitter", + ["EventEmitter", "EventTarget"], + emitterOrTarget, + ); +} + +/** + * Creates a `Promise` that is fulfilled when the emitter + * emits the given event. + * @param {EventEmitter} emitter + * @param {string} name + * @param {{ signal: AbortSignal; }} [options] + * @returns {Promise} + */ +// deno-lint-ignore require-await +export async function once(emitter, name, options = {}) { + const signal = options?.signal; + validateAbortSignal(signal, "options.signal"); + if (signal?.aborted) { + throw new AbortError(); + } + return new Promise((resolve, reject) => { + const errorListener = (err) => { + emitter.removeListener(name, resolver); + if (signal != null) { + eventTargetAgnosticRemoveListener(signal, "abort", abortListener); + } + reject(err); + }; + const resolver = (...args) => { + if (typeof emitter.removeListener === "function") { + emitter.removeListener("error", errorListener); + } + if (signal != null) { + eventTargetAgnosticRemoveListener(signal, "abort", abortListener); + } + resolve(args); + }; + eventTargetAgnosticAddListener(emitter, name, resolver, { once: true }); + if (name !== "error" && typeof emitter.once === "function") { + emitter.once("error", errorListener); + } + function abortListener() { + eventTargetAgnosticRemoveListener(emitter, name, resolver); + eventTargetAgnosticRemoveListener(emitter, "error", errorListener); + reject(new AbortError()); + } + if (signal != null) { + eventTargetAgnosticAddListener( + signal, + "abort", + abortListener, + { once: true }, + ); + } + }); +} + +const AsyncIteratorPrototype = Object.getPrototypeOf( + Object.getPrototypeOf(async function* () {}).prototype, +); + +function createIterResult(value, done) { + return { value, done }; +} + +function eventTargetAgnosticRemoveListener(emitter, name, listener, flags) { + if (typeof emitter.removeListener === "function") { + emitter.removeListener(name, listener); + } else if (typeof emitter.removeEventListener === "function") { + emitter.removeEventListener(name, listener, flags); + } else { + throw new ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter); + } +} + +function eventTargetAgnosticAddListener(emitter, name, listener, flags) { + if (typeof emitter.on === "function") { + if (flags?.once) { + emitter.once(name, listener); + } else { + emitter.on(name, listener); + } + } else if (typeof emitter.addEventListener === "function") { + // EventTarget does not have `error` event semantics like Node + // EventEmitters, we do not listen to `error` events here. + emitter.addEventListener(name, (arg) => { + listener(arg); + }, flags); + } else { + throw new ERR_INVALID_ARG_TYPE("emitter", "EventEmitter", emitter); + } +} + +/** + * Returns an `AsyncIterator` that iterates `event` events. + * @param {EventEmitter} emitter + * @param {string | symbol} event + * @param {{ signal: AbortSignal; }} [options] + * @returns {AsyncIterator} + */ +export function on(emitter, event, options) { + const signal = options?.signal; + validateAbortSignal(signal, "options.signal"); + if (signal?.aborted) { + throw new AbortError(); + } + + const unconsumedEvents = []; + const unconsumedPromises = []; + let error = null; + let finished = false; + + const iterator = Object.setPrototypeOf({ + next() { + // First, we consume all unread events + const value = unconsumedEvents.shift(); + if (value) { + return Promise.resolve(createIterResult(value, false)); + } + + // Then we error, if an error happened + // This happens one time if at all, because after 'error' + // we stop listening + if (error) { + const p = Promise.reject(error); + // Only the first element errors + error = null; + return p; + } + + // If the iterator is finished, resolve to done + if (finished) { + return Promise.resolve(createIterResult(undefined, true)); + } + + // Wait until an event happens + return new Promise(function (resolve, reject) { + unconsumedPromises.push({ resolve, reject }); + }); + }, + + return() { + eventTargetAgnosticRemoveListener(emitter, event, eventHandler); + eventTargetAgnosticRemoveListener(emitter, "error", errorHandler); + + if (signal) { + eventTargetAgnosticRemoveListener( + signal, + "abort", + abortListener, + { once: true }, + ); + } + + finished = true; + + for (const promise of unconsumedPromises) { + promise.resolve(createIterResult(undefined, true)); + } + + return Promise.resolve(createIterResult(undefined, true)); + }, + + throw(err) { + if (!err || !(err instanceof Error)) { + throw new ERR_INVALID_ARG_TYPE( + "EventEmitter.AsyncIterator", + "Error", + err, + ); + } + error = err; + eventTargetAgnosticRemoveListener(emitter, event, eventHandler); + eventTargetAgnosticRemoveListener(emitter, "error", errorHandler); + }, + + [Symbol.asyncIterator]() { + return this; + }, + }, AsyncIteratorPrototype); + + eventTargetAgnosticAddListener(emitter, event, eventHandler); + if (event !== "error" && typeof emitter.on === "function") { + emitter.on("error", errorHandler); + } + + if (signal) { + eventTargetAgnosticAddListener( + signal, + "abort", + abortListener, + { once: true }, + ); + } + + return iterator; + + function abortListener() { + errorHandler(new AbortError()); + } + + function eventHandler(...args) { + const promise = unconsumedPromises.shift(); + if (promise) { + promise.resolve(createIterResult(args, false)); + } else { + unconsumedEvents.push(args); + } + } + + function errorHandler(err) { + finished = true; + + const toError = unconsumedPromises.shift(); + + if (toError) { + toError.reject(err); + } else { + // The next time we call next() + error = err; + } + + iterator.return(); + } +} diff --git a/ext/node/polyfills/_fs/_fs_access.ts b/ext/node/polyfills/_fs/_fs_access.ts new file mode 100644 index 0000000000..9450c2f011 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_access.ts @@ -0,0 +1,127 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + type CallbackWithError, + makeCallback, +} from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { fs } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { + getValidatedPath, + getValidMode, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import type { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export function access( + path: string | Buffer | URL, + mode: number | CallbackWithError, + callback?: CallbackWithError, +) { + if (typeof mode === "function") { + callback = mode; + mode = fs.F_OK; + } + + path = getValidatedPath(path).toString(); + mode = getValidMode(mode, "access"); + const cb = makeCallback(callback); + + Deno.lstat(path).then((info) => { + if (info.mode === null) { + // If the file mode is unavailable, we pretend it has + // the permission + cb(null); + return; + } + const m = +mode || 0; + let fileMode = +info.mode || 0; + if (Deno.build.os !== "windows" && info.uid === Deno.uid()) { + // If the user is the owner of the file, then use the owner bits of + // the file permission + fileMode >>= 6; + } + // TODO(kt3k): Also check the case when the user belong to the group + // of the file + if ((m & fileMode) === m) { + // all required flags exist + cb(null); + } else { + // some required flags don't + // deno-lint-ignore no-explicit-any + const e: any = new Error(`EACCES: permission denied, access '${path}'`); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("EACCES"); + e.code = "EACCES"; + cb(e); + } + }, (err) => { + if (err instanceof Deno.errors.NotFound) { + // deno-lint-ignore no-explicit-any + const e: any = new Error( + `ENOENT: no such file or directory, access '${path}'`, + ); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("ENOENT"); + e.code = "ENOENT"; + cb(e); + } else { + cb(err); + } + }); +} + +export const accessPromise = promisify(access) as ( + path: string | Buffer | URL, + mode?: number, +) => Promise; + +export function accessSync(path: string | Buffer | URL, mode?: number) { + path = getValidatedPath(path).toString(); + mode = getValidMode(mode, "access"); + try { + const info = Deno.lstatSync(path.toString()); + if (info.mode === null) { + // If the file mode is unavailable, we pretend it has + // the permission + return; + } + const m = +mode! || 0; + let fileMode = +info.mode! || 0; + if (Deno.build.os !== "windows" && info.uid === Deno.uid()) { + // If the user is the owner of the file, then use the owner bits of + // the file permission + fileMode >>= 6; + } + // TODO(kt3k): Also check the case when the user belong to the group + // of the file + if ((m & fileMode) === m) { + // all required flags exist + } else { + // some required flags don't + // deno-lint-ignore no-explicit-any + const e: any = new Error(`EACCES: permission denied, access '${path}'`); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("EACCES"); + e.code = "EACCES"; + throw e; + } + } catch (err) { + if (err instanceof Deno.errors.NotFound) { + // deno-lint-ignore no-explicit-any + const e: any = new Error( + `ENOENT: no such file or directory, access '${path}'`, + ); + e.path = path; + e.syscall = "access"; + e.errno = codeMap.get("ENOENT"); + e.code = "ENOENT"; + throw e; + } else { + throw err; + } + } +} diff --git a/ext/node/polyfills/_fs/_fs_appendFile.ts b/ext/node/polyfills/_fs/_fs_appendFile.ts new file mode 100644 index 0000000000..d47afe81b6 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_appendFile.ts @@ -0,0 +1,73 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + CallbackWithError, + isFd, + maybeCallback, + WriteFileOptions, +} from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { Encodings } from "internal:deno_node/polyfills/_utils.ts"; +import { + copyObject, + getOptions, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { + writeFile, + writeFileSync, +} from "internal:deno_node/polyfills/_fs/_fs_writeFile.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +/** + * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function appendFile( + path: string | number | URL, + data: string | Uint8Array, + options: Encodings | WriteFileOptions | CallbackWithError, + callback?: CallbackWithError, +) { + callback = maybeCallback(callback || options); + options = getOptions(options, { encoding: "utf8", mode: 0o666, flag: "a" }); + + // Don't make changes directly on options object + options = copyObject(options); + + // Force append behavior when using a supplied file descriptor + if (!options.flag || isFd(path)) { + options.flag = "a"; + } + + writeFile(path, data, options, callback); +} + +/** + * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export const appendFilePromise = promisify(appendFile) as ( + path: string | number | URL, + data: string | Uint8Array, + options?: Encodings | WriteFileOptions, +) => Promise; + +/** + * TODO: Also accept 'data' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function appendFileSync( + path: string | number | URL, + data: string | Uint8Array, + options?: Encodings | WriteFileOptions, +) { + options = getOptions(options, { encoding: "utf8", mode: 0o666, flag: "a" }); + + // Don't make changes directly on options object + options = copyObject(options); + + // Force append behavior when using a supplied file descriptor + if (!options.flag || isFd(path)) { + options.flag = "a"; + } + + writeFileSync(path, data, options); +} diff --git a/ext/node/polyfills/_fs/_fs_chmod.ts b/ext/node/polyfills/_fs/_fs_chmod.ts new file mode 100644 index 0000000000..3a19e5622b --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_chmod.ts @@ -0,0 +1,69 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import type { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { getValidatedPath } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import * as pathModule from "internal:deno_node/polyfills/path.ts"; +import { parseFileMode } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export function chmod( + path: string | Buffer | URL, + mode: string | number, + callback: CallbackWithError, +) { + path = getValidatedPath(path).toString(); + + try { + mode = parseFileMode(mode, "mode"); + } catch (error) { + // TODO(PolarETech): Errors should not be ignored when Deno.chmod is supported on Windows. + // https://github.com/denoland/deno_std/issues/2916 + if (Deno.build.os === "windows") { + mode = 0; // set dummy value to avoid type checking error at Deno.chmod + } else { + throw error; + } + } + + Deno.chmod(pathModule.toNamespacedPath(path), mode).catch((error) => { + // Ignore NotSupportedError that occurs on windows + // https://github.com/denoland/deno_std/issues/2995 + if (!(error instanceof Deno.errors.NotSupported)) { + throw error; + } + }).then( + () => callback(null), + callback, + ); +} + +export const chmodPromise = promisify(chmod) as ( + path: string | Buffer | URL, + mode: string | number, +) => Promise; + +export function chmodSync(path: string | URL, mode: string | number) { + path = getValidatedPath(path).toString(); + + try { + mode = parseFileMode(mode, "mode"); + } catch (error) { + // TODO(PolarETech): Errors should not be ignored when Deno.chmodSync is supported on Windows. + // https://github.com/denoland/deno_std/issues/2916 + if (Deno.build.os === "windows") { + mode = 0; // set dummy value to avoid type checking error at Deno.chmodSync + } else { + throw error; + } + } + + try { + Deno.chmodSync(pathModule.toNamespacedPath(path), mode); + } catch (error) { + // Ignore NotSupportedError that occurs on windows + // https://github.com/denoland/deno_std/issues/2995 + if (!(error instanceof Deno.errors.NotSupported)) { + throw error; + } + } +} diff --git a/ext/node/polyfills/_fs/_fs_chown.ts b/ext/node/polyfills/_fs/_fs_chown.ts new file mode 100644 index 0000000000..55a469fbaf --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_chown.ts @@ -0,0 +1,56 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + type CallbackWithError, + makeCallback, +} from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { + getValidatedPath, + kMaxUserId, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import * as pathModule from "internal:deno_node/polyfills/path.ts"; +import { validateInteger } from "internal:deno_node/polyfills/internal/validators.mjs"; +import type { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +/** + * Asynchronously changes the owner and group + * of a file. + */ +export function chown( + path: string | Buffer | URL, + uid: number, + gid: number, + callback: CallbackWithError, +) { + callback = makeCallback(callback); + path = getValidatedPath(path).toString(); + validateInteger(uid, "uid", -1, kMaxUserId); + validateInteger(gid, "gid", -1, kMaxUserId); + + Deno.chown(pathModule.toNamespacedPath(path), uid, gid).then( + () => callback(null), + callback, + ); +} + +export const chownPromise = promisify(chown) as ( + path: string | Buffer | URL, + uid: number, + gid: number, +) => Promise; + +/** + * Synchronously changes the owner and group + * of a file. + */ +export function chownSync( + path: string | Buffer | URL, + uid: number, + gid: number, +) { + path = getValidatedPath(path).toString(); + validateInteger(uid, "uid", -1, kMaxUserId); + validateInteger(gid, "gid", -1, kMaxUserId); + + Deno.chownSync(pathModule.toNamespacedPath(path), uid, gid); +} diff --git a/ext/node/polyfills/_fs/_fs_close.ts b/ext/node/polyfills/_fs/_fs_close.ts new file mode 100644 index 0000000000..ff6082980b --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_close.ts @@ -0,0 +1,21 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import type { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { getValidatedFd } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; + +export function close(fd: number, callback: CallbackWithError) { + fd = getValidatedFd(fd); + setTimeout(() => { + let error = null; + try { + Deno.close(fd); + } catch (err) { + error = err instanceof Error ? err : new Error("[non-error thrown]"); + } + callback(error); + }, 0); +} + +export function closeSync(fd: number) { + fd = getValidatedFd(fd); + Deno.close(fd); +} diff --git a/ext/node/polyfills/_fs/_fs_common.ts b/ext/node/polyfills/_fs/_fs_common.ts new file mode 100644 index 0000000000..1e9f0f2668 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_common.ts @@ -0,0 +1,233 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + O_APPEND, + O_CREAT, + O_EXCL, + O_RDONLY, + O_RDWR, + O_TRUNC, + O_WRONLY, +} from "internal:deno_node/polyfills/_fs/_fs_constants.ts"; +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import type { ErrnoException } from "internal:deno_node/polyfills/_global.d.ts"; +import { + BinaryEncodings, + Encodings, + notImplemented, + TextEncodings, +} from "internal:deno_node/polyfills/_utils.ts"; + +export type CallbackWithError = (err: ErrnoException | null) => void; + +export interface FileOptions { + encoding?: Encodings; + flag?: string; + signal?: AbortSignal; +} + +export type TextOptionsArgument = + | TextEncodings + | ({ encoding: TextEncodings } & FileOptions); +export type BinaryOptionsArgument = + | BinaryEncodings + | ({ encoding: BinaryEncodings } & FileOptions); +export type FileOptionsArgument = Encodings | FileOptions; + +export interface WriteFileOptions extends FileOptions { + mode?: number; +} + +export function isFileOptions( + fileOptions: string | WriteFileOptions | undefined, +): fileOptions is FileOptions { + if (!fileOptions) return false; + + return ( + (fileOptions as FileOptions).encoding != undefined || + (fileOptions as FileOptions).flag != undefined || + (fileOptions as FileOptions).signal != undefined || + (fileOptions as WriteFileOptions).mode != undefined + ); +} + +export function getEncoding( + optOrCallback?: + | FileOptions + | WriteFileOptions + // deno-lint-ignore no-explicit-any + | ((...args: any[]) => any) + | Encodings + | null, +): Encodings | null { + if (!optOrCallback || typeof optOrCallback === "function") { + return null; + } + + const encoding = typeof optOrCallback === "string" + ? optOrCallback + : optOrCallback.encoding; + if (!encoding) return null; + return encoding; +} + +export function checkEncoding(encoding: Encodings | null): Encodings | null { + if (!encoding) return null; + + encoding = encoding.toLowerCase() as Encodings; + if (["utf8", "hex", "base64"].includes(encoding)) return encoding; + + if (encoding === "utf-8") { + return "utf8"; + } + if (encoding === "binary") { + return "binary"; + // before this was buffer, however buffer is not used in Node + // node -e "require('fs').readFile('../world.txt', 'buffer', console.log)" + } + + const notImplementedEncodings = ["utf16le", "latin1", "ascii", "ucs2"]; + + if (notImplementedEncodings.includes(encoding as string)) { + notImplemented(`"${encoding}" encoding`); + } + + throw new Error(`The value "${encoding}" is invalid for option "encoding"`); +} + +export function getOpenOptions( + flag: string | number | undefined, +): Deno.OpenOptions { + if (!flag) { + return { create: true, append: true }; + } + + let openOptions: Deno.OpenOptions = {}; + + if (typeof flag === "string") { + switch (flag) { + case "a": { + // 'a': Open file for appending. The file is created if it does not exist. + openOptions = { create: true, append: true }; + break; + } + case "ax": + case "xa": { + // 'ax', 'xa': Like 'a' but fails if the path exists. + openOptions = { createNew: true, write: true, append: true }; + break; + } + case "a+": { + // 'a+': Open file for reading and appending. The file is created if it does not exist. + openOptions = { read: true, create: true, append: true }; + break; + } + case "ax+": + case "xa+": { + // 'ax+', 'xa+': Like 'a+' but fails if the path exists. + openOptions = { read: true, createNew: true, append: true }; + break; + } + case "r": { + // 'r': Open file for reading. An exception occurs if the file does not exist. + openOptions = { read: true }; + break; + } + case "r+": { + // 'r+': Open file for reading and writing. An exception occurs if the file does not exist. + openOptions = { read: true, write: true }; + break; + } + case "w": { + // 'w': Open file for writing. The file is created (if it does not exist) or truncated (if it exists). + openOptions = { create: true, write: true, truncate: true }; + break; + } + case "wx": + case "xw": { + // 'wx', 'xw': Like 'w' but fails if the path exists. + openOptions = { createNew: true, write: true }; + break; + } + case "w+": { + // 'w+': Open file for reading and writing. The file is created (if it does not exist) or truncated (if it exists). + openOptions = { create: true, write: true, truncate: true, read: true }; + break; + } + case "wx+": + case "xw+": { + // 'wx+', 'xw+': Like 'w+' but fails if the path exists. + openOptions = { createNew: true, write: true, read: true }; + break; + } + case "as": + case "sa": { + // 'as', 'sa': Open file for appending in synchronous mode. The file is created if it does not exist. + openOptions = { create: true, append: true }; + break; + } + case "as+": + case "sa+": { + // 'as+', 'sa+': Open file for reading and appending in synchronous mode. The file is created if it does not exist. + openOptions = { create: true, read: true, append: true }; + break; + } + case "rs+": + case "sr+": { + // 'rs+', 'sr+': Open file for reading and writing in synchronous mode. Instructs the operating system to bypass the local file system cache. + openOptions = { create: true, read: true, write: true }; + break; + } + default: { + throw new Error(`Unrecognized file system flag: ${flag}`); + } + } + } else if (typeof flag === "number") { + if ((flag & O_APPEND) === O_APPEND) { + openOptions.append = true; + } + if ((flag & O_CREAT) === O_CREAT) { + openOptions.create = true; + openOptions.write = true; + } + if ((flag & O_EXCL) === O_EXCL) { + openOptions.createNew = true; + openOptions.read = true; + openOptions.write = true; + } + if ((flag & O_TRUNC) === O_TRUNC) { + openOptions.truncate = true; + } + if ((flag & O_RDONLY) === O_RDONLY) { + openOptions.read = true; + } + if ((flag & O_WRONLY) === O_WRONLY) { + openOptions.write = true; + } + if ((flag & O_RDWR) === O_RDWR) { + openOptions.read = true; + openOptions.write = true; + } + } + + return openOptions; +} + +export { isUint32 as isFd } from "internal:deno_node/polyfills/internal/validators.mjs"; + +export function maybeCallback(cb: unknown) { + validateFunction(cb, "cb"); + + return cb as CallbackWithError; +} + +// Ensure that callbacks run in the global context. Only use this function +// for callbacks that are passed to the binding layer, callbacks that are +// invoked from JS already run in the proper scope. +export function makeCallback( + this: unknown, + cb?: (err: Error | null, result?: unknown) => void, +) { + validateFunction(cb, "cb"); + + return (...args: unknown[]) => Reflect.apply(cb!, this, args); +} diff --git a/ext/node/polyfills/_fs/_fs_constants.ts b/ext/node/polyfills/_fs/_fs_constants.ts new file mode 100644 index 0000000000..761f6a9b7a --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_constants.ts @@ -0,0 +1,39 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { fs } from "internal:deno_node/polyfills/internal_binding/constants.ts"; + +export const { + F_OK, + R_OK, + W_OK, + X_OK, + S_IRUSR, + S_IWUSR, + S_IXUSR, + S_IRGRP, + S_IWGRP, + S_IXGRP, + S_IROTH, + S_IWOTH, + S_IXOTH, + COPYFILE_EXCL, + COPYFILE_FICLONE, + COPYFILE_FICLONE_FORCE, + UV_FS_COPYFILE_EXCL, + UV_FS_COPYFILE_FICLONE, + UV_FS_COPYFILE_FICLONE_FORCE, + O_RDONLY, + O_WRONLY, + O_RDWR, + O_NOCTTY, + O_TRUNC, + O_APPEND, + O_DIRECTORY, + O_NOFOLLOW, + O_SYNC, + O_DSYNC, + O_SYMLINK, + O_NONBLOCK, + O_CREAT, + O_EXCL, +} = fs; diff --git a/ext/node/polyfills/_fs/_fs_copy.ts b/ext/node/polyfills/_fs/_fs_copy.ts new file mode 100644 index 0000000000..0971da1eb9 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_copy.ts @@ -0,0 +1,88 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import type { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { makeCallback } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + getValidatedPath, + getValidMode, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { fs } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export function copyFile( + src: string | Buffer | URL, + dest: string | Buffer | URL, + callback: CallbackWithError, +): void; +export function copyFile( + src: string | Buffer | URL, + dest: string | Buffer | URL, + mode: number, + callback: CallbackWithError, +): void; +export function copyFile( + src: string | Buffer | URL, + dest: string | Buffer | URL, + mode: number | CallbackWithError, + callback?: CallbackWithError, +) { + if (typeof mode === "function") { + callback = mode; + mode = 0; + } + const srcStr = getValidatedPath(src, "src").toString(); + const destStr = getValidatedPath(dest, "dest").toString(); + const modeNum = getValidMode(mode, "copyFile"); + const cb = makeCallback(callback); + + if ((modeNum & fs.COPYFILE_EXCL) === fs.COPYFILE_EXCL) { + Deno.lstat(destStr).then(() => { + // deno-lint-ignore no-explicit-any + const e: any = new Error( + `EEXIST: file already exists, copyfile '${srcStr}' -> '${destStr}'`, + ); + e.syscall = "copyfile"; + e.errno = codeMap.get("EEXIST"); + e.code = "EEXIST"; + cb(e); + }, (e) => { + if (e instanceof Deno.errors.NotFound) { + Deno.copyFile(srcStr, destStr).then(() => cb(null), cb); + } + cb(e); + }); + } else { + Deno.copyFile(srcStr, destStr).then(() => cb(null), cb); + } +} + +export const copyFilePromise = promisify(copyFile) as ( + src: string | Buffer | URL, + dest: string | Buffer | URL, + mode?: number, +) => Promise; + +export function copyFileSync( + src: string | Buffer | URL, + dest: string | Buffer | URL, + mode?: number, +) { + const srcStr = getValidatedPath(src, "src").toString(); + const destStr = getValidatedPath(dest, "dest").toString(); + const modeNum = getValidMode(mode, "copyFile"); + + if ((modeNum & fs.COPYFILE_EXCL) === fs.COPYFILE_EXCL) { + try { + Deno.lstatSync(destStr); + throw new Error(`A file exists at the destination: ${destStr}`); + } catch (e) { + if (e instanceof Deno.errors.NotFound) { + Deno.copyFileSync(srcStr, destStr); + } + throw e; + } + } else { + Deno.copyFileSync(srcStr, destStr); + } +} diff --git a/ext/node/polyfills/_fs/_fs_dir.ts b/ext/node/polyfills/_fs/_fs_dir.ts new file mode 100644 index 0000000000..e135472414 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_dir.ts @@ -0,0 +1,104 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import Dirent from "internal:deno_node/polyfills/_fs/_fs_dirent.ts"; +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { ERR_MISSING_ARGS } from "internal:deno_node/polyfills/internal/errors.ts"; +import { TextDecoder } from "internal:deno_web/08_text_encoding.js"; + +export default class Dir { + #dirPath: string | Uint8Array; + #syncIterator!: Iterator | null; + #asyncIterator!: AsyncIterator | null; + + constructor(path: string | Uint8Array) { + if (!path) { + throw new ERR_MISSING_ARGS("path"); + } + this.#dirPath = path; + } + + get path(): string { + if (this.#dirPath instanceof Uint8Array) { + return new TextDecoder().decode(this.#dirPath); + } + return this.#dirPath; + } + + // deno-lint-ignore no-explicit-any + read(callback?: (...args: any[]) => void): Promise { + return new Promise((resolve, reject) => { + if (!this.#asyncIterator) { + this.#asyncIterator = Deno.readDir(this.path)[Symbol.asyncIterator](); + } + assert(this.#asyncIterator); + this.#asyncIterator + .next() + .then((iteratorResult) => { + resolve( + iteratorResult.done ? null : new Dirent(iteratorResult.value), + ); + if (callback) { + callback( + null, + iteratorResult.done ? null : new Dirent(iteratorResult.value), + ); + } + }, (err) => { + if (callback) { + callback(err); + } + reject(err); + }); + }); + } + + readSync(): Dirent | null { + if (!this.#syncIterator) { + this.#syncIterator = Deno.readDirSync(this.path)![Symbol.iterator](); + } + + const iteratorResult = this.#syncIterator.next(); + if (iteratorResult.done) { + return null; + } else { + return new Dirent(iteratorResult.value); + } + } + + /** + * Unlike Node, Deno does not require managing resource ids for reading + * directories, and therefore does not need to close directories when + * finished reading. + */ + // deno-lint-ignore no-explicit-any + close(callback?: (...args: any[]) => void): Promise { + return new Promise((resolve) => { + if (callback) { + callback(null); + } + resolve(); + }); + } + + /** + * Unlike Node, Deno does not require managing resource ids for reading + * directories, and therefore does not need to close directories when + * finished reading + */ + closeSync() { + //No op + } + + async *[Symbol.asyncIterator](): AsyncIterableIterator { + try { + while (true) { + const dirent: Dirent | null = await this.read(); + if (dirent === null) { + break; + } + yield dirent; + } + } finally { + await this.close(); + } + } +} diff --git a/ext/node/polyfills/_fs/_fs_dirent.ts b/ext/node/polyfills/_fs/_fs_dirent.ts new file mode 100644 index 0000000000..5a7c243bf4 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_dirent.ts @@ -0,0 +1,46 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export default class Dirent { + constructor(private entry: Deno.DirEntry) {} + + isBlockDevice(): boolean { + notImplemented("Deno does not yet support identification of block devices"); + return false; + } + + isCharacterDevice(): boolean { + notImplemented( + "Deno does not yet support identification of character devices", + ); + return false; + } + + isDirectory(): boolean { + return this.entry.isDirectory; + } + + isFIFO(): boolean { + notImplemented( + "Deno does not yet support identification of FIFO named pipes", + ); + return false; + } + + isFile(): boolean { + return this.entry.isFile; + } + + isSocket(): boolean { + notImplemented("Deno does not yet support identification of sockets"); + return false; + } + + isSymbolicLink(): boolean { + return this.entry.isSymlink; + } + + get name(): string | null { + return this.entry.name; + } +} diff --git a/ext/node/polyfills/_fs/_fs_exists.ts b/ext/node/polyfills/_fs/_fs_exists.ts new file mode 100644 index 0000000000..9b0f183034 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_exists.ts @@ -0,0 +1,40 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; + +type ExistsCallback = (exists: boolean) => void; + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + * Deprecated in node api + */ +export function exists(path: string | URL, callback: ExistsCallback) { + path = path instanceof URL ? fromFileUrl(path) : path; + Deno.lstat(path).then(() => callback(true), () => callback(false)); +} + +// The callback of fs.exists doesn't have standard callback signature. +// We need to provide special implementation for promisify. +// See https://github.com/nodejs/node/pull/13316 +const kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom"); +Object.defineProperty(exists, kCustomPromisifiedSymbol, { + value: (path: string | URL) => { + return new Promise((resolve) => { + exists(path, (exists) => resolve(exists)); + }); + }, +}); + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer or URL type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function existsSync(path: string | URL): boolean { + path = path instanceof URL ? fromFileUrl(path) : path; + try { + Deno.lstatSync(path); + return true; + } catch (_err) { + return false; + } +} diff --git a/ext/node/polyfills/_fs/_fs_fdatasync.ts b/ext/node/polyfills/_fs/_fs_fdatasync.ts new file mode 100644 index 0000000000..325ac30da6 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_fdatasync.ts @@ -0,0 +1,13 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; + +export function fdatasync( + fd: number, + callback: CallbackWithError, +) { + Deno.fdatasync(fd).then(() => callback(null), callback); +} + +export function fdatasyncSync(fd: number) { + Deno.fdatasyncSync(fd); +} diff --git a/ext/node/polyfills/_fs/_fs_fstat.ts b/ext/node/polyfills/_fs/_fs_fstat.ts new file mode 100644 index 0000000000..ab9cbead46 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_fstat.ts @@ -0,0 +1,60 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + BigIntStats, + CFISBIS, + statCallback, + statCallbackBigInt, + statOptions, + Stats, +} from "internal:deno_node/polyfills/_fs/_fs_stat.ts"; + +export function fstat(fd: number, callback: statCallback): void; +export function fstat( + fd: number, + options: { bigint: false }, + callback: statCallback, +): void; +export function fstat( + fd: number, + options: { bigint: true }, + callback: statCallbackBigInt, +): void; +export function fstat( + fd: number, + optionsOrCallback: statCallback | statCallbackBigInt | statOptions, + maybeCallback?: statCallback | statCallbackBigInt, +) { + const callback = + (typeof optionsOrCallback === "function" + ? optionsOrCallback + : maybeCallback) as ( + ...args: [Error] | [null, BigIntStats | Stats] + ) => void; + const options = typeof optionsOrCallback === "object" + ? optionsOrCallback + : { bigint: false }; + + if (!callback) throw new Error("No callback function supplied"); + + Deno.fstat(fd).then( + (stat) => callback(null, CFISBIS(stat, options.bigint)), + (err) => callback(err), + ); +} + +export function fstatSync(fd: number): Stats; +export function fstatSync( + fd: number, + options: { bigint: false }, +): Stats; +export function fstatSync( + fd: number, + options: { bigint: true }, +): BigIntStats; +export function fstatSync( + fd: number, + options?: statOptions, +): Stats | BigIntStats { + const origin = Deno.fstatSync(fd); + return CFISBIS(origin, options?.bigint || false); +} diff --git a/ext/node/polyfills/_fs/_fs_fsync.ts b/ext/node/polyfills/_fs/_fs_fsync.ts new file mode 100644 index 0000000000..02be24abcf --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_fsync.ts @@ -0,0 +1,13 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; + +export function fsync( + fd: number, + callback: CallbackWithError, +) { + Deno.fsync(fd).then(() => callback(null), callback); +} + +export function fsyncSync(fd: number) { + Deno.fsyncSync(fd); +} diff --git a/ext/node/polyfills/_fs/_fs_ftruncate.ts b/ext/node/polyfills/_fs/_fs_ftruncate.ts new file mode 100644 index 0000000000..9c7bfbd016 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_ftruncate.ts @@ -0,0 +1,23 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; + +export function ftruncate( + fd: number, + lenOrCallback: number | CallbackWithError, + maybeCallback?: CallbackWithError, +) { + const len: number | undefined = typeof lenOrCallback === "number" + ? lenOrCallback + : undefined; + const callback: CallbackWithError = typeof lenOrCallback === "function" + ? lenOrCallback + : maybeCallback as CallbackWithError; + + if (!callback) throw new Error("No callback function supplied"); + + Deno.ftruncate(fd, len).then(() => callback(null), callback); +} + +export function ftruncateSync(fd: number, len?: number) { + Deno.ftruncateSync(fd, len); +} diff --git a/ext/node/polyfills/_fs/_fs_futimes.ts b/ext/node/polyfills/_fs/_fs_futimes.ts new file mode 100644 index 0000000000..60f06bc34e --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_futimes.ts @@ -0,0 +1,50 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import type { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; + +function getValidTime( + time: number | string | Date, + name: string, +): number | Date { + if (typeof time === "string") { + time = Number(time); + } + + if ( + typeof time === "number" && + (Number.isNaN(time) || !Number.isFinite(time)) + ) { + throw new Deno.errors.InvalidData( + `invalid ${name}, must not be infinity or NaN`, + ); + } + + return time; +} + +export function futimes( + fd: number, + atime: number | string | Date, + mtime: number | string | Date, + callback: CallbackWithError, +) { + if (!callback) { + throw new Deno.errors.InvalidData("No callback function supplied"); + } + + atime = getValidTime(atime, "atime"); + mtime = getValidTime(mtime, "mtime"); + + Deno.futime(fd, atime, mtime).then(() => callback(null), callback); +} + +export function futimesSync( + fd: number, + atime: number | string | Date, + mtime: number | string | Date, +) { + atime = getValidTime(atime, "atime"); + mtime = getValidTime(mtime, "mtime"); + + Deno.futimeSync(fd, atime, mtime); +} diff --git a/ext/node/polyfills/_fs/_fs_link.ts b/ext/node/polyfills/_fs/_fs_link.ts new file mode 100644 index 0000000000..eb95a10f6d --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_link.ts @@ -0,0 +1,46 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import type { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function link( + existingPath: string | URL, + newPath: string | URL, + callback: CallbackWithError, +) { + existingPath = existingPath instanceof URL + ? fromFileUrl(existingPath) + : existingPath; + newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; + + Deno.link(existingPath, newPath).then(() => callback(null), callback); +} + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export const linkPromise = promisify(link) as ( + existingPath: string | URL, + newPath: string | URL, +) => Promise; + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function linkSync( + existingPath: string | URL, + newPath: string | URL, +) { + existingPath = existingPath instanceof URL + ? fromFileUrl(existingPath) + : existingPath; + newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; + + Deno.linkSync(existingPath, newPath); +} diff --git a/ext/node/polyfills/_fs/_fs_lstat.ts b/ext/node/polyfills/_fs/_fs_lstat.ts new file mode 100644 index 0000000000..c85f82a11b --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_lstat.ts @@ -0,0 +1,67 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + BigIntStats, + CFISBIS, + statCallback, + statCallbackBigInt, + statOptions, + Stats, +} from "internal:deno_node/polyfills/_fs/_fs_stat.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export function lstat(path: string | URL, callback: statCallback): void; +export function lstat( + path: string | URL, + options: { bigint: false }, + callback: statCallback, +): void; +export function lstat( + path: string | URL, + options: { bigint: true }, + callback: statCallbackBigInt, +): void; +export function lstat( + path: string | URL, + optionsOrCallback: statCallback | statCallbackBigInt | statOptions, + maybeCallback?: statCallback | statCallbackBigInt, +) { + const callback = + (typeof optionsOrCallback === "function" + ? optionsOrCallback + : maybeCallback) as ( + ...args: [Error] | [null, BigIntStats | Stats] + ) => void; + const options = typeof optionsOrCallback === "object" + ? optionsOrCallback + : { bigint: false }; + + if (!callback) throw new Error("No callback function supplied"); + + Deno.lstat(path).then( + (stat) => callback(null, CFISBIS(stat, options.bigint)), + (err) => callback(err), + ); +} + +export const lstatPromise = promisify(lstat) as ( + & ((path: string | URL) => Promise) + & ((path: string | URL, options: { bigint: false }) => Promise) + & ((path: string | URL, options: { bigint: true }) => Promise) +); + +export function lstatSync(path: string | URL): Stats; +export function lstatSync( + path: string | URL, + options: { bigint: false }, +): Stats; +export function lstatSync( + path: string | URL, + options: { bigint: true }, +): BigIntStats; +export function lstatSync( + path: string | URL, + options?: statOptions, +): Stats | BigIntStats { + const origin = Deno.lstatSync(path); + return CFISBIS(origin, options?.bigint || false); +} diff --git a/ext/node/polyfills/_fs/_fs_mkdir.ts b/ext/node/polyfills/_fs/_fs_mkdir.ts new file mode 100644 index 0000000000..ac4b782591 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_mkdir.ts @@ -0,0 +1,77 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import type { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; +import { denoErrorToNodeError } from "internal:deno_node/polyfills/internal/errors.ts"; +import { getValidatedPath } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { validateBoolean } from "internal:deno_node/polyfills/internal/validators.mjs"; + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +type MkdirOptions = + | { recursive?: boolean; mode?: number | undefined } + | number + | boolean; + +export function mkdir( + path: string | URL, + options?: MkdirOptions | CallbackWithError, + callback?: CallbackWithError, +) { + path = getValidatedPath(path) as string; + + let mode = 0o777; + let recursive = false; + + if (typeof options == "function") { + callback = options; + } else if (typeof options === "number") { + mode = options; + } else if (typeof options === "boolean") { + recursive = options; + } else if (options) { + if (options.recursive !== undefined) recursive = options.recursive; + if (options.mode !== undefined) mode = options.mode; + } + validateBoolean(recursive, "options.recursive"); + + Deno.mkdir(path, { recursive, mode }) + .then(() => { + if (typeof callback === "function") { + callback(null); + } + }, (err) => { + if (typeof callback === "function") { + callback(err); + } + }); +} + +export const mkdirPromise = promisify(mkdir) as ( + path: string | URL, + options?: MkdirOptions, +) => Promise; + +export function mkdirSync(path: string | URL, options?: MkdirOptions) { + path = getValidatedPath(path) as string; + + let mode = 0o777; + let recursive = false; + + if (typeof options === "number") { + mode = options; + } else if (typeof options === "boolean") { + recursive = options; + } else if (options) { + if (options.recursive !== undefined) recursive = options.recursive; + if (options.mode !== undefined) mode = options.mode; + } + validateBoolean(recursive, "options.recursive"); + + try { + Deno.mkdirSync(path, { recursive, mode }); + } catch (err) { + throw denoErrorToNodeError(err as Error, { syscall: "mkdir", path }); + } +} diff --git a/ext/node/polyfills/_fs/_fs_mkdtemp.ts b/ext/node/polyfills/_fs/_fs_mkdtemp.ts new file mode 100644 index 0000000000..de227b216e --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_mkdtemp.ts @@ -0,0 +1,115 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Node.js contributors. All rights reserved. MIT License. + +import { + TextDecoder, + TextEncoder, +} from "internal:deno_web/08_text_encoding.js"; +import { existsSync } from "internal:deno_node/polyfills/_fs/_fs_exists.ts"; +import { + mkdir, + mkdirSync, +} from "internal:deno_node/polyfills/_fs/_fs_mkdir.ts"; +import { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_OPT_VALUE_ENCODING, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export type mkdtempCallback = ( + err: Error | null, + directory?: string, +) => void; + +// https://nodejs.org/dist/latest-v15.x/docs/api/fs.html#fs_fs_mkdtemp_prefix_options_callback +export function mkdtemp(prefix: string, callback: mkdtempCallback): void; +export function mkdtemp( + prefix: string, + options: { encoding: string } | string, + callback: mkdtempCallback, +): void; +export function mkdtemp( + prefix: string, + optionsOrCallback: { encoding: string } | string | mkdtempCallback, + maybeCallback?: mkdtempCallback, +) { + const callback: mkdtempCallback | undefined = + typeof optionsOrCallback == "function" ? optionsOrCallback : maybeCallback; + if (!callback) { + throw new ERR_INVALID_ARG_TYPE("callback", "function", callback); + } + + const encoding: string | undefined = parseEncoding(optionsOrCallback); + const path = tempDirPath(prefix); + + mkdir( + path, + { recursive: false, mode: 0o700 }, + (err: Error | null | undefined) => { + if (err) callback(err); + else callback(null, decode(path, encoding)); + }, + ); +} + +export const mkdtempPromise = promisify(mkdtemp) as ( + prefix: string, + options?: { encoding: string } | string, +) => Promise; + +// https://nodejs.org/dist/latest-v15.x/docs/api/fs.html#fs_fs_mkdtempsync_prefix_options +export function mkdtempSync( + prefix: string, + options?: { encoding: string } | string, +): string { + const encoding: string | undefined = parseEncoding(options); + const path = tempDirPath(prefix); + + mkdirSync(path, { recursive: false, mode: 0o700 }); + return decode(path, encoding); +} + +function parseEncoding( + optionsOrCallback?: { encoding: string } | string | mkdtempCallback, +): string | undefined { + let encoding: string | undefined; + if (typeof optionsOrCallback == "function") encoding = undefined; + else if (optionsOrCallback instanceof Object) { + encoding = optionsOrCallback?.encoding; + } else encoding = optionsOrCallback; + + if (encoding) { + try { + new TextDecoder(encoding); + } catch { + throw new ERR_INVALID_OPT_VALUE_ENCODING(encoding); + } + } + + return encoding; +} + +function decode(str: string, encoding?: string): string { + if (!encoding) return str; + else { + const decoder = new TextDecoder(encoding); + const encoder = new TextEncoder(); + return decoder.decode(encoder.encode(str)); + } +} + +const CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +function randomName(): string { + return [...Array(6)].map(() => + CHARS[Math.floor(Math.random() * CHARS.length)] + ).join(""); +} + +function tempDirPath(prefix: string): string { + let path: string; + do { + path = prefix + randomName(); + } while (existsSync(path)); + + return path; +} diff --git a/ext/node/polyfills/_fs/_fs_open.ts b/ext/node/polyfills/_fs/_fs_open.ts new file mode 100644 index 0000000000..e703da56fd --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_open.ts @@ -0,0 +1,198 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + O_APPEND, + O_CREAT, + O_EXCL, + O_RDWR, + O_TRUNC, + O_WRONLY, +} from "internal:deno_node/polyfills/_fs/_fs_constants.ts"; +import { getOpenOptions } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; +import { parseFileMode } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { getValidatedPath } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import type { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function existsSync(filePath: string | URL): boolean { + try { + Deno.lstatSync(filePath); + return true; + } catch (error) { + if (error instanceof Deno.errors.NotFound) { + return false; + } + throw error; + } +} + +const FLAGS_AX = O_APPEND | O_CREAT | O_WRONLY | O_EXCL; +const FLAGS_AX_PLUS = O_APPEND | O_CREAT | O_RDWR | O_EXCL; +const FLAGS_WX = O_TRUNC | O_CREAT | O_WRONLY | O_EXCL; +const FLAGS_WX_PLUS = O_TRUNC | O_CREAT | O_RDWR | O_EXCL; + +export type openFlags = + | "a" + | "ax" + | "a+" + | "ax+" + | "as" + | "as+" + | "r" + | "r+" + | "rs+" + | "w" + | "wx" + | "w+" + | "wx+" + | number; + +type openCallback = (err: Error | null, fd: number) => void; + +function convertFlagAndModeToOptions( + flag?: openFlags, + mode?: number, +): Deno.OpenOptions | undefined { + if (!flag && !mode) return undefined; + if (!flag && mode) return { mode }; + return { ...getOpenOptions(flag), mode }; +} + +export function open(path: string | Buffer | URL, callback: openCallback): void; +export function open( + path: string | Buffer | URL, + flags: openFlags, + callback: openCallback, +): void; +export function open( + path: string | Buffer | URL, + flags: openFlags, + mode: number, + callback: openCallback, +): void; +export function open( + path: string | Buffer | URL, + flags: openCallback | openFlags, + mode?: openCallback | number, + callback?: openCallback, +) { + if (flags === undefined) { + throw new ERR_INVALID_ARG_TYPE( + "flags or callback", + ["string", "function"], + flags, + ); + } + path = getValidatedPath(path); + if (arguments.length < 3) { + // deno-lint-ignore no-explicit-any + callback = flags as any; + flags = "r"; + mode = 0o666; + } else if (typeof mode === "function") { + callback = mode; + mode = 0o666; + } else { + mode = parseFileMode(mode, "mode", 0o666); + } + + if (typeof callback !== "function") { + throw new ERR_INVALID_ARG_TYPE( + "callback", + "function", + callback, + ); + } + + if (flags === undefined) { + flags = "r"; + } + + if ( + existenceCheckRequired(flags as openFlags) && + existsSync(path as string) + ) { + const err = new Error(`EEXIST: file already exists, open '${path}'`); + (callback as (err: Error) => void)(err); + } else { + if (flags === "as" || flags === "as+") { + let err: Error | null = null, res: number; + try { + res = openSync(path, flags, mode); + } catch (error) { + err = error instanceof Error ? error : new Error("[non-error thrown]"); + } + if (err) { + (callback as (err: Error) => void)(err); + } else { + callback(null, res!); + } + return; + } + Deno.open( + path as string, + convertFlagAndModeToOptions(flags as openFlags, mode), + ).then( + (file) => callback!(null, file.rid), + (err) => (callback as (err: Error) => void)(err), + ); + } +} + +export const openPromise = promisify(open) as ( + & ((path: string | Buffer | URL) => Promise) + & ((path: string | Buffer | URL, flags: openFlags) => Promise) + & ((path: string | Buffer | URL, mode?: number) => Promise) + & (( + path: string | Buffer | URL, + flags?: openFlags, + mode?: number, + ) => Promise) +); + +export function openSync(path: string | Buffer | URL): number; +export function openSync( + path: string | Buffer | URL, + flags?: openFlags, +): number; +export function openSync(path: string | Buffer | URL, mode?: number): number; +export function openSync( + path: string | Buffer | URL, + flags?: openFlags, + mode?: number, +): number; +export function openSync( + path: string | Buffer | URL, + flags?: openFlags, + maybeMode?: number, +) { + const mode = parseFileMode(maybeMode, "mode", 0o666); + path = getValidatedPath(path); + + if (flags === undefined) { + flags = "r"; + } + + if ( + existenceCheckRequired(flags) && + existsSync(path as string) + ) { + throw new Error(`EEXIST: file already exists, open '${path}'`); + } + + return Deno.openSync(path as string, convertFlagAndModeToOptions(flags, mode)) + .rid; +} + +function existenceCheckRequired(flags: openFlags | number) { + return ( + (typeof flags === "string" && + ["ax", "ax+", "wx", "wx+"].includes(flags)) || + (typeof flags === "number" && ( + ((flags & FLAGS_AX) === FLAGS_AX) || + ((flags & FLAGS_AX_PLUS) === FLAGS_AX_PLUS) || + ((flags & FLAGS_WX) === FLAGS_WX) || + ((flags & FLAGS_WX_PLUS) === FLAGS_WX_PLUS) + )) + ); +} diff --git a/ext/node/polyfills/_fs/_fs_opendir.ts b/ext/node/polyfills/_fs/_fs_opendir.ts new file mode 100644 index 0000000000..5ee13f9519 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_opendir.ts @@ -0,0 +1,89 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import Dir from "internal:deno_node/polyfills/_fs/_fs_dir.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + getOptions, + getValidatedPath, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { denoErrorToNodeError } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateFunction, + validateInteger, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +/** These options aren't funcitonally used right now, as `Dir` doesn't yet support them. + * However, these values are still validated. + */ +type Options = { + encoding?: string; + bufferSize?: number; +}; +type Callback = (err?: Error | null, dir?: Dir) => void; + +function _validateFunction(callback: unknown): asserts callback is Callback { + validateFunction(callback, "callback"); +} + +/** @link https://nodejs.org/api/fs.html#fsopendirsyncpath-options */ +export function opendir( + path: string | Buffer | URL, + options: Options | Callback, + callback?: Callback, +) { + callback = typeof options === "function" ? options : callback; + _validateFunction(callback); + + path = getValidatedPath(path).toString(); + + let err, dir; + try { + const { bufferSize } = getOptions(options, { + encoding: "utf8", + bufferSize: 32, + }); + validateInteger(bufferSize, "options.bufferSize", 1, 4294967295); + + /** Throws if path is invalid */ + Deno.readDirSync(path); + + dir = new Dir(path); + } catch (error) { + err = denoErrorToNodeError(error as Error, { syscall: "opendir" }); + } + if (err) { + callback(err); + } else { + callback(null, dir); + } +} + +/** @link https://nodejs.org/api/fs.html#fspromisesopendirpath-options */ +export const opendirPromise = promisify(opendir) as ( + path: string | Buffer | URL, + options?: Options, +) => Promise; + +export function opendirSync( + path: string | Buffer | URL, + options?: Options, +): Dir { + path = getValidatedPath(path).toString(); + + const { bufferSize } = getOptions(options, { + encoding: "utf8", + bufferSize: 32, + }); + + validateInteger(bufferSize, "options.bufferSize", 1, 4294967295); + + try { + /** Throws if path is invalid */ + Deno.readDirSync(path); + + return new Dir(path); + } catch (err) { + throw denoErrorToNodeError(err as Error, { syscall: "opendir" }); + } +} diff --git a/ext/node/polyfills/_fs/_fs_read.ts b/ext/node/polyfills/_fs/_fs_read.ts new file mode 100644 index 0000000000..d744458298 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_read.ts @@ -0,0 +1,197 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateOffsetLengthRead, + validatePosition, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { + validateBuffer, + validateInteger, +} from "internal:deno_node/polyfills/internal/validators.mjs"; + +type readOptions = { + buffer: Buffer | Uint8Array; + offset: number; + length: number; + position: number | null; +}; + +type readSyncOptions = { + offset: number; + length: number; + position: number | null; +}; + +type BinaryCallback = ( + err: Error | null, + bytesRead: number | null, + data?: Buffer, +) => void; +type Callback = BinaryCallback; + +export function read(fd: number, callback: Callback): void; +export function read( + fd: number, + options: readOptions, + callback: Callback, +): void; +export function read( + fd: number, + buffer: Buffer | Uint8Array, + offset: number, + length: number, + position: number | null, + callback: Callback, +): void; +export function read( + fd: number, + optOrBufferOrCb?: Buffer | Uint8Array | readOptions | Callback, + offsetOrCallback?: number | Callback, + length?: number, + position?: number | null, + callback?: Callback, +) { + let cb: Callback | undefined; + let offset = 0, + buffer: Buffer | Uint8Array; + + if (typeof fd !== "number") { + throw new ERR_INVALID_ARG_TYPE("fd", "number", fd); + } + + if (length == null) { + length = 0; + } + + if (typeof offsetOrCallback === "function") { + cb = offsetOrCallback; + } else if (typeof optOrBufferOrCb === "function") { + cb = optOrBufferOrCb; + } else { + offset = offsetOrCallback as number; + validateInteger(offset, "offset", 0); + cb = callback; + } + + if ( + optOrBufferOrCb instanceof Buffer || optOrBufferOrCb instanceof Uint8Array + ) { + buffer = optOrBufferOrCb; + } else if (typeof optOrBufferOrCb === "function") { + offset = 0; + buffer = Buffer.alloc(16384); + length = buffer.byteLength; + position = null; + } else { + const opt = optOrBufferOrCb as readOptions; + if ( + !(opt.buffer instanceof Buffer) && !(opt.buffer instanceof Uint8Array) + ) { + if (opt.buffer === null) { + // @ts-ignore: Intentionally create TypeError for passing test-fs-read.js#L87 + length = opt.buffer.byteLength; + } + throw new ERR_INVALID_ARG_TYPE("buffer", [ + "Buffer", + "TypedArray", + "DataView", + ], optOrBufferOrCb); + } + offset = opt.offset ?? 0; + buffer = opt.buffer ?? Buffer.alloc(16384); + length = opt.length ?? buffer.byteLength; + position = opt.position ?? null; + } + + if (position == null) { + position = -1; + } + + validatePosition(position); + validateOffsetLengthRead(offset, length, buffer.byteLength); + + if (!cb) throw new ERR_INVALID_ARG_TYPE("cb", "Callback", cb); + + (async () => { + try { + let nread: number | null; + if (typeof position === "number" && position >= 0) { + const currentPosition = await Deno.seek(fd, 0, Deno.SeekMode.Current); + // We use sync calls below to avoid being affected by others during + // these calls. + Deno.seekSync(fd, position, Deno.SeekMode.Start); + nread = Deno.readSync(fd, buffer); + Deno.seekSync(fd, currentPosition, Deno.SeekMode.Start); + } else { + nread = await Deno.read(fd, buffer); + } + cb(null, nread ?? 0, Buffer.from(buffer.buffer, offset, length)); + } catch (error) { + cb(error as Error, null); + } + })(); +} + +export function readSync( + fd: number, + buffer: Buffer | Uint8Array, + offset: number, + length: number, + position: number | null, +): number; +export function readSync( + fd: number, + buffer: Buffer | Uint8Array, + opt: readSyncOptions, +): number; +export function readSync( + fd: number, + buffer: Buffer | Uint8Array, + offsetOrOpt?: number | readSyncOptions, + length?: number, + position?: number | null, +): number { + let offset = 0; + + if (typeof fd !== "number") { + throw new ERR_INVALID_ARG_TYPE("fd", "number", fd); + } + + validateBuffer(buffer); + + if (length == null) { + length = 0; + } + + if (typeof offsetOrOpt === "number") { + offset = offsetOrOpt; + validateInteger(offset, "offset", 0); + } else { + const opt = offsetOrOpt as readSyncOptions; + offset = opt.offset ?? 0; + length = opt.length ?? buffer.byteLength; + position = opt.position ?? null; + } + + if (position == null) { + position = -1; + } + + validatePosition(position); + validateOffsetLengthRead(offset, length, buffer.byteLength); + + let currentPosition = 0; + if (typeof position === "number" && position >= 0) { + currentPosition = Deno.seekSync(fd, 0, Deno.SeekMode.Current); + Deno.seekSync(fd, position, Deno.SeekMode.Start); + } + + const numberOfBytesRead = Deno.readSync(fd, buffer); + + if (typeof position === "number" && position >= 0) { + Deno.seekSync(fd, currentPosition, Deno.SeekMode.Start); + } + + return numberOfBytesRead ?? 0; +} diff --git a/ext/node/polyfills/_fs/_fs_readFile.ts b/ext/node/polyfills/_fs/_fs_readFile.ts new file mode 100644 index 0000000000..6c5e9fb8bc --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_readFile.ts @@ -0,0 +1,108 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + BinaryOptionsArgument, + FileOptionsArgument, + getEncoding, + TextOptionsArgument, +} from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { + BinaryEncodings, + Encodings, + TextEncodings, +} from "internal:deno_node/polyfills/_utils.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +function maybeDecode(data: Uint8Array, encoding: TextEncodings): string; +function maybeDecode( + data: Uint8Array, + encoding: BinaryEncodings | null, +): Buffer; +function maybeDecode( + data: Uint8Array, + encoding: Encodings | null, +): string | Buffer { + const buffer = Buffer.from(data.buffer, data.byteOffset, data.byteLength); + if (encoding && encoding !== "binary") return buffer.toString(encoding); + return buffer; +} + +type TextCallback = (err: Error | null, data?: string) => void; +type BinaryCallback = (err: Error | null, data?: Buffer) => void; +type GenericCallback = (err: Error | null, data?: string | Buffer) => void; +type Callback = TextCallback | BinaryCallback | GenericCallback; + +export function readFile( + path: string | URL, + options: TextOptionsArgument, + callback: TextCallback, +): void; +export function readFile( + path: string | URL, + options: BinaryOptionsArgument, + callback: BinaryCallback, +): void; +export function readFile( + path: string | URL, + options: null | undefined | FileOptionsArgument, + callback: BinaryCallback, +): void; +export function readFile(path: string | URL, callback: BinaryCallback): void; +export function readFile( + path: string | URL, + optOrCallback?: FileOptionsArgument | Callback | null | undefined, + callback?: Callback, +) { + path = path instanceof URL ? fromFileUrl(path) : path; + let cb: Callback | undefined; + if (typeof optOrCallback === "function") { + cb = optOrCallback; + } else { + cb = callback; + } + + const encoding = getEncoding(optOrCallback); + + const p = Deno.readFile(path); + + if (cb) { + p.then((data: Uint8Array) => { + if (encoding && encoding !== "binary") { + const text = maybeDecode(data, encoding); + return (cb as TextCallback)(null, text); + } + const buffer = maybeDecode(data, encoding); + (cb as BinaryCallback)(null, buffer); + }, (err) => cb && cb(err)); + } +} + +export const readFilePromise = promisify(readFile) as ( + & ((path: string | URL, opt: TextOptionsArgument) => Promise) + & ((path: string | URL, opt?: BinaryOptionsArgument) => Promise) + & ((path: string | URL, opt?: FileOptionsArgument) => Promise) +); + +export function readFileSync( + path: string | URL, + opt: TextOptionsArgument, +): string; +export function readFileSync( + path: string | URL, + opt?: BinaryOptionsArgument, +): Buffer; +export function readFileSync( + path: string | URL, + opt?: FileOptionsArgument, +): string | Buffer { + path = path instanceof URL ? fromFileUrl(path) : path; + const data = Deno.readFileSync(path); + const encoding = getEncoding(opt); + if (encoding && encoding !== "binary") { + const text = maybeDecode(data, encoding); + return text; + } + const buffer = maybeDecode(data, encoding); + return buffer; +} diff --git a/ext/node/polyfills/_fs/_fs_readdir.ts b/ext/node/polyfills/_fs/_fs_readdir.ts new file mode 100644 index 0000000000..f6cfae4f73 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_readdir.ts @@ -0,0 +1,142 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + TextDecoder, + TextEncoder, +} from "internal:deno_web/08_text_encoding.js"; +import { asyncIterableToCallback } from "internal:deno_node/polyfills/_fs/_fs_watch.ts"; +import Dirent from "internal:deno_node/polyfills/_fs/_fs_dirent.ts"; +import { denoErrorToNodeError } from "internal:deno_node/polyfills/internal/errors.ts"; +import { getValidatedPath } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +function toDirent(val: Deno.DirEntry): Dirent { + return new Dirent(val); +} + +type readDirOptions = { + encoding?: string; + withFileTypes?: boolean; +}; + +type readDirCallback = (err: Error | null, files: string[]) => void; + +type readDirCallbackDirent = (err: Error | null, files: Dirent[]) => void; + +type readDirBoth = ( + ...args: [Error] | [null, string[] | Dirent[] | Array] +) => void; + +export function readdir( + path: string | Buffer | URL, + options: { withFileTypes?: false; encoding?: string }, + callback: readDirCallback, +): void; +export function readdir( + path: string | Buffer | URL, + options: { withFileTypes: true; encoding?: string }, + callback: readDirCallbackDirent, +): void; +export function readdir(path: string | URL, callback: readDirCallback): void; +export function readdir( + path: string | Buffer | URL, + optionsOrCallback: readDirOptions | readDirCallback | readDirCallbackDirent, + maybeCallback?: readDirCallback | readDirCallbackDirent, +) { + const callback = + (typeof optionsOrCallback === "function" + ? optionsOrCallback + : maybeCallback) as readDirBoth | undefined; + const options = typeof optionsOrCallback === "object" + ? optionsOrCallback + : null; + const result: Array = []; + path = getValidatedPath(path); + + if (!callback) throw new Error("No callback function supplied"); + + if (options?.encoding) { + try { + new TextDecoder(options.encoding); + } catch { + throw new Error( + `TypeError [ERR_INVALID_OPT_VALUE_ENCODING]: The value "${options.encoding}" is invalid for option "encoding"`, + ); + } + } + + try { + asyncIterableToCallback(Deno.readDir(path.toString()), (val, done) => { + if (typeof path !== "string") return; + if (done) { + callback(null, result); + return; + } + if (options?.withFileTypes) { + result.push(toDirent(val)); + } else result.push(decode(val.name)); + }, (e) => { + callback(denoErrorToNodeError(e as Error, { syscall: "readdir" })); + }); + } catch (e) { + callback(denoErrorToNodeError(e as Error, { syscall: "readdir" })); + } +} + +function decode(str: string, encoding?: string): string { + if (!encoding) return str; + else { + const decoder = new TextDecoder(encoding); + const encoder = new TextEncoder(); + return decoder.decode(encoder.encode(str)); + } +} + +export const readdirPromise = promisify(readdir) as ( + & ((path: string | Buffer | URL, options: { + withFileTypes: true; + encoding?: string; + }) => Promise) + & ((path: string | Buffer | URL, options?: { + withFileTypes?: false; + encoding?: string; + }) => Promise) +); + +export function readdirSync( + path: string | Buffer | URL, + options: { withFileTypes: true; encoding?: string }, +): Dirent[]; +export function readdirSync( + path: string | Buffer | URL, + options?: { withFileTypes?: false; encoding?: string }, +): string[]; +export function readdirSync( + path: string | Buffer | URL, + options?: readDirOptions, +): Array { + const result = []; + path = getValidatedPath(path); + + if (options?.encoding) { + try { + new TextDecoder(options.encoding); + } catch { + throw new Error( + `TypeError [ERR_INVALID_OPT_VALUE_ENCODING]: The value "${options.encoding}" is invalid for option "encoding"`, + ); + } + } + + try { + for (const file of Deno.readDirSync(path.toString())) { + if (options?.withFileTypes) { + result.push(toDirent(file)); + } else result.push(decode(file.name)); + } + } catch (e) { + throw denoErrorToNodeError(e as Error, { syscall: "readdir" }); + } + return result; +} diff --git a/ext/node/polyfills/_fs/_fs_readlink.ts b/ext/node/polyfills/_fs/_fs_readlink.ts new file mode 100644 index 0000000000..07d1b6f6f1 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_readlink.ts @@ -0,0 +1,89 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { TextEncoder } from "internal:deno_web/08_text_encoding.js"; +import { + intoCallbackAPIWithIntercept, + MaybeEmpty, + notImplemented, +} from "internal:deno_node/polyfills/_utils.ts"; +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +type ReadlinkCallback = ( + err: MaybeEmpty, + linkString: MaybeEmpty, +) => void; + +interface ReadlinkOptions { + encoding?: string | null; +} + +function maybeEncode( + data: string, + encoding: string | null, +): string | Uint8Array { + if (encoding === "buffer") { + return new TextEncoder().encode(data); + } + return data; +} + +function getEncoding( + optOrCallback?: ReadlinkOptions | ReadlinkCallback, +): string | null { + if (!optOrCallback || typeof optOrCallback === "function") { + return null; + } else { + if (optOrCallback.encoding) { + if ( + optOrCallback.encoding === "utf8" || + optOrCallback.encoding === "utf-8" + ) { + return "utf8"; + } else if (optOrCallback.encoding === "buffer") { + return "buffer"; + } else { + notImplemented(`fs.readlink encoding=${optOrCallback.encoding}`); + } + } + return null; + } +} + +export function readlink( + path: string | URL, + optOrCallback: ReadlinkCallback | ReadlinkOptions, + callback?: ReadlinkCallback, +) { + path = path instanceof URL ? fromFileUrl(path) : path; + + let cb: ReadlinkCallback | undefined; + if (typeof optOrCallback === "function") { + cb = optOrCallback; + } else { + cb = callback; + } + + const encoding = getEncoding(optOrCallback); + + intoCallbackAPIWithIntercept( + Deno.readLink, + (data: string): string | Uint8Array => maybeEncode(data, encoding), + cb, + path, + ); +} + +export const readlinkPromise = promisify(readlink) as ( + path: string | URL, + opt?: ReadlinkOptions, +) => Promise; + +export function readlinkSync( + path: string | URL, + opt?: ReadlinkOptions, +): string | Uint8Array { + path = path instanceof URL ? fromFileUrl(path) : path; + + return maybeEncode(Deno.readLinkSync(path), getEncoding(opt)); +} diff --git a/ext/node/polyfills/_fs/_fs_realpath.ts b/ext/node/polyfills/_fs/_fs_realpath.ts new file mode 100644 index 0000000000..5892b2c0f4 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_realpath.ts @@ -0,0 +1,35 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +type Options = { encoding: string }; +type Callback = (err: Error | null, path?: string) => void; + +export function realpath( + path: string, + options?: Options | Callback, + callback?: Callback, +) { + if (typeof options === "function") { + callback = options; + } + if (!callback) { + throw new Error("No callback function supplied"); + } + Deno.realPath(path).then( + (path) => callback!(null, path), + (err) => callback!(err), + ); +} + +realpath.native = realpath; + +export const realpathPromise = promisify(realpath) as ( + path: string, + options?: Options, +) => Promise; + +export function realpathSync(path: string): string { + return Deno.realPathSync(path); +} + +realpathSync.native = realpathSync; diff --git a/ext/node/polyfills/_fs/_fs_rename.ts b/ext/node/polyfills/_fs/_fs_rename.ts new file mode 100644 index 0000000000..3f8b5bd7e3 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_rename.ts @@ -0,0 +1,28 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export function rename( + oldPath: string | URL, + newPath: string | URL, + callback: (err?: Error) => void, +) { + oldPath = oldPath instanceof URL ? fromFileUrl(oldPath) : oldPath; + newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; + + if (!callback) throw new Error("No callback function supplied"); + + Deno.rename(oldPath, newPath).then((_) => callback(), callback); +} + +export const renamePromise = promisify(rename) as ( + oldPath: string | URL, + newPath: string | URL, +) => Promise; + +export function renameSync(oldPath: string | URL, newPath: string | URL) { + oldPath = oldPath instanceof URL ? fromFileUrl(oldPath) : oldPath; + newPath = newPath instanceof URL ? fromFileUrl(newPath) : newPath; + + Deno.renameSync(oldPath, newPath); +} diff --git a/ext/node/polyfills/_fs/_fs_rm.ts b/ext/node/polyfills/_fs/_fs_rm.ts new file mode 100644 index 0000000000..80ba0b5f8d --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_rm.ts @@ -0,0 +1,81 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + validateRmOptions, + validateRmOptionsSync, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { denoErrorToNodeError } from "internal:deno_node/polyfills/internal/errors.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +type rmOptions = { + force?: boolean; + maxRetries?: number; + recursive?: boolean; + retryDelay?: number; +}; + +type rmCallback = (err: Error | null) => void; + +export function rm(path: string | URL, callback: rmCallback): void; +export function rm( + path: string | URL, + options: rmOptions, + callback: rmCallback, +): void; +export function rm( + path: string | URL, + optionsOrCallback: rmOptions | rmCallback, + maybeCallback?: rmCallback, +) { + const callback = typeof optionsOrCallback === "function" + ? optionsOrCallback + : maybeCallback; + const options = typeof optionsOrCallback === "object" + ? optionsOrCallback + : undefined; + + if (!callback) throw new Error("No callback function supplied"); + + validateRmOptions( + path, + options, + false, + (err: Error | null, options: rmOptions) => { + if (err) { + return callback(err); + } + Deno.remove(path, { recursive: options?.recursive }) + .then((_) => callback(null), (err: unknown) => { + if (options?.force && err instanceof Deno.errors.NotFound) { + callback(null); + } else { + callback( + err instanceof Error + ? denoErrorToNodeError(err, { syscall: "rm" }) + : err, + ); + } + }); + }, + ); +} + +export const rmPromise = promisify(rm) as ( + path: string | URL, + options?: rmOptions, +) => Promise; + +export function rmSync(path: string | URL, options?: rmOptions) { + options = validateRmOptionsSync(path, options, false); + try { + Deno.removeSync(path, { recursive: options?.recursive }); + } catch (err: unknown) { + if (options?.force && err instanceof Deno.errors.NotFound) { + return; + } + if (err instanceof Error) { + throw denoErrorToNodeError(err, { syscall: "stat" }); + } else { + throw err; + } + } +} diff --git a/ext/node/polyfills/_fs/_fs_rmdir.ts b/ext/node/polyfills/_fs/_fs_rmdir.ts new file mode 100644 index 0000000000..ba753a7432 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_rmdir.ts @@ -0,0 +1,108 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + emitRecursiveRmdirWarning, + getValidatedPath, + validateRmdirOptions, + validateRmOptions, + validateRmOptionsSync, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { toNamespacedPath } from "internal:deno_node/polyfills/path.ts"; +import { + denoErrorToNodeError, + ERR_FS_RMDIR_ENOTDIR, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +type rmdirOptions = { + maxRetries?: number; + recursive?: boolean; + retryDelay?: number; +}; + +type rmdirCallback = (err?: Error) => void; + +export function rmdir(path: string | URL, callback: rmdirCallback): void; +export function rmdir( + path: string | URL, + options: rmdirOptions, + callback: rmdirCallback, +): void; +export function rmdir( + path: string | URL, + optionsOrCallback: rmdirOptions | rmdirCallback, + maybeCallback?: rmdirCallback, +) { + path = toNamespacedPath(getValidatedPath(path) as string); + + const callback = typeof optionsOrCallback === "function" + ? optionsOrCallback + : maybeCallback; + const options = typeof optionsOrCallback === "object" + ? optionsOrCallback + : undefined; + + if (!callback) throw new Error("No callback function supplied"); + + if (options?.recursive) { + emitRecursiveRmdirWarning(); + validateRmOptions( + path, + { ...options, force: false }, + true, + (err: Error | null | false, options: rmdirOptions) => { + if (err === false) { + return callback(new ERR_FS_RMDIR_ENOTDIR(path.toString())); + } + if (err) { + return callback(err); + } + + Deno.remove(path, { recursive: options?.recursive }) + .then((_) => callback(), callback); + }, + ); + } else { + validateRmdirOptions(options); + Deno.remove(path, { recursive: options?.recursive }) + .then((_) => callback(), (err: unknown) => { + callback( + err instanceof Error + ? denoErrorToNodeError(err, { syscall: "rmdir" }) + : err, + ); + }); + } +} + +export const rmdirPromise = promisify(rmdir) as ( + path: string | Buffer | URL, + options?: rmdirOptions, +) => Promise; + +export function rmdirSync(path: string | Buffer | URL, options?: rmdirOptions) { + path = getValidatedPath(path); + if (options?.recursive) { + emitRecursiveRmdirWarning(); + const optionsOrFalse: rmdirOptions | false = validateRmOptionsSync(path, { + ...options, + force: false, + }, true); + if (optionsOrFalse === false) { + throw new ERR_FS_RMDIR_ENOTDIR(path.toString()); + } + options = optionsOrFalse; + } else { + validateRmdirOptions(options); + } + + try { + Deno.removeSync(toNamespacedPath(path as string), { + recursive: options?.recursive, + }); + } catch (err: unknown) { + throw (err instanceof Error + ? denoErrorToNodeError(err, { syscall: "rmdir" }) + : err); + } +} diff --git a/ext/node/polyfills/_fs/_fs_stat.ts b/ext/node/polyfills/_fs/_fs_stat.ts new file mode 100644 index 0000000000..3a006084df --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_stat.ts @@ -0,0 +1,314 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { denoErrorToNodeError } from "internal:deno_node/polyfills/internal/errors.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export type statOptions = { + bigint: boolean; + throwIfNoEntry?: boolean; +}; + +export type Stats = { + /** ID of the device containing the file. + * + * _Linux/Mac OS only._ */ + dev: number | null; + /** Inode number. + * + * _Linux/Mac OS only._ */ + ino: number | null; + /** **UNSTABLE**: Match behavior with Go on Windows for `mode`. + * + * The underlying raw `st_mode` bits that contain the standard Unix + * permissions for this file/directory. */ + mode: number | null; + /** Number of hard links pointing to this file. + * + * _Linux/Mac OS only._ */ + nlink: number | null; + /** User ID of the owner of this file. + * + * _Linux/Mac OS only._ */ + uid: number | null; + /** Group ID of the owner of this file. + * + * _Linux/Mac OS only._ */ + gid: number | null; + /** Device ID of this file. + * + * _Linux/Mac OS only._ */ + rdev: number | null; + /** The size of the file, in bytes. */ + size: number; + /** Blocksize for filesystem I/O. + * + * _Linux/Mac OS only._ */ + blksize: number | null; + /** Number of blocks allocated to the file, in 512-byte units. + * + * _Linux/Mac OS only._ */ + blocks: number | null; + /** The last modification time of the file. This corresponds to the `mtime` + * field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This + * may not be available on all platforms. */ + mtime: Date | null; + /** The last access time of the file. This corresponds to the `atime` + * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not + * be available on all platforms. */ + atime: Date | null; + /** The creation time of the file. This corresponds to the `birthtime` + * field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may + * not be available on all platforms. */ + birthtime: Date | null; + /** change time */ + ctime: Date | null; + /** atime in milliseconds */ + atimeMs: number | null; + /** atime in milliseconds */ + mtimeMs: number | null; + /** atime in milliseconds */ + ctimeMs: number | null; + /** atime in milliseconds */ + birthtimeMs: number | null; + isBlockDevice: () => boolean; + isCharacterDevice: () => boolean; + isDirectory: () => boolean; + isFIFO: () => boolean; + isFile: () => boolean; + isSocket: () => boolean; + isSymbolicLink: () => boolean; +}; + +export type BigIntStats = { + /** ID of the device containing the file. + * + * _Linux/Mac OS only._ */ + dev: bigint | null; + /** Inode number. + * + * _Linux/Mac OS only._ */ + ino: bigint | null; + /** **UNSTABLE**: Match behavior with Go on Windows for `mode`. + * + * The underlying raw `st_mode` bits that contain the standard Unix + * permissions for this file/directory. */ + mode: bigint | null; + /** Number of hard links pointing to this file. + * + * _Linux/Mac OS only._ */ + nlink: bigint | null; + /** User ID of the owner of this file. + * + * _Linux/Mac OS only._ */ + uid: bigint | null; + /** Group ID of the owner of this file. + * + * _Linux/Mac OS only._ */ + gid: bigint | null; + /** Device ID of this file. + * + * _Linux/Mac OS only._ */ + rdev: bigint | null; + /** The size of the file, in bytes. */ + size: bigint; + /** Blocksize for filesystem I/O. + * + * _Linux/Mac OS only._ */ + blksize: bigint | null; + /** Number of blocks allocated to the file, in 512-byte units. + * + * _Linux/Mac OS only._ */ + blocks: bigint | null; + /** The last modification time of the file. This corresponds to the `mtime` + * field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This + * may not be available on all platforms. */ + mtime: Date | null; + /** The last access time of the file. This corresponds to the `atime` + * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not + * be available on all platforms. */ + atime: Date | null; + /** The creation time of the file. This corresponds to the `birthtime` + * field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may + * not be available on all platforms. */ + birthtime: Date | null; + /** change time */ + ctime: Date | null; + /** atime in milliseconds */ + atimeMs: bigint | null; + /** atime in milliseconds */ + mtimeMs: bigint | null; + /** atime in milliseconds */ + ctimeMs: bigint | null; + /** atime in nanoseconds */ + birthtimeMs: bigint | null; + /** atime in nanoseconds */ + atimeNs: bigint | null; + /** atime in nanoseconds */ + mtimeNs: bigint | null; + /** atime in nanoseconds */ + ctimeNs: bigint | null; + /** atime in nanoseconds */ + birthtimeNs: bigint | null; + isBlockDevice: () => boolean; + isCharacterDevice: () => boolean; + isDirectory: () => boolean; + isFIFO: () => boolean; + isFile: () => boolean; + isSocket: () => boolean; + isSymbolicLink: () => boolean; +}; + +export function convertFileInfoToStats(origin: Deno.FileInfo): Stats { + return { + dev: origin.dev, + ino: origin.ino, + mode: origin.mode, + nlink: origin.nlink, + uid: origin.uid, + gid: origin.gid, + rdev: origin.rdev, + size: origin.size, + blksize: origin.blksize, + blocks: origin.blocks, + mtime: origin.mtime, + atime: origin.atime, + birthtime: origin.birthtime, + mtimeMs: origin.mtime?.getTime() || null, + atimeMs: origin.atime?.getTime() || null, + birthtimeMs: origin.birthtime?.getTime() || null, + isFile: () => origin.isFile, + isDirectory: () => origin.isDirectory, + isSymbolicLink: () => origin.isSymlink, + // not sure about those + isBlockDevice: () => false, + isFIFO: () => false, + isCharacterDevice: () => false, + isSocket: () => false, + ctime: origin.mtime, + ctimeMs: origin.mtime?.getTime() || null, + }; +} + +function toBigInt(number?: number | null) { + if (number === null || number === undefined) return null; + return BigInt(number); +} + +export function convertFileInfoToBigIntStats( + origin: Deno.FileInfo, +): BigIntStats { + return { + dev: toBigInt(origin.dev), + ino: toBigInt(origin.ino), + mode: toBigInt(origin.mode), + nlink: toBigInt(origin.nlink), + uid: toBigInt(origin.uid), + gid: toBigInt(origin.gid), + rdev: toBigInt(origin.rdev), + size: toBigInt(origin.size) || 0n, + blksize: toBigInt(origin.blksize), + blocks: toBigInt(origin.blocks), + mtime: origin.mtime, + atime: origin.atime, + birthtime: origin.birthtime, + mtimeMs: origin.mtime ? BigInt(origin.mtime.getTime()) : null, + atimeMs: origin.atime ? BigInt(origin.atime.getTime()) : null, + birthtimeMs: origin.birthtime ? BigInt(origin.birthtime.getTime()) : null, + mtimeNs: origin.mtime ? BigInt(origin.mtime.getTime()) * 1000000n : null, + atimeNs: origin.atime ? BigInt(origin.atime.getTime()) * 1000000n : null, + birthtimeNs: origin.birthtime + ? BigInt(origin.birthtime.getTime()) * 1000000n + : null, + isFile: () => origin.isFile, + isDirectory: () => origin.isDirectory, + isSymbolicLink: () => origin.isSymlink, + // not sure about those + isBlockDevice: () => false, + isFIFO: () => false, + isCharacterDevice: () => false, + isSocket: () => false, + ctime: origin.mtime, + ctimeMs: origin.mtime ? BigInt(origin.mtime.getTime()) : null, + ctimeNs: origin.mtime ? BigInt(origin.mtime.getTime()) * 1000000n : null, + }; +} + +// shortcut for Convert File Info to Stats or BigIntStats +export function CFISBIS(fileInfo: Deno.FileInfo, bigInt: boolean) { + if (bigInt) return convertFileInfoToBigIntStats(fileInfo); + return convertFileInfoToStats(fileInfo); +} + +export type statCallbackBigInt = (err: Error | null, stat: BigIntStats) => void; + +export type statCallback = (err: Error | null, stat: Stats) => void; + +export function stat(path: string | URL, callback: statCallback): void; +export function stat( + path: string | URL, + options: { bigint: false }, + callback: statCallback, +): void; +export function stat( + path: string | URL, + options: { bigint: true }, + callback: statCallbackBigInt, +): void; +export function stat( + path: string | URL, + optionsOrCallback: statCallback | statCallbackBigInt | statOptions, + maybeCallback?: statCallback | statCallbackBigInt, +) { + const callback = + (typeof optionsOrCallback === "function" + ? optionsOrCallback + : maybeCallback) as ( + ...args: [Error] | [null, BigIntStats | Stats] + ) => void; + const options = typeof optionsOrCallback === "object" + ? optionsOrCallback + : { bigint: false }; + + if (!callback) throw new Error("No callback function supplied"); + + Deno.stat(path).then( + (stat) => callback(null, CFISBIS(stat, options.bigint)), + (err) => callback(denoErrorToNodeError(err, { syscall: "stat" })), + ); +} + +export const statPromise = promisify(stat) as ( + & ((path: string | URL) => Promise) + & ((path: string | URL, options: { bigint: false }) => Promise) + & ((path: string | URL, options: { bigint: true }) => Promise) +); + +export function statSync(path: string | URL): Stats; +export function statSync( + path: string | URL, + options: { bigint: false; throwIfNoEntry?: boolean }, +): Stats; +export function statSync( + path: string | URL, + options: { bigint: true; throwIfNoEntry?: boolean }, +): BigIntStats; +export function statSync( + path: string | URL, + options: statOptions = { bigint: false, throwIfNoEntry: true }, +): Stats | BigIntStats | undefined { + try { + const origin = Deno.statSync(path); + return CFISBIS(origin, options.bigint); + } catch (err) { + if ( + options?.throwIfNoEntry === false && + err instanceof Deno.errors.NotFound + ) { + return; + } + if (err instanceof Error) { + throw denoErrorToNodeError(err, { syscall: "stat" }); + } else { + throw err; + } + } +} diff --git a/ext/node/polyfills/_fs/_fs_symlink.ts b/ext/node/polyfills/_fs/_fs_symlink.ts new file mode 100644 index 0000000000..c8652885fb --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_symlink.ts @@ -0,0 +1,46 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +type SymlinkType = "file" | "dir"; + +export function symlink( + target: string | URL, + path: string | URL, + typeOrCallback: SymlinkType | CallbackWithError, + maybeCallback?: CallbackWithError, +) { + target = target instanceof URL ? fromFileUrl(target) : target; + path = path instanceof URL ? fromFileUrl(path) : path; + + const type: SymlinkType = typeof typeOrCallback === "string" + ? typeOrCallback + : "file"; + + const callback: CallbackWithError = typeof typeOrCallback === "function" + ? typeOrCallback + : (maybeCallback as CallbackWithError); + + if (!callback) throw new Error("No callback function supplied"); + + Deno.symlink(target, path, { type }).then(() => callback(null), callback); +} + +export const symlinkPromise = promisify(symlink) as ( + target: string | URL, + path: string | URL, + type?: SymlinkType, +) => Promise; + +export function symlinkSync( + target: string | URL, + path: string | URL, + type?: SymlinkType, +) { + target = target instanceof URL ? fromFileUrl(target) : target; + path = path instanceof URL ? fromFileUrl(path) : path; + type = type || "file"; + + Deno.symlinkSync(target, path, { type }); +} diff --git a/ext/node/polyfills/_fs/_fs_truncate.ts b/ext/node/polyfills/_fs/_fs_truncate.ts new file mode 100644 index 0000000000..105555abcf --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_truncate.ts @@ -0,0 +1,33 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export function truncate( + path: string | URL, + lenOrCallback: number | CallbackWithError, + maybeCallback?: CallbackWithError, +) { + path = path instanceof URL ? fromFileUrl(path) : path; + const len: number | undefined = typeof lenOrCallback === "number" + ? lenOrCallback + : undefined; + const callback: CallbackWithError = typeof lenOrCallback === "function" + ? lenOrCallback + : maybeCallback as CallbackWithError; + + if (!callback) throw new Error("No callback function supplied"); + + Deno.truncate(path, len).then(() => callback(null), callback); +} + +export const truncatePromise = promisify(truncate) as ( + path: string | URL, + len?: number, +) => Promise; + +export function truncateSync(path: string | URL, len?: number) { + path = path instanceof URL ? fromFileUrl(path) : path; + + Deno.truncateSync(path, len); +} diff --git a/ext/node/polyfills/_fs/_fs_unlink.ts b/ext/node/polyfills/_fs/_fs_unlink.ts new file mode 100644 index 0000000000..ed43bb1b31 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_unlink.ts @@ -0,0 +1,15 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +export function unlink(path: string | URL, callback: (err?: Error) => void) { + if (!callback) throw new Error("No callback function supplied"); + Deno.remove(path).then((_) => callback(), callback); +} + +export const unlinkPromise = promisify(unlink) as ( + path: string | URL, +) => Promise; + +export function unlinkSync(path: string | URL) { + Deno.removeSync(path); +} diff --git a/ext/node/polyfills/_fs/_fs_utimes.ts b/ext/node/polyfills/_fs/_fs_utimes.ts new file mode 100644 index 0000000000..7423a1060e --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_utimes.ts @@ -0,0 +1,61 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import type { CallbackWithError } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +function getValidTime( + time: number | string | Date, + name: string, +): number | Date { + if (typeof time === "string") { + time = Number(time); + } + + if ( + typeof time === "number" && + (Number.isNaN(time) || !Number.isFinite(time)) + ) { + throw new Deno.errors.InvalidData( + `invalid ${name}, must not be infinity or NaN`, + ); + } + + return time; +} + +export function utimes( + path: string | URL, + atime: number | string | Date, + mtime: number | string | Date, + callback: CallbackWithError, +) { + path = path instanceof URL ? fromFileUrl(path) : path; + + if (!callback) { + throw new Deno.errors.InvalidData("No callback function supplied"); + } + + atime = getValidTime(atime, "atime"); + mtime = getValidTime(mtime, "mtime"); + + Deno.utime(path, atime, mtime).then(() => callback(null), callback); +} + +export const utimesPromise = promisify(utimes) as ( + path: string | URL, + atime: number | string | Date, + mtime: number | string | Date, +) => Promise; + +export function utimesSync( + path: string | URL, + atime: number | string | Date, + mtime: number | string | Date, +) { + path = path instanceof URL ? fromFileUrl(path) : path; + atime = getValidTime(atime, "atime"); + mtime = getValidTime(mtime, "mtime"); + + Deno.utimeSync(path, atime, mtime); +} diff --git a/ext/node/polyfills/_fs/_fs_watch.ts b/ext/node/polyfills/_fs/_fs_watch.ts new file mode 100644 index 0000000000..79f226126a --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_watch.ts @@ -0,0 +1,346 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { basename } from "internal:deno_node/polyfills/path.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { promisify } from "internal:deno_node/polyfills/util.ts"; +import { getValidatedPath } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { stat, Stats } from "internal:deno_node/polyfills/_fs/_fs_stat.ts"; +import { Stats as StatsClass } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { delay } from "internal:deno_node/polyfills/_util/async.ts"; + +const statPromisified = promisify(stat); +const statAsync = async (filename: string): Promise => { + try { + return await statPromisified(filename); + } catch { + return emptyStats; + } +}; +const emptyStats = new StatsClass( + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + Date.UTC(1970, 0, 1, 0, 0, 0), + Date.UTC(1970, 0, 1, 0, 0, 0), + Date.UTC(1970, 0, 1, 0, 0, 0), + Date.UTC(1970, 0, 1, 0, 0, 0), +) as unknown as Stats; + +export function asyncIterableIteratorToCallback( + iterator: AsyncIterableIterator, + callback: (val: T, done?: boolean) => void, +) { + function next() { + iterator.next().then((obj) => { + if (obj.done) { + callback(obj.value, true); + return; + } + callback(obj.value); + next(); + }); + } + next(); +} + +export function asyncIterableToCallback( + iter: AsyncIterable, + callback: (val: T, done?: boolean) => void, + errCallback: (e: unknown) => void, +) { + const iterator = iter[Symbol.asyncIterator](); + function next() { + iterator.next().then((obj) => { + if (obj.done) { + callback(obj.value, true); + return; + } + callback(obj.value); + next(); + }, errCallback); + } + next(); +} + +type watchOptions = { + persistent?: boolean; + recursive?: boolean; + encoding?: string; +}; + +type watchListener = (eventType: string, filename: string) => void; + +export function watch( + filename: string | URL, + options: watchOptions, + listener: watchListener, +): FSWatcher; +export function watch( + filename: string | URL, + listener: watchListener, +): FSWatcher; +export function watch( + filename: string | URL, + options: watchOptions, +): FSWatcher; +export function watch(filename: string | URL): FSWatcher; +export function watch( + filename: string | URL, + optionsOrListener?: watchOptions | watchListener, + optionsOrListener2?: watchOptions | watchListener, +) { + const listener = typeof optionsOrListener === "function" + ? optionsOrListener + : typeof optionsOrListener2 === "function" + ? optionsOrListener2 + : undefined; + const options = typeof optionsOrListener === "object" + ? optionsOrListener + : typeof optionsOrListener2 === "object" + ? optionsOrListener2 + : undefined; + + const watchPath = getValidatedPath(filename).toString(); + + let iterator: Deno.FsWatcher; + // Start the actual watcher a few msec later to avoid race condition + // error in test case in compat test case + // (parallel/test-fs-watch.js, parallel/test-fs-watchfile.js) + const timer = setTimeout(() => { + iterator = Deno.watchFs(watchPath, { + recursive: options?.recursive || false, + }); + + asyncIterableToCallback(iterator, (val, done) => { + if (done) return; + fsWatcher.emit( + "change", + convertDenoFsEventToNodeFsEvent(val.kind), + basename(val.paths[0]), + ); + }, (e) => { + fsWatcher.emit("error", e); + }); + }, 5); + + const fsWatcher = new FSWatcher(() => { + clearTimeout(timer); + try { + iterator?.close(); + } catch (e) { + if (e instanceof Deno.errors.BadResource) { + // already closed + return; + } + throw e; + } + }); + + if (listener) { + fsWatcher.on("change", listener.bind({ _handle: fsWatcher })); + } + + return fsWatcher; +} + +export const watchPromise = promisify(watch) as ( + & (( + filename: string | URL, + options: watchOptions, + listener: watchListener, + ) => Promise) + & (( + filename: string | URL, + listener: watchListener, + ) => Promise) + & (( + filename: string | URL, + options: watchOptions, + ) => Promise) + & ((filename: string | URL) => Promise) +); + +type WatchFileListener = (curr: Stats, prev: Stats) => void; +type WatchFileOptions = { + bigint?: boolean; + persistent?: boolean; + interval?: number; +}; + +export function watchFile( + filename: string | Buffer | URL, + listener: WatchFileListener, +): StatWatcher; +export function watchFile( + filename: string | Buffer | URL, + options: WatchFileOptions, + listener: WatchFileListener, +): StatWatcher; +export function watchFile( + filename: string | Buffer | URL, + listenerOrOptions: WatchFileListener | WatchFileOptions, + listener?: WatchFileListener, +): StatWatcher { + const watchPath = getValidatedPath(filename).toString(); + const handler = typeof listenerOrOptions === "function" + ? listenerOrOptions + : listener!; + validateFunction(handler, "listener"); + const { + bigint = false, + persistent = true, + interval = 5007, + } = typeof listenerOrOptions === "object" ? listenerOrOptions : {}; + + let stat = statWatchers.get(watchPath); + if (stat === undefined) { + stat = new StatWatcher(bigint); + stat[kFSStatWatcherStart](watchPath, persistent, interval); + statWatchers.set(watchPath, stat); + } + + stat.addListener("change", listener!); + return stat; +} + +export function unwatchFile( + filename: string | Buffer | URL, + listener?: WatchFileListener, +) { + const watchPath = getValidatedPath(filename).toString(); + const stat = statWatchers.get(watchPath); + + if (!stat) { + return; + } + + if (typeof listener === "function") { + const beforeListenerCount = stat.listenerCount("change"); + stat.removeListener("change", listener); + if (stat.listenerCount("change") < beforeListenerCount) { + stat[kFSStatWatcherAddOrCleanRef]("clean"); + } + } else { + stat.removeAllListeners("change"); + stat[kFSStatWatcherAddOrCleanRef]("cleanAll"); + } + + if (stat.listenerCount("change") === 0) { + stat.stop(); + statWatchers.delete(watchPath); + } +} + +const statWatchers = new Map(); + +const kFSStatWatcherStart = Symbol("kFSStatWatcherStart"); +const kFSStatWatcherAddOrCleanRef = Symbol("kFSStatWatcherAddOrCleanRef"); + +class StatWatcher extends EventEmitter { + #bigint: boolean; + #refCount = 0; + #abortController = new AbortController(); + constructor(bigint: boolean) { + super(); + this.#bigint = bigint; + } + [kFSStatWatcherStart]( + filename: string, + persistent: boolean, + interval: number, + ) { + if (persistent) { + this.#refCount++; + } + + (async () => { + let prev = await statAsync(filename); + + if (prev === emptyStats) { + this.emit("change", prev, prev); + } + + try { + while (true) { + await delay(interval, { signal: this.#abortController.signal }); + const curr = await statAsync(filename); + if (curr?.mtime !== prev?.mtime) { + this.emit("change", curr, prev); + prev = curr; + } + } + } catch (e) { + if (e instanceof DOMException && e.name === "AbortError") { + return; + } + this.emit("error", e); + } + })(); + } + [kFSStatWatcherAddOrCleanRef](addOrClean: "add" | "clean" | "cleanAll") { + if (addOrClean === "add") { + this.#refCount++; + } else if (addOrClean === "clean") { + this.#refCount--; + } else { + this.#refCount = 0; + } + } + stop() { + if (this.#abortController.signal.aborted) { + return; + } + this.#abortController.abort(); + this.emit("stop"); + } + ref() { + notImplemented("FSWatcher.ref() is not implemented"); + } + unref() { + notImplemented("FSWatcher.unref() is not implemented"); + } +} + +class FSWatcher extends EventEmitter { + #closer: () => void; + #closed = false; + constructor(closer: () => void) { + super(); + this.#closer = closer; + } + close() { + if (this.#closed) { + return; + } + this.#closed = true; + this.emit("close"); + this.#closer(); + } + ref() { + notImplemented("FSWatcher.ref() is not implemented"); + } + unref() { + notImplemented("FSWatcher.unref() is not implemented"); + } +} + +type NodeFsEventType = "rename" | "change"; + +function convertDenoFsEventToNodeFsEvent( + kind: Deno.FsEvent["kind"], +): NodeFsEventType { + if (kind === "create" || kind === "remove") { + return "rename"; + } else { + return "change"; + } +} diff --git a/ext/node/polyfills/_fs/_fs_write.d.ts b/ext/node/polyfills/_fs/_fs_write.d.ts new file mode 100644 index 0000000000..eb6dbcc95d --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_write.d.ts @@ -0,0 +1,207 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts + +import { + BufferEncoding, + ErrnoException, +} from "internal:deno_node/polyfills/_global.d.ts"; + +/** + * Write `buffer` to the file specified by `fd`. If `buffer` is a normal object, it + * must have an own `toString` function property. + * + * `offset` determines the part of the buffer to be written, and `length` is + * an integer specifying the number of bytes to write. + * + * `position` refers to the offset from the beginning of the file where this data + * should be written. If `typeof position !== 'number'`, the data will be written + * at the current position. See [`pwrite(2)`](http://man7.org/linux/man-pages/man2/pwrite.2.html). + * + * The callback will be given three arguments `(err, bytesWritten, buffer)` where`bytesWritten` specifies how many _bytes_ were written from `buffer`. + * + * If this method is invoked as its `util.promisify()` ed version, it returns + * a promise for an `Object` with `bytesWritten` and `buffer` properties. + * + * It is unsafe to use `fs.write()` multiple times on the same file without waiting + * for the callback. For this scenario, {@link createWriteStream} is + * recommended. + * + * On Linux, positional writes don't work when the file is opened in append mode. + * The kernel ignores the position argument and always appends the data to + * the end of the file. + * @since v0.0.2 + */ +export function write( + fd: number, + buffer: TBuffer, + offset: number | undefined | null, + length: number | undefined | null, + position: number | undefined | null, + callback: ( + err: ErrnoException | null, + written: number, + buffer: TBuffer, + ) => void, +): void; +/** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + * @param length The number of bytes to write. If not supplied, defaults to `buffer.length - offset`. + */ +export function write( + fd: number, + buffer: TBuffer, + offset: number | undefined | null, + length: number | undefined | null, + callback: ( + err: ErrnoException | null, + written: number, + buffer: TBuffer, + ) => void, +): void; +/** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + */ +export function write( + fd: number, + buffer: TBuffer, + offset: number | undefined | null, + callback: ( + err: ErrnoException | null, + written: number, + buffer: TBuffer, + ) => void, +): void; +/** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + */ +export function write( + fd: number, + buffer: TBuffer, + callback: ( + err: ErrnoException | null, + written: number, + buffer: TBuffer, + ) => void, +): void; +/** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + * @param encoding The expected string encoding. + */ +export function write( + fd: number, + string: string, + position: number | undefined | null, + encoding: BufferEncoding | undefined | null, + callback: (err: ErrnoException | null, written: number, str: string) => void, +): void; +/** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + */ +export function write( + fd: number, + string: string, + position: number | undefined | null, + callback: (err: ErrnoException | null, written: number, str: string) => void, +): void; +/** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. + */ +export function write( + fd: number, + string: string, + callback: (err: ErrnoException | null, written: number, str: string) => void, +): void; +export namespace write { + /** + * Asynchronously writes `buffer` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param offset The part of the buffer to be written. If not supplied, defaults to `0`. + * @param length The number of bytes to write. If not supplied, defaults to `buffer.length - offset`. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + */ + function __promisify__( + fd: number, + buffer?: TBuffer, + offset?: number, + length?: number, + position?: number | null, + ): Promise<{ + bytesWritten: number; + buffer: TBuffer; + }>; + /** + * Asynchronously writes `string` to the file referenced by the supplied file descriptor. + * @param fd A file descriptor. + * @param string A string to write. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + * @param encoding The expected string encoding. + */ + function __promisify__( + fd: number, + string: string, + position?: number | null, + encoding?: BufferEncoding | null, + ): Promise<{ + bytesWritten: number; + buffer: string; + }>; +} +/** + * If `buffer` is a plain object, it must have an own (not inherited) `toString`function property. + * + * For detailed information, see the documentation of the asynchronous version of + * this API: {@link write}. + * @since v0.1.21 + * @return The number of bytes written. + */ +export function writeSync( + fd: number, + buffer: ArrayBufferView, + offset?: number | null, + length?: number | null, + position?: number | null, +): number; +/** + * Synchronously writes `string` to the file referenced by the supplied file descriptor, returning the number of bytes written. + * @param fd A file descriptor. + * @param string A string to write. + * @param position The offset from the beginning of the file where this data should be written. If not supplied, defaults to the current position. + * @param encoding The expected string encoding. + */ +export function writeSync( + fd: number, + string: string, + position?: number | null, + encoding?: BufferEncoding | null, +): number; +export type ReadPosition = number | bigint; +/** + * Read data from the file specified by `fd`. + * + * The callback is given the three arguments, `(err, bytesRead, buffer)`. + * + * If the file is not modified concurrently, the end-of-file is reached when the + * number of bytes read is zero. + * + * If this method is invoked as its `util.promisify()` ed version, it returns + * a promise for an `Object` with `bytesRead` and `buffer` properties. + * @since v0.0.2 + * @param buffer The buffer that the data will be written to. + * @param offset The position in `buffer` to write the data to. + * @param length The number of bytes to read. + * @param position Specifies where to begin reading from in the file. If `position` is `null` or `-1 `, data will be read from the current file position, and the file position will be updated. If + * `position` is an integer, the file position will be unchanged. + */ diff --git a/ext/node/polyfills/_fs/_fs_write.mjs b/ext/node/polyfills/_fs/_fs_write.mjs new file mode 100644 index 0000000000..d44a729216 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_write.mjs @@ -0,0 +1,132 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { validateEncoding, validateInteger } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + getValidatedFd, + showStringCoercionDeprecation, + validateOffsetLengthWrite, + validateStringAfterArrayBufferView, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { isArrayBufferView } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { maybeCallback } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; + +export function writeSync(fd, buffer, offset, length, position) { + fd = getValidatedFd(fd); + + const innerWriteSync = (fd, buffer, offset, length, position) => { + if (buffer instanceof DataView) { + buffer = new Uint8Array(buffer.buffer); + } + if (typeof position === "number") { + Deno.seekSync(fd, position, Deno.SeekMode.Start); + } + let currentOffset = offset; + const end = offset + length; + while (currentOffset - offset < length) { + currentOffset += Deno.writeSync(fd, buffer.subarray(currentOffset, end)); + } + return currentOffset - offset; + }; + + if (isArrayBufferView(buffer)) { + if (position === undefined) { + position = null; + } + if (offset == null) { + offset = 0; + } else { + validateInteger(offset, "offset", 0); + } + if (typeof length !== "number") { + length = buffer.byteLength - offset; + } + validateOffsetLengthWrite(offset, length, buffer.byteLength); + return innerWriteSync(fd, buffer, offset, length, position); + } + validateStringAfterArrayBufferView(buffer, "buffer"); + validateEncoding(buffer, length); + if (offset === undefined) { + offset = null; + } + buffer = Buffer.from(buffer, length); + return innerWriteSync(fd, buffer, 0, buffer.length, position); +} + +/** Writes the buffer to the file of the given descriptor. + * https://nodejs.org/api/fs.html#fswritefd-buffer-offset-length-position-callback + * https://github.com/nodejs/node/blob/42ad4137aadda69c51e1df48eee9bc2e5cebca5c/lib/fs.js#L797 + */ +export function write(fd, buffer, offset, length, position, callback) { + fd = getValidatedFd(fd); + + const innerWrite = async (fd, buffer, offset, length, position) => { + if (buffer instanceof DataView) { + buffer = new Uint8Array(buffer.buffer); + } + if (typeof position === "number") { + await Deno.seek(fd, position, Deno.SeekMode.Start); + } + let currentOffset = offset; + const end = offset + length; + while (currentOffset - offset < length) { + currentOffset += await Deno.write( + fd, + buffer.subarray(currentOffset, end), + ); + } + return currentOffset - offset; + }; + + if (isArrayBufferView(buffer)) { + callback = maybeCallback(callback || position || length || offset); + if (offset == null || typeof offset === "function") { + offset = 0; + } else { + validateInteger(offset, "offset", 0); + } + if (typeof length !== "number") { + length = buffer.byteLength - offset; + } + if (typeof position !== "number") { + position = null; + } + validateOffsetLengthWrite(offset, length, buffer.byteLength); + innerWrite(fd, buffer, offset, length, position).then( + (nwritten) => { + callback(null, nwritten, buffer); + }, + (err) => callback(err), + ); + return; + } + + // Here the call signature is + // `fs.write(fd, string[, position[, encoding]], callback)` + + validateStringAfterArrayBufferView(buffer, "buffer"); + if (typeof buffer !== "string") { + showStringCoercionDeprecation(); + } + + if (typeof position !== "function") { + if (typeof offset === "function") { + position = offset; + offset = null; + } else { + position = length; + } + length = "utf-8"; + } + + const str = String(buffer); + validateEncoding(str, length); + callback = maybeCallback(position); + buffer = Buffer.from(str, length); + innerWrite(fd, buffer, 0, buffer.length, offset, callback).then( + (nwritten) => { + callback(null, nwritten, buffer); + }, + (err) => callback(err), + ); +} diff --git a/ext/node/polyfills/_fs/_fs_writeFile.ts b/ext/node/polyfills/_fs/_fs_writeFile.ts new file mode 100644 index 0000000000..3cad5f947c --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_writeFile.ts @@ -0,0 +1,193 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Encodings } from "internal:deno_node/polyfills/_utils.ts"; +import { fromFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + CallbackWithError, + checkEncoding, + getEncoding, + getOpenOptions, + isFileOptions, + WriteFileOptions, +} from "internal:deno_node/polyfills/_fs/_fs_common.ts"; +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; +import { + AbortError, + denoErrorToNodeError, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + showStringCoercionDeprecation, + validateStringAfterArrayBufferView, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; + +interface Writer { + write(p: Uint8Array): Promise; +} + +export function writeFile( + pathOrRid: string | number | URL, + // deno-lint-ignore ban-types + data: string | Uint8Array | Object, + optOrCallback: Encodings | CallbackWithError | WriteFileOptions | undefined, + callback?: CallbackWithError, +) { + const callbackFn: CallbackWithError | undefined = + optOrCallback instanceof Function ? optOrCallback : callback; + const options: Encodings | WriteFileOptions | undefined = + optOrCallback instanceof Function ? undefined : optOrCallback; + + if (!callbackFn) { + throw new TypeError("Callback must be a function."); + } + + pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; + + const flag: string | undefined = isFileOptions(options) + ? options.flag + : undefined; + + const mode: number | undefined = isFileOptions(options) + ? options.mode + : undefined; + + const encoding = checkEncoding(getEncoding(options)) || "utf8"; + const openOptions = getOpenOptions(flag || "w"); + + if (!ArrayBuffer.isView(data)) { + validateStringAfterArrayBufferView(data, "data"); + if (typeof data !== "string") { + showStringCoercionDeprecation(); + } + data = Buffer.from(String(data), encoding); + } + + const isRid = typeof pathOrRid === "number"; + let file; + + let error: Error | null = null; + (async () => { + try { + file = isRid + ? new Deno.FsFile(pathOrRid as number) + : await Deno.open(pathOrRid as string, openOptions); + + // ignore mode because it's not supported on windows + // TODO(@bartlomieju): remove `!isWindows` when `Deno.chmod` is supported + if (!isRid && mode && !isWindows) { + await Deno.chmod(pathOrRid as string, mode); + } + + const signal: AbortSignal | undefined = isFileOptions(options) + ? options.signal + : undefined; + await writeAll(file, data as Uint8Array, { signal }); + } catch (e) { + error = e instanceof Error + ? denoErrorToNodeError(e, { syscall: "write" }) + : new Error("[non-error thrown]"); + } finally { + // Make sure to close resource + if (!isRid && file) file.close(); + callbackFn(error); + } + })(); +} + +export const writeFilePromise = promisify(writeFile) as ( + pathOrRid: string | number | URL, + // deno-lint-ignore ban-types + data: string | Uint8Array | Object, + options?: Encodings | WriteFileOptions, +) => Promise; + +export function writeFileSync( + pathOrRid: string | number | URL, + // deno-lint-ignore ban-types + data: string | Uint8Array | Object, + options?: Encodings | WriteFileOptions, +) { + pathOrRid = pathOrRid instanceof URL ? fromFileUrl(pathOrRid) : pathOrRid; + + const flag: string | undefined = isFileOptions(options) + ? options.flag + : undefined; + + const mode: number | undefined = isFileOptions(options) + ? options.mode + : undefined; + + const encoding = checkEncoding(getEncoding(options)) || "utf8"; + const openOptions = getOpenOptions(flag || "w"); + + if (!ArrayBuffer.isView(data)) { + validateStringAfterArrayBufferView(data, "data"); + if (typeof data !== "string") { + showStringCoercionDeprecation(); + } + data = Buffer.from(String(data), encoding); + } + + const isRid = typeof pathOrRid === "number"; + let file; + + let error: Error | null = null; + try { + file = isRid + ? new Deno.FsFile(pathOrRid as number) + : Deno.openSync(pathOrRid as string, openOptions); + + // ignore mode because it's not supported on windows + // TODO(@bartlomieju): remove `!isWindows` when `Deno.chmod` is supported + if (!isRid && mode && !isWindows) { + Deno.chmodSync(pathOrRid as string, mode); + } + + // TODO(crowlKats): duplicate from runtime/js/13_buffer.js + let nwritten = 0; + while (nwritten < (data as Uint8Array).length) { + nwritten += file.writeSync((data as Uint8Array).subarray(nwritten)); + } + } catch (e) { + error = e instanceof Error + ? denoErrorToNodeError(e, { syscall: "write" }) + : new Error("[non-error thrown]"); + } finally { + // Make sure to close resource + if (!isRid && file) file.close(); + } + + if (error) throw error; +} + +interface WriteAllOptions { + offset?: number; + length?: number; + signal?: AbortSignal; +} +async function writeAll( + w: Writer, + arr: Uint8Array, + options: WriteAllOptions = {}, +) { + const { offset = 0, length = arr.byteLength, signal } = options; + checkAborted(signal); + + const written = await w.write(arr.subarray(offset, offset + length)); + + if (written === length) { + return; + } + + await writeAll(w, arr, { + offset: offset + written, + length: length - written, + signal, + }); +} + +function checkAborted(signal?: AbortSignal) { + if (signal?.aborted) { + throw new AbortError(); + } +} diff --git a/ext/node/polyfills/_fs/_fs_writev.d.ts b/ext/node/polyfills/_fs/_fs_writev.d.ts new file mode 100644 index 0000000000..d828bf6771 --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_writev.d.ts @@ -0,0 +1,65 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/d9df51e34526f48bef4e2546a006157b391ad96c/types/node/fs.d.ts + +import { ErrnoException } from "internal:deno_node/polyfills/_global.d.ts"; + +/** + * Write an array of `ArrayBufferView`s to the file specified by `fd` using`writev()`. + * + * `position` is the offset from the beginning of the file where this data + * should be written. If `typeof position !== 'number'`, the data will be written + * at the current position. + * + * The callback will be given three arguments: `err`, `bytesWritten`, and`buffers`. `bytesWritten` is how many bytes were written from `buffers`. + * + * If this method is `util.promisify()` ed, it returns a promise for an`Object` with `bytesWritten` and `buffers` properties. + * + * It is unsafe to use `fs.writev()` multiple times on the same file without + * waiting for the callback. For this scenario, use {@link createWriteStream}. + * + * On Linux, positional writes don't work when the file is opened in append mode. + * The kernel ignores the position argument and always appends the data to + * the end of the file. + * @since v12.9.0 + */ +export function writev( + fd: number, + buffers: ReadonlyArray, + cb: ( + err: ErrnoException | null, + bytesWritten: number, + buffers: ArrayBufferView[], + ) => void, +): void; +export function writev( + fd: number, + buffers: ReadonlyArray, + position: number | null, + cb: ( + err: ErrnoException | null, + bytesWritten: number, + buffers: ArrayBufferView[], + ) => void, +): void; +export interface WriteVResult { + bytesWritten: number; + buffers: ArrayBufferView[]; +} +export namespace writev { + function __promisify__( + fd: number, + buffers: ReadonlyArray, + position?: number, + ): Promise; +} +/** + * For detailed information, see the documentation of the asynchronous version of + * this API: {@link writev}. + * @since v12.9.0 + * @return The number of bytes written. + */ +export function writevSync( + fd: number, + buffers: ReadonlyArray, + position?: number, +): number; diff --git a/ext/node/polyfills/_fs/_fs_writev.mjs b/ext/node/polyfills/_fs/_fs_writev.mjs new file mode 100644 index 0000000000..ffc67c81ae --- /dev/null +++ b/ext/node/polyfills/_fs/_fs_writev.mjs @@ -0,0 +1,81 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { validateBufferArray } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { getValidatedFd } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { maybeCallback } from "internal:deno_node/polyfills/_fs/_fs_common.ts"; + +export function writev(fd, buffers, position, callback) { + const innerWritev = async (fd, buffers, position) => { + const chunks = []; + const offset = 0; + for (let i = 0; i < buffers.length; i++) { + if (Buffer.isBuffer(buffers[i])) { + chunks.push(buffers[i]); + } else { + chunks.push(Buffer.from(buffers[i])); + } + } + if (typeof position === "number") { + await Deno.seekSync(fd, position, Deno.SeekMode.Start); + } + const buffer = Buffer.concat(chunks); + let currentOffset = 0; + while (currentOffset < buffer.byteLength) { + currentOffset += await Deno.writeSync(fd, buffer.subarray(currentOffset)); + } + return currentOffset - offset; + }; + + fd = getValidatedFd(fd); + validateBufferArray(buffers); + callback = maybeCallback(callback || position); + + if (buffers.length === 0) { + process.nextTick(callback, null, 0, buffers); + return; + } + + if (typeof position !== "number") position = null; + + innerWritev(fd, buffers, position).then( + (nwritten) => { + callback(null, nwritten, buffers); + }, + (err) => callback(err), + ); +} + +export function writevSync(fd, buffers, position) { + const innerWritev = (fd, buffers, position) => { + const chunks = []; + const offset = 0; + for (let i = 0; i < buffers.length; i++) { + if (Buffer.isBuffer(buffers[i])) { + chunks.push(buffers[i]); + } else { + chunks.push(Buffer.from(buffers[i])); + } + } + if (typeof position === "number") { + Deno.seekSync(fd, position, Deno.SeekMode.Start); + } + const buffer = Buffer.concat(chunks); + let currentOffset = 0; + while (currentOffset < buffer.byteLength) { + currentOffset += Deno.writeSync(fd, buffer.subarray(currentOffset)); + } + return currentOffset - offset; + }; + + fd = getValidatedFd(fd); + validateBufferArray(buffers); + + if (buffers.length === 0) { + return 0; + } + + if (typeof position !== "number") position = null; + + return innerWritev(fd, buffers, position); +} diff --git a/ext/node/polyfills/_fs/testdata/hello.txt b/ext/node/polyfills/_fs/testdata/hello.txt new file mode 100644 index 0000000000..95d09f2b10 --- /dev/null +++ b/ext/node/polyfills/_fs/testdata/hello.txt @@ -0,0 +1 @@ +hello world \ No newline at end of file diff --git a/ext/node/polyfills/_global.d.ts b/ext/node/polyfills/_global.d.ts new file mode 100644 index 0000000000..4b017c4044 --- /dev/null +++ b/ext/node/polyfills/_global.d.ts @@ -0,0 +1,66 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { EventEmitter } from "internal:deno_node/polyfills/_events.d.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +/** One of: + * | "ascii" + * | "utf8" + * | "utf-8" + * | "utf16le" + * | "ucs2" + * | "ucs-2" + * | "base64" + * | "base64url" + * | "latin1" + * | "binary" + * | "hex"; + */ +export type BufferEncoding = string; + +export interface Buffered { + chunk: Buffer; + encoding: string; + callback: (err?: Error | null) => void; +} + +export interface ErrnoException extends Error { + errno?: number | undefined; + code?: string | undefined; + path?: string | undefined; + syscall?: string | undefined; +} + +export interface ReadableStream extends EventEmitter { + readable: boolean; + read(size?: number): string | Buffer; + setEncoding(encoding: BufferEncoding): this; + pause(): this; + resume(): this; + isPaused(): boolean; + pipe( + destination: T, + options?: { end?: boolean | undefined }, + ): T; + unpipe(destination?: WritableStream): this; + unshift(chunk: string | Uint8Array, encoding?: BufferEncoding): void; + wrap(oldStream: ReadableStream): this; + [Symbol.asyncIterator](): AsyncIterableIterator; +} + +export interface WritableStream extends EventEmitter { + writable: boolean; + write( + buffer: Uint8Array | string, + cb?: (err?: Error | null) => void, + ): boolean; + write( + str: string, + encoding?: BufferEncoding, + cb?: (err?: Error | null) => void, + ): boolean; + end(cb?: () => void): void; + end(data: string | Uint8Array, cb?: () => void): void; + end(str: string, encoding?: BufferEncoding, cb?: () => void): void; +} + +export interface ReadWriteStream extends ReadableStream, WritableStream {} diff --git a/ext/node/polyfills/_http_agent.mjs b/ext/node/polyfills/_http_agent.mjs new file mode 100644 index 0000000000..20c1eaf0bf --- /dev/null +++ b/ext/node/polyfills/_http_agent.mjs @@ -0,0 +1,526 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import * as net from "internal:deno_node/polyfills/net.ts"; +import EventEmitter from "internal:deno_node/polyfills/events.ts"; +import { debuglog } from "internal:deno_node/polyfills/internal/util/debuglog.ts"; +let debug = debuglog("http", (fn) => { + debug = fn; +}); +import { AsyncResource } from "internal:deno_node/polyfills/async_hooks.ts"; +import { symbols } from "internal:deno_node/polyfills/internal/async_hooks.ts"; +// deno-lint-ignore camelcase +const { async_id_symbol } = symbols; +import { ERR_OUT_OF_RANGE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { once } from "internal:deno_node/polyfills/internal/util.mjs"; +import { + validateNumber, + validateOneOf, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; + +const kOnKeylog = Symbol("onkeylog"); +const kRequestOptions = Symbol("requestOptions"); +const kRequestAsyncResource = Symbol("requestAsyncResource"); +// New Agent code. + +// The largest departure from the previous implementation is that +// an Agent instance holds connections for a variable number of host:ports. +// Surprisingly, this is still API compatible as far as third parties are +// concerned. The only code that really notices the difference is the +// request object. + +// Another departure is that all code related to HTTP parsing is in +// ClientRequest.onSocket(). The Agent is now *strictly* +// concerned with managing a connection pool. + +class ReusedHandle { + constructor(type, handle) { + this.type = type; + this.handle = handle; + } +} + +function freeSocketErrorListener(err) { + // deno-lint-ignore no-this-alias + const socket = this; + debug("SOCKET ERROR on FREE socket:", err.message, err.stack); + socket.destroy(); + socket.emit("agentRemove"); +} + +export function Agent(options) { + if (!(this instanceof Agent)) { + return new Agent(options); + } + + EventEmitter.call(this); + + this.defaultPort = 80; + this.protocol = "http:"; + + this.options = { __proto__: null, ...options }; + + // Don't confuse net and make it think that we're connecting to a pipe + this.options.path = null; + this.requests = Object.create(null); + this.sockets = Object.create(null); + this.freeSockets = Object.create(null); + this.keepAliveMsecs = this.options.keepAliveMsecs || 1000; + this.keepAlive = this.options.keepAlive || false; + this.maxSockets = this.options.maxSockets || Agent.defaultMaxSockets; + this.maxFreeSockets = this.options.maxFreeSockets || 256; + this.scheduling = this.options.scheduling || "lifo"; + this.maxTotalSockets = this.options.maxTotalSockets; + this.totalSocketCount = 0; + + validateOneOf(this.scheduling, "scheduling", ["fifo", "lifo"]); + + if (this.maxTotalSockets !== undefined) { + validateNumber(this.maxTotalSockets, "maxTotalSockets"); + if (this.maxTotalSockets <= 0 || Number.isNaN(this.maxTotalSockets)) { + throw new ERR_OUT_OF_RANGE( + "maxTotalSockets", + "> 0", + this.maxTotalSockets, + ); + } + } else { + this.maxTotalSockets = Infinity; + } + + this.on("free", (socket, options) => { + const name = this.getName(options); + debug("agent.on(free)", name); + + // TODO(ronag): socket.destroy(err) might have been called + // before coming here and have an 'error' scheduled. In the + // case of socket.destroy() below this 'error' has no handler + // and could cause unhandled exception. + + if (!socket.writable) { + socket.destroy(); + return; + } + + const requests = this.requests[name]; + if (requests && requests.length) { + const req = requests.shift(); + const reqAsyncRes = req[kRequestAsyncResource]; + if (reqAsyncRes) { + // Run request within the original async context. + reqAsyncRes.runInAsyncScope(() => { + asyncResetHandle(socket); + setRequestSocket(this, req, socket); + }); + req[kRequestAsyncResource] = null; + } else { + setRequestSocket(this, req, socket); + } + if (requests.length === 0) { + delete this.requests[name]; + } + return; + } + + // If there are no pending requests, then put it in + // the freeSockets pool, but only if we're allowed to do so. + const req = socket._httpMessage; + if (!req || !req.shouldKeepAlive || !this.keepAlive) { + socket.destroy(); + return; + } + + const freeSockets = this.freeSockets[name] || []; + const freeLen = freeSockets.length; + let count = freeLen; + if (this.sockets[name]) { + count += this.sockets[name].length; + } + + if ( + this.totalSocketCount > this.maxTotalSockets || + count > this.maxSockets || + freeLen >= this.maxFreeSockets || + !this.keepSocketAlive(socket) + ) { + socket.destroy(); + return; + } + + this.freeSockets[name] = freeSockets; + socket[async_id_symbol] = -1; + socket._httpMessage = null; + this.removeSocket(socket, options); + + socket.once("error", freeSocketErrorListener); + freeSockets.push(socket); + }); + + // Don't emit keylog events unless there is a listener for them. + this.on("newListener", maybeEnableKeylog); +} +Object.setPrototypeOf(Agent.prototype, EventEmitter.prototype); +Object.setPrototypeOf(Agent, EventEmitter); + +function maybeEnableKeylog(eventName) { + if (eventName === "keylog") { + this.removeListener("newListener", maybeEnableKeylog); + // Future sockets will listen on keylog at creation. + // deno-lint-ignore no-this-alias + const agent = this; + this[kOnKeylog] = function onkeylog(keylog) { + agent.emit("keylog", keylog, this); + }; + // Existing sockets will start listening on keylog now. + const sockets = ObjectValues(this.sockets); + for (let i = 0; i < sockets.length; i++) { + sockets[i].on("keylog", this[kOnKeylog]); + } + } +} + +Agent.defaultMaxSockets = Infinity; + +Agent.prototype.createConnection = net.createConnection; + +// Get the key for a given set of request options +Agent.prototype.getName = function getName(options = {}) { + let name = options.host || "localhost"; + + name += ":"; + if (options.port) { + name += options.port; + } + + name += ":"; + if (options.localAddress) { + name += options.localAddress; + } + + // Pacify parallel/test-http-agent-getname by only appending + // the ':' when options.family is set. + if (options.family === 4 || options.family === 6) { + name += `:${options.family}`; + } + + if (options.socketPath) { + name += `:${options.socketPath}`; + } + + return name; +}; + +Agent.prototype.addRequest = function addRequest( + req, + options, + port, /* legacy */ + localAddress, /* legacy */ +) { + // Legacy API: addRequest(req, host, port, localAddress) + if (typeof options === "string") { + options = { + __proto__: null, + host: options, + port, + localAddress, + }; + } + + options = { __proto__: null, ...options, ...this.options }; + if (options.socketPath) { + options.path = options.socketPath; + } + + if (!options.servername && options.servername !== "") { + options.servername = calculateServerName(options, req); + } + + const name = this.getName(options); + if (!this.sockets[name]) { + this.sockets[name] = []; + } + + const freeSockets = this.freeSockets[name]; + let socket; + if (freeSockets) { + while (freeSockets.length && freeSockets[0].destroyed) { + freeSockets.shift(); + } + socket = this.scheduling === "fifo" + ? freeSockets.shift() + : freeSockets.pop(); + if (!freeSockets.length) { + delete this.freeSockets[name]; + } + } + + const freeLen = freeSockets ? freeSockets.length : 0; + const sockLen = freeLen + this.sockets[name].length; + + if (socket) { + asyncResetHandle(socket); + this.reuseSocket(socket, req); + setRequestSocket(this, req, socket); + this.sockets[name].push(socket); + } else if ( + sockLen < this.maxSockets && + this.totalSocketCount < this.maxTotalSockets + ) { + debug("call onSocket", sockLen, freeLen); + // If we are under maxSockets create a new one. + this.createSocket(req, options, (err, socket) => { + if (err) { + req.onSocket(socket, err); + } else { + setRequestSocket(this, req, socket); + } + }); + } else { + debug("wait for socket"); + // We are over limit so we'll add it to the queue. + if (!this.requests[name]) { + this.requests[name] = []; + } + + // Used to create sockets for pending requests from different origin + req[kRequestOptions] = options; + // Used to capture the original async context. + req[kRequestAsyncResource] = new AsyncResource("QueuedRequest"); + + this.requests[name].push(req); + } +}; + +Agent.prototype.createSocket = function createSocket(req, options, cb) { + options = { __proto__: null, ...options, ...this.options }; + if (options.socketPath) { + options.path = options.socketPath; + } + + if (!options.servername && options.servername !== "") { + options.servername = calculateServerName(options, req); + } + + const name = this.getName(options); + options._agentKey = name; + + debug("createConnection", name, options); + options.encoding = null; + + const oncreate = once((err, s) => { + if (err) { + return cb(err); + } + if (!this.sockets[name]) { + this.sockets[name] = []; + } + this.sockets[name].push(s); + this.totalSocketCount++; + debug("sockets", name, this.sockets[name].length, this.totalSocketCount); + installListeners(this, s, options); + cb(null, s); + }); + + const newSocket = this.createConnection(options, oncreate); + if (newSocket) { + oncreate(null, newSocket); + } +}; + +function calculateServerName(options, req) { + let servername = options.host; + const hostHeader = req.getHeader("host"); + if (hostHeader) { + validateString(hostHeader, "options.headers.host"); + + // abc => abc + // abc:123 => abc + // [::1] => ::1 + // [::1]:123 => ::1 + if (hostHeader.startsWith("[")) { + const index = hostHeader.indexOf("]"); + if (index === -1) { + // Leading '[', but no ']'. Need to do something... + servername = hostHeader; + } else { + servername = hostHeader.slice(1, index); + } + } else { + servername = hostHeader.split(":", 1)[0]; + } + } + // Don't implicitly set invalid (IP) servernames. + if (net.isIP(servername)) { + servername = ""; + } + return servername; +} + +function installListeners(agent, s, options) { + function onFree() { + debug("CLIENT socket onFree"); + agent.emit("free", s, options); + } + s.on("free", onFree); + + function onClose(_err) { + debug("CLIENT socket onClose"); + // This is the only place where sockets get removed from the Agent. + // If you want to remove a socket from the pool, just close it. + // All socket errors end in a close event anyway. + agent.totalSocketCount--; + agent.removeSocket(s, options); + } + s.on("close", onClose); + + function onTimeout() { + debug("CLIENT socket onTimeout"); + + // Destroy if in free list. + // TODO(ronag): Always destroy, even if not in free list. + const sockets = agent.freeSockets; + if (Object.keys(sockets).some((name) => sockets[name].includes(s))) { + return s.destroy(); + } + } + s.on("timeout", onTimeout); + + function onRemove() { + // We need this function for cases like HTTP 'upgrade' + // (defined by WebSockets) where we need to remove a socket from the + // pool because it'll be locked up indefinitely + debug("CLIENT socket onRemove"); + agent.totalSocketCount--; + agent.removeSocket(s, options); + s.removeListener("close", onClose); + s.removeListener("free", onFree); + s.removeListener("timeout", onTimeout); + s.removeListener("agentRemove", onRemove); + } + s.on("agentRemove", onRemove); + + if (agent[kOnKeylog]) { + s.on("keylog", agent[kOnKeylog]); + } +} + +Agent.prototype.removeSocket = function removeSocket(s, options) { + const name = this.getName(options); + debug("removeSocket", name, "writable:", s.writable); + const sets = [this.sockets]; + + // If the socket was destroyed, remove it from the free buffers too. + if (!s.writable) { + sets.push(this.freeSockets); + } + + for (let sk = 0; sk < sets.length; sk++) { + const sockets = sets[sk]; + + if (sockets[name]) { + const index = sockets[name].indexOf(s); + if (index !== -1) { + sockets[name].splice(index, 1); + // Don't leak + if (sockets[name].length === 0) { + delete sockets[name]; + } + } + } + } + + let req; + if (this.requests[name] && this.requests[name].length) { + debug("removeSocket, have a request, make a socket"); + req = this.requests[name][0]; + } else { + // TODO(rickyes): this logic will not be FIFO across origins. + // There might be older requests in a different origin, but + // if the origin which releases the socket has pending requests + // that will be prioritized. + const keys = Object.keys(this.requests); + for (let i = 0; i < keys.length; i++) { + const prop = keys[i]; + // Check whether this specific origin is already at maxSockets + if (this.sockets[prop] && this.sockets[prop].length) break; + debug( + "removeSocket, have a request with different origin," + + " make a socket", + ); + req = this.requests[prop][0]; + options = req[kRequestOptions]; + break; + } + } + + if (req && options) { + req[kRequestOptions] = undefined; + // If we have pending requests and a socket gets closed make a new one + this.createSocket(req, options, (err, socket) => { + if (err) { + req.onSocket(socket, err); + } else { + socket.emit("free"); + } + }); + } +}; + +Agent.prototype.keepSocketAlive = function keepSocketAlive(socket) { + socket.setKeepAlive(true, this.keepAliveMsecs); + socket.unref(); + + const agentTimeout = this.options.timeout || 0; + if (socket.timeout !== agentTimeout) { + socket.setTimeout(agentTimeout); + } + + return true; +}; + +Agent.prototype.reuseSocket = function reuseSocket(socket, req) { + debug("have free socket"); + socket.removeListener("error", freeSocketErrorListener); + req.reusedSocket = true; + socket.ref(); +}; + +Agent.prototype.destroy = function destroy() { + const sets = [this.freeSockets, this.sockets]; + for (let s = 0; s < sets.length; s++) { + const set = sets[s]; + const keys = Object.keys(set); + for (let v = 0; v < keys.length; v++) { + const setName = set[keys[v]]; + for (let n = 0; n < setName.length; n++) { + setName[n].destroy(); + } + } + } +}; + +function setRequestSocket(agent, req, socket) { + req.onSocket(socket); + const agentTimeout = agent.options.timeout || 0; + if (req.timeout === undefined || req.timeout === agentTimeout) { + return; + } + socket.setTimeout(req.timeout); +} + +function asyncResetHandle(socket) { + // Guard against an uninitialized or user supplied Socket. + const handle = socket._handle; + if (handle && typeof handle.asyncReset === "function") { + // Assign the handle a new asyncId and run any destroy()/init() hooks. + handle.asyncReset(new ReusedHandle(handle.getProviderType(), handle)); + socket[async_id_symbol] = handle.getAsyncId(); + } +} + +export const globalAgent = new Agent(); +export default { + Agent, + globalAgent, +}; diff --git a/ext/node/polyfills/_http_common.ts b/ext/node/polyfills/_http_common.ts new file mode 100644 index 0000000000..616fbb65da --- /dev/null +++ b/ext/node/polyfills/_http_common.ts @@ -0,0 +1,29 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +const tokenRegExp = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/; +/** + * Verifies that the given val is a valid HTTP token + * per the rules defined in RFC 7230 + * See https://tools.ietf.org/html/rfc7230#section-3.2.6 + */ +function checkIsHttpToken(val: string) { + return tokenRegExp.test(val); +} + +const headerCharRegex = /[^\t\x20-\x7e\x80-\xff]/; +/** + * True if val contains an invalid field-vchar + * field-value = *( field-content / obs-fold ) + * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ] + * field-vchar = VCHAR / obs-text + */ +function checkInvalidHeaderChar(val: string) { + return headerCharRegex.test(val); +} + +export const chunkExpression = /(?:^|\W)chunked(?:$|\W)/i; +export { + checkInvalidHeaderChar as _checkInvalidHeaderChar, + checkIsHttpToken as _checkIsHttpToken, +}; diff --git a/ext/node/polyfills/_http_outgoing.ts b/ext/node/polyfills/_http_outgoing.ts new file mode 100644 index 0000000000..cb0312d4a8 --- /dev/null +++ b/ext/node/polyfills/_http_outgoing.ts @@ -0,0 +1,1099 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { getDefaultHighWaterMark } from "internal:deno_node/polyfills/internal/streams/state.mjs"; +import assert from "internal:deno_node/polyfills/internal/assert.mjs"; +import EE from "internal:deno_node/polyfills/events.ts"; +import { Stream } from "internal:deno_node/polyfills/stream.ts"; +import { deprecate } from "internal:deno_node/polyfills/util.ts"; +import type { Socket } from "internal:deno_node/polyfills/net.ts"; +import { + kNeedDrain, + kOutHeaders, + utcDate, +} from "internal:deno_node/polyfills/internal/http.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + _checkInvalidHeaderChar as checkInvalidHeaderChar, + _checkIsHttpToken as checkIsHttpToken, + chunkExpression as RE_TE_CHUNKED, +} from "internal:deno_node/polyfills/_http_common.ts"; +import { + defaultTriggerAsyncIdScope, + symbols, +} from "internal:deno_node/polyfills/internal/async_hooks.ts"; +// deno-lint-ignore camelcase +const { async_id_symbol } = symbols; +import { + ERR_HTTP_HEADERS_SENT, + ERR_HTTP_INVALID_HEADER_VALUE, + ERR_HTTP_TRAILER_INVALID, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_CHAR, + ERR_INVALID_HTTP_TOKEN, + ERR_METHOD_NOT_IMPLEMENTED, + ERR_STREAM_ALREADY_FINISHED, + ERR_STREAM_CANNOT_PIPE, + ERR_STREAM_DESTROYED, + ERR_STREAM_NULL_VALUES, + ERR_STREAM_WRITE_AFTER_END, + hideStackFrames, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isUint8Array } from "internal:deno_node/polyfills/internal/util/types.ts"; + +import { debuglog } from "internal:deno_node/polyfills/internal/util/debuglog.ts"; +let debug = debuglog("http", (fn) => { + debug = fn; +}); + +const HIGH_WATER_MARK = getDefaultHighWaterMark(); + +const kCorked = Symbol("corked"); + +const nop = () => {}; + +const RE_CONN_CLOSE = /(?:^|\W)close(?:$|\W)/i; + +// isCookieField performs a case-insensitive comparison of a provided string +// against the word "cookie." As of V8 6.6 this is faster than handrolling or +// using a case-insensitive RegExp. +function isCookieField(s: string) { + return s.length === 6 && s.toLowerCase() === "cookie"; +} + +// deno-lint-ignore no-explicit-any +export function OutgoingMessage(this: any) { + Stream.call(this); + + // Queue that holds all currently pending data, until the response will be + // assigned to the socket (until it will its turn in the HTTP pipeline). + this.outputData = []; + + // `outputSize` is an approximate measure of how much data is queued on this + // response. `_onPendingData` will be invoked to update similar global + // per-connection counter. That counter will be used to pause/unpause the + // TCP socket and HTTP Parser and thus handle the backpressure. + this.outputSize = 0; + + this.writable = true; + this.destroyed = false; + + this._last = false; + this.chunkedEncoding = false; + this.shouldKeepAlive = true; + this.maxRequestsOnConnectionReached = false; + this._defaultKeepAlive = true; + this.useChunkedEncodingByDefault = true; + this.sendDate = false; + this._removedConnection = false; + this._removedContLen = false; + this._removedTE = false; + + this._contentLength = null; + this._hasBody = true; + this._trailer = ""; + this[kNeedDrain] = false; + + this.finished = false; + this._headerSent = false; + this[kCorked] = 0; + this._closed = false; + + this.socket = null; + this._header = null; + this[kOutHeaders] = null; + + this._keepAliveTimeout = 0; + + this._onPendingData = nop; +} +Object.setPrototypeOf(OutgoingMessage.prototype, Stream.prototype); +Object.setPrototypeOf(OutgoingMessage, Stream); + +Object.defineProperty(OutgoingMessage.prototype, "writableFinished", { + get() { + return ( + this.finished && + this.outputSize === 0 && + (!this.socket || this.socket.writableLength === 0) + ); + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "writableObjectMode", { + get() { + return false; + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "writableLength", { + get() { + return this.outputSize + (this.socket ? this.socket.writableLength : 0); + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "writableHighWaterMark", { + get() { + return this.socket ? this.socket.writableHighWaterMark : HIGH_WATER_MARK; + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "writableCorked", { + get() { + const corked = this.socket ? this.socket.writableCorked : 0; + return corked + this[kCorked]; + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "_headers", { + get: deprecate( + // deno-lint-ignore no-explicit-any + function (this: any) { + return this.getHeaders(); + }, + "OutgoingMessage.prototype._headers is deprecated", + "DEP0066", + ), + set: deprecate( + // deno-lint-ignore no-explicit-any + function (this: any, val: any) { + if (val == null) { + this[kOutHeaders] = null; + } else if (typeof val === "object") { + const headers = this[kOutHeaders] = Object.create(null); + const keys = Object.keys(val); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0; i < keys.length; ++i) { + const name = keys[i]; + headers[name.toLowerCase()] = [name, val[name]]; + } + } + }, + "OutgoingMessage.prototype._headers is deprecated", + "DEP0066", + ), +}); + +Object.defineProperty(OutgoingMessage.prototype, "connection", { + get: function () { + return this.socket; + }, + set: function (val) { + this.socket = val; + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "_headerNames", { + get: deprecate( + // deno-lint-ignore no-explicit-any + function (this: any) { + const headers = this[kOutHeaders]; + if (headers !== null) { + const out = Object.create(null); + const keys = Object.keys(headers); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0; i < keys.length; ++i) { + const key = keys[i]; + const val = headers[key][0]; + out[key] = val; + } + return out; + } + return null; + }, + "OutgoingMessage.prototype._headerNames is deprecated", + "DEP0066", + ), + set: deprecate( + // deno-lint-ignore no-explicit-any + function (this: any, val: any) { + if (typeof val === "object" && val !== null) { + const headers = this[kOutHeaders]; + if (!headers) { + return; + } + const keys = Object.keys(val); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0; i < keys.length; ++i) { + const header = headers[keys[i]]; + if (header) { + header[0] = val[keys[i]]; + } + } + } + }, + "OutgoingMessage.prototype._headerNames is deprecated", + "DEP0066", + ), +}); + +OutgoingMessage.prototype._renderHeaders = function _renderHeaders() { + if (this._header) { + throw new ERR_HTTP_HEADERS_SENT("render"); + } + + const headersMap = this[kOutHeaders]; + // deno-lint-ignore no-explicit-any + const headers: any = {}; + + if (headersMap !== null) { + const keys = Object.keys(headersMap); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0, l = keys.length; i < l; i++) { + const key = keys[i]; + headers[headersMap[key][0]] = headersMap[key][1]; + } + } + return headers; +}; + +OutgoingMessage.prototype.cork = function () { + if (this.socket) { + this.socket.cork(); + } else { + this[kCorked]++; + } +}; + +OutgoingMessage.prototype.uncork = function () { + if (this.socket) { + this.socket.uncork(); + } else if (this[kCorked]) { + this[kCorked]--; + } +}; + +OutgoingMessage.prototype.setTimeout = function setTimeout( + msecs: number, + callback?: (...args: unknown[]) => void, +) { + if (callback) { + this.on("timeout", callback); + } + + if (!this.socket) { + // deno-lint-ignore no-explicit-any + this.once("socket", function socketSetTimeoutOnConnect(socket: any) { + socket.setTimeout(msecs); + }); + } else { + this.socket.setTimeout(msecs); + } + return this; +}; + +// It's possible that the socket will be destroyed, and removed from +// any messages, before ever calling this. In that case, just skip +// it, since something else is destroying this connection anyway. +OutgoingMessage.prototype.destroy = function destroy(error: unknown) { + if (this.destroyed) { + return this; + } + this.destroyed = true; + + if (this.socket) { + this.socket.destroy(error); + } else { + // deno-lint-ignore no-explicit-any + this.once("socket", function socketDestroyOnConnect(socket: any) { + socket.destroy(error); + }); + } + + return this; +}; + +// This abstract either writing directly to the socket or buffering it. +OutgoingMessage.prototype._send = function _send( + // deno-lint-ignore no-explicit-any + data: any, + encoding: string | null, + callback: () => void, +) { + // This is a shameful hack to get the headers and first body chunk onto + // the same packet. Future versions of Node are going to take care of + // this at a lower level and in a more general way. + if (!this._headerSent) { + if ( + typeof data === "string" && + (encoding === "utf8" || encoding === "latin1" || !encoding) + ) { + data = this._header + data; + } else { + const header = this._header; + this.outputData.unshift({ + data: header, + encoding: "latin1", + callback: null, + }); + this.outputSize += header.length; + this._onPendingData(header.length); + } + this._headerSent = true; + } + return this._writeRaw(data, encoding, callback); +}; + +OutgoingMessage.prototype._writeRaw = _writeRaw; +function _writeRaw( + // deno-lint-ignore no-explicit-any + this: any, + // deno-lint-ignore no-explicit-any + data: any, + encoding: string | null, + callback: () => void, +) { + const conn = this.socket; + if (conn && conn.destroyed) { + // The socket was destroyed. If we're still trying to write to it, + // then we haven't gotten the 'close' event yet. + return false; + } + + if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } + + if (conn && conn._httpMessage === this && conn.writable) { + // There might be pending data in the this.output buffer. + if (this.outputData.length) { + this._flushOutput(conn); + } + // Directly write to socket. + return conn.write(data, encoding, callback); + } + // Buffer, as long as we're not destroyed. + this.outputData.push({ data, encoding, callback }); + this.outputSize += data.length; + this._onPendingData(data.length); + return this.outputSize < HIGH_WATER_MARK; +} + +OutgoingMessage.prototype._storeHeader = _storeHeader; +// deno-lint-ignore no-explicit-any +function _storeHeader(this: any, firstLine: any, headers: any) { + // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n' + // in the case of response it is: 'HTTP/1.1 200 OK\r\n' + const state = { + connection: false, + contLen: false, + te: false, + date: false, + expect: false, + trailer: false, + header: firstLine, + }; + + if (headers) { + if (headers === this[kOutHeaders]) { + for (const key in headers) { + if (Object.hasOwn(headers, key)) { + const entry = headers[key]; + processHeader(this, state, entry[0], entry[1], false); + } + } + } else if (Array.isArray(headers)) { + if (headers.length && Array.isArray(headers[0])) { + for (let i = 0; i < headers.length; i++) { + const entry = headers[i]; + processHeader(this, state, entry[0], entry[1], true); + } + } else { + if (headers.length % 2 !== 0) { + throw new ERR_INVALID_ARG_VALUE("headers", headers); + } + + for (let n = 0; n < headers.length; n += 2) { + processHeader(this, state, headers[n + 0], headers[n + 1], true); + } + } + } else { + for (const key in headers) { + if (Object.hasOwn(headers, key)) { + processHeader(this, state, key, headers[key], true); + } + } + } + } + + let { header } = state; + + // Date header + if (this.sendDate && !state.date) { + header += "Date: " + utcDate() + "\r\n"; + } + + // Force the connection to close when the response is a 204 No Content or + // a 304 Not Modified and the user has set a "Transfer-Encoding: chunked" + // header. + // + // RFC 2616 mandates that 204 and 304 responses MUST NOT have a body but + // node.js used to send out a zero chunk anyway to accommodate clients + // that don't have special handling for those responses. + // + // It was pointed out that this might confuse reverse proxies to the point + // of creating security liabilities, so suppress the zero chunk and force + // the connection to close. + if ( + this.chunkedEncoding && (this.statusCode === 204 || + this.statusCode === 304) + ) { + debug( + this.statusCode + " response should not use chunked encoding," + + " closing connection.", + ); + this.chunkedEncoding = false; + this.shouldKeepAlive = false; + } + + // keep-alive logic + if (this._removedConnection) { + this._last = true; + this.shouldKeepAlive = false; + } else if (!state.connection) { + const shouldSendKeepAlive = this.shouldKeepAlive && + (state.contLen || this.useChunkedEncodingByDefault || this.agent); + if (shouldSendKeepAlive && this.maxRequestsOnConnectionReached) { + header += "Connection: close\r\n"; + } else if (shouldSendKeepAlive) { + header += "Connection: keep-alive\r\n"; + if (this._keepAliveTimeout && this._defaultKeepAlive) { + const timeoutSeconds = Math.floor(this._keepAliveTimeout / 1000); + header += `Keep-Alive: timeout=${timeoutSeconds}\r\n`; + } + } else { + this._last = true; + header += "Connection: close\r\n"; + } + } + + if (!state.contLen && !state.te) { + if (!this._hasBody) { + // Make sure we don't end the 0\r\n\r\n at the end of the message. + this.chunkedEncoding = false; + } else if (!this.useChunkedEncodingByDefault) { + this._last = true; + } else if ( + !state.trailer && + !this._removedContLen && + typeof this._contentLength === "number" + ) { + header += "Content-Length: " + this._contentLength + "\r\n"; + } else if (!this._removedTE) { + header += "Transfer-Encoding: chunked\r\n"; + this.chunkedEncoding = true; + } else { + // We should only be able to get here if both Content-Length and + // Transfer-Encoding are removed by the user. + // See: test/parallel/test-http-remove-header-stays-removed.js + debug("Both Content-Length and Transfer-Encoding are removed"); + } + } + + // Test non-chunked message does not have trailer header set, + // message will be terminated by the first empty line after the + // header fields, regardless of the header fields present in the + // message, and thus cannot contain a message body or 'trailers'. + if (this.chunkedEncoding !== true && state.trailer) { + throw new ERR_HTTP_TRAILER_INVALID(); + } + + this._header = header + "\r\n"; + this._headerSent = false; + + // Wait until the first body chunk, or close(), is sent to flush, + // UNLESS we're sending Expect: 100-continue. + if (state.expect) this._send(""); +} + +function processHeader( + // deno-lint-ignore no-explicit-any + self: any, + // deno-lint-ignore no-explicit-any + state: any, + // deno-lint-ignore no-explicit-any + key: any, + // deno-lint-ignore no-explicit-any + value: any, + // deno-lint-ignore no-explicit-any + validate: any, +) { + if (validate) { + validateHeaderName(key); + } + if (Array.isArray(value)) { + if (value.length < 2 || !isCookieField(key)) { + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0; i < value.length; i++) { + storeHeader(self, state, key, value[i], validate); + } + return; + } + value = value.join("; "); + } + storeHeader(self, state, key, value, validate); +} + +function storeHeader( + // deno-lint-ignore no-explicit-any + self: any, + // deno-lint-ignore no-explicit-any + state: any, + // deno-lint-ignore no-explicit-any + key: any, + // deno-lint-ignore no-explicit-any + value: any, + // deno-lint-ignore no-explicit-any + validate: any, +) { + if (validate) { + validateHeaderValue(key, value); + } + state.header += key + ": " + value + "\r\n"; + matchHeader(self, state, key, value); +} + +// deno-lint-ignore no-explicit-any +function matchHeader(self: any, state: any, field: string, value: any) { + if (field.length < 4 || field.length > 17) { + return; + } + field = field.toLowerCase(); + switch (field) { + case "connection": + state.connection = true; + self._removedConnection = false; + if (RE_CONN_CLOSE.test(value)) { + self._last = true; + } else { + self.shouldKeepAlive = true; + } + break; + case "transfer-encoding": + state.te = true; + self._removedTE = false; + if (RE_TE_CHUNKED.test(value)) { + self.chunkedEncoding = true; + } + break; + case "content-length": + state.contLen = true; + self._removedContLen = false; + break; + case "date": + case "expect": + case "trailer": + state[field] = true; + break; + case "keep-alive": + self._defaultKeepAlive = false; + break; + } +} + +export const validateHeaderName = hideStackFrames((name) => { + if (typeof name !== "string" || !name || !checkIsHttpToken(name)) { + throw new ERR_INVALID_HTTP_TOKEN("Header name", name); + } +}); + +export const validateHeaderValue = hideStackFrames((name, value) => { + if (value === undefined) { + throw new ERR_HTTP_INVALID_HEADER_VALUE(value, name); + } + if (checkInvalidHeaderChar(value)) { + debug('Header "%s" contains invalid characters', name); + throw new ERR_INVALID_CHAR("header content", name); + } +}); + +OutgoingMessage.prototype.setHeader = function setHeader( + name: string, + value: string, +) { + if (this._header) { + throw new ERR_HTTP_HEADERS_SENT("set"); + } + validateHeaderName(name); + validateHeaderValue(name, value); + + let headers = this[kOutHeaders]; + if (headers === null) { + this[kOutHeaders] = headers = Object.create(null); + } + + headers[name.toLowerCase()] = [name, value]; + return this; +}; + +OutgoingMessage.prototype.getHeader = function getHeader(name: string) { + validateString(name, "name"); + + const headers = this[kOutHeaders]; + if (headers === null) { + return; + } + + const entry = headers[name.toLowerCase()]; + return entry && entry[1]; +}; + +// Returns an array of the names of the current outgoing headers. +OutgoingMessage.prototype.getHeaderNames = function getHeaderNames() { + return this[kOutHeaders] !== null ? Object.keys(this[kOutHeaders]) : []; +}; + +// Returns an array of the names of the current outgoing raw headers. +OutgoingMessage.prototype.getRawHeaderNames = function getRawHeaderNames() { + const headersMap = this[kOutHeaders]; + if (headersMap === null) return []; + + const values = Object.values(headersMap); + const headers = Array(values.length); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0, l = values.length; i < l; i++) { + // deno-lint-ignore no-explicit-any + headers[i] = (values as any)[i][0]; + } + + return headers; +}; + +// Returns a shallow copy of the current outgoing headers. +OutgoingMessage.prototype.getHeaders = function getHeaders() { + const headers = this[kOutHeaders]; + const ret = Object.create(null); + if (headers) { + const keys = Object.keys(headers); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0; i < keys.length; ++i) { + const key = keys[i]; + const val = headers[key][1]; + ret[key] = val; + } + } + return ret; +}; + +OutgoingMessage.prototype.hasHeader = function hasHeader(name: string) { + validateString(name, "name"); + return this[kOutHeaders] !== null && + !!this[kOutHeaders][name.toLowerCase()]; +}; + +OutgoingMessage.prototype.removeHeader = function removeHeader(name: string) { + validateString(name, "name"); + + if (this._header) { + throw new ERR_HTTP_HEADERS_SENT("remove"); + } + + const key = name.toLowerCase(); + + switch (key) { + case "connection": + this._removedConnection = true; + break; + case "content-length": + this._removedContLen = true; + break; + case "transfer-encoding": + this._removedTE = true; + break; + case "date": + this.sendDate = false; + break; + } + + if (this[kOutHeaders] !== null) { + delete this[kOutHeaders][key]; + } +}; + +OutgoingMessage.prototype._implicitHeader = function _implicitHeader() { + throw new ERR_METHOD_NOT_IMPLEMENTED("_implicitHeader()"); +}; + +Object.defineProperty(OutgoingMessage.prototype, "headersSent", { + configurable: true, + enumerable: true, + get: function () { + return !!this._header; + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "writableEnded", { + get: function () { + return this.finished; + }, +}); + +Object.defineProperty(OutgoingMessage.prototype, "writableNeedDrain", { + get: function () { + return !this.destroyed && !this.finished && this[kNeedDrain]; + }, +}); + +// deno-lint-ignore camelcase +const crlf_buf = Buffer.from("\r\n"); +OutgoingMessage.prototype.write = function write( + // deno-lint-ignore no-explicit-any + chunk: any, + encoding: string | null, + callback: () => void, +) { + if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } + + const ret = write_(this, chunk, encoding, callback, false); + if (!ret) { + this[kNeedDrain] = true; + } + return ret; +}; + +// deno-lint-ignore no-explicit-any +function onError(msg: any, err: any, callback: any) { + const triggerAsyncId = msg.socket ? msg.socket[async_id_symbol] : undefined; + defaultTriggerAsyncIdScope( + triggerAsyncId, + // deno-lint-ignore no-explicit-any + (globalThis as any).process.nextTick, + emitErrorNt, + msg, + err, + callback, + ); +} + +// deno-lint-ignore no-explicit-any +function emitErrorNt(msg: any, err: any, callback: any) { + callback(err); + if (typeof msg.emit === "function" && !msg._closed) { + msg.emit("error", err); + } +} + +function write_( + // deno-lint-ignore no-explicit-any + msg: any, + // deno-lint-ignore no-explicit-any + chunk: any, + encoding: string | null, + // deno-lint-ignore no-explicit-any + callback: any, + // deno-lint-ignore no-explicit-any + fromEnd: any, +) { + if (typeof callback !== "function") { + callback = nop; + } + + let len; + if (chunk === null) { + throw new ERR_STREAM_NULL_VALUES(); + } else if (typeof chunk === "string") { + len = Buffer.byteLength(chunk, encoding); + } else if (isUint8Array(chunk)) { + len = chunk.length; + } else { + throw new ERR_INVALID_ARG_TYPE( + "chunk", + ["string", "Buffer", "Uint8Array"], + chunk, + ); + } + + let err; + if (msg.finished) { + err = new ERR_STREAM_WRITE_AFTER_END(); + } else if (msg.destroyed) { + err = new ERR_STREAM_DESTROYED("write"); + } + + if (err) { + if (!msg.destroyed) { + onError(msg, err, callback); + } else { + // deno-lint-ignore no-explicit-any + (globalThis as any).process.nextTick(callback, err); + } + return false; + } + + if (!msg._header) { + if (fromEnd) { + msg._contentLength = len; + } + msg._implicitHeader(); + } + + if (!msg._hasBody) { + debug( + "This type of response MUST NOT have a body. " + + "Ignoring write() calls.", + ); + // deno-lint-ignore no-explicit-any + (globalThis as any).process.nextTick(callback); + return true; + } + + if (!fromEnd && msg.socket && !msg.socket.writableCorked) { + msg.socket.cork(); + // deno-lint-ignore no-explicit-any + (globalThis as any).process.nextTick(connectionCorkNT, msg.socket); + } + + let ret; + if (msg.chunkedEncoding && chunk.length !== 0) { + msg._send(len.toString(16), "latin1", null); + msg._send(crlf_buf, null, null); + msg._send(chunk, encoding, null); + ret = msg._send(crlf_buf, null, callback); + } else { + ret = msg._send(chunk, encoding, callback); + } + + debug("write ret = " + ret); + return ret; +} + +// deno-lint-ignore no-explicit-any +function connectionCorkNT(conn: any) { + conn.uncork(); +} + +// deno-lint-ignore no-explicit-any +OutgoingMessage.prototype.addTrailers = function addTrailers(headers: any) { + this._trailer = ""; + const keys = Object.keys(headers); + const isArray = Array.isArray(headers); + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0, l = keys.length; i < l; i++) { + let field, value; + const key = keys[i]; + if (isArray) { + // deno-lint-ignore no-explicit-any + field = headers[key as any][0]; + // deno-lint-ignore no-explicit-any + value = headers[key as any][1]; + } else { + field = key; + value = headers[key]; + } + if (typeof field !== "string" || !field || !checkIsHttpToken(field)) { + throw new ERR_INVALID_HTTP_TOKEN("Trailer name", field); + } + if (checkInvalidHeaderChar(value)) { + debug('Trailer "%s" contains invalid characters', field); + throw new ERR_INVALID_CHAR("trailer content", field); + } + this._trailer += field + ": " + value + "\r\n"; + } +}; + +// deno-lint-ignore no-explicit-any +function onFinish(outmsg: any) { + if (outmsg && outmsg.socket && outmsg.socket._hadError) return; + outmsg.emit("finish"); +} + +OutgoingMessage.prototype.end = function end( + // deno-lint-ignore no-explicit-any + chunk: any, + // deno-lint-ignore no-explicit-any + encoding: any, + // deno-lint-ignore no-explicit-any + callback: any, +) { + if (typeof chunk === "function") { + callback = chunk; + chunk = null; + encoding = null; + } else if (typeof encoding === "function") { + callback = encoding; + encoding = null; + } + + if (chunk) { + if (this.finished) { + onError( + this, + new ERR_STREAM_WRITE_AFTER_END(), + typeof callback !== "function" ? nop : callback, + ); + return this; + } + + if (this.socket) { + this.socket.cork(); + } + + write_(this, chunk, encoding, null, true); + } else if (this.finished) { + if (typeof callback === "function") { + if (!this.writableFinished) { + this.on("finish", callback); + } else { + callback(new ERR_STREAM_ALREADY_FINISHED("end")); + } + } + return this; + } else if (!this._header) { + if (this.socket) { + this.socket.cork(); + } + + this._contentLength = 0; + this._implicitHeader(); + } + + if (typeof callback === "function") { + this.once("finish", callback); + } + + const finish = onFinish.bind(undefined, this); + + if (this._hasBody && this.chunkedEncoding) { + this._send("0\r\n" + this._trailer + "\r\n", "latin1", finish); + } else if (!this._headerSent || this.writableLength || chunk) { + this._send("", "latin1", finish); + } else { + // deno-lint-ignore no-explicit-any + (globalThis as any).process.nextTick(finish); + } + + if (this.socket) { + // Fully uncork connection on end(). + this.socket._writableState.corked = 1; + this.socket.uncork(); + } + this[kCorked] = 0; + + this.finished = true; + + // There is the first message on the outgoing queue, and we've sent + // everything to the socket. + debug("outgoing message end."); + if ( + this.outputData.length === 0 && + this.socket && + this.socket._httpMessage === this + ) { + this._finish(); + } + + return this; +}; + +OutgoingMessage.prototype._finish = function _finish() { + assert(this.socket); + this.emit("prefinish"); +}; + +// This logic is probably a bit confusing. Let me explain a bit: +// +// In both HTTP servers and clients it is possible to queue up several +// outgoing messages. This is easiest to imagine in the case of a client. +// Take the following situation: +// +// req1 = client.request('GET', '/'); +// req2 = client.request('POST', '/'); +// +// When the user does +// +// req2.write('hello world\n'); +// +// it's possible that the first request has not been completely flushed to +// the socket yet. Thus the outgoing messages need to be prepared to queue +// up data internally before sending it on further to the socket's queue. +// +// This function, outgoingFlush(), is called by both the Server and Client +// to attempt to flush any pending messages out to the socket. +OutgoingMessage.prototype._flush = function _flush() { + const socket = this.socket; + + if (socket && socket.writable) { + // There might be remaining data in this.output; write it out + const ret = this._flushOutput(socket); + + if (this.finished) { + // This is a queue to the server or client to bring in the next this. + this._finish(); + } else if (ret && this[kNeedDrain]) { + this[kNeedDrain] = false; + this.emit("drain"); + } + } +}; + +OutgoingMessage.prototype._flushOutput = function _flushOutput(socket: Socket) { + while (this[kCorked]) { + this[kCorked]--; + socket.cork(); + } + + const outputLength = this.outputData.length; + if (outputLength <= 0) { + return undefined; + } + + const outputData = this.outputData; + socket.cork(); + let ret; + // Retain for(;;) loop for performance reasons + // Refs: https://github.com/nodejs/node/pull/30958 + for (let i = 0; i < outputLength; i++) { + const { data, encoding, callback } = outputData[i]; + ret = socket.write(data, encoding, callback); + } + socket.uncork(); + + this.outputData = []; + this._onPendingData(-this.outputSize); + this.outputSize = 0; + + return ret; +}; + +OutgoingMessage.prototype.flushHeaders = function flushHeaders() { + if (!this._header) { + this._implicitHeader(); + } + + // Force-flush the headers. + this._send(""); +}; + +OutgoingMessage.prototype.pipe = function pipe() { + // OutgoingMessage should be write-only. Piping from it is disabled. + this.emit("error", new ERR_STREAM_CANNOT_PIPE()); +}; + +OutgoingMessage.prototype[EE.captureRejectionSymbol] = function ( + // deno-lint-ignore no-explicit-any + err: any, + // deno-lint-ignore no-explicit-any + _event: any, +) { + this.destroy(err); +}; + +export default { + validateHeaderName, + validateHeaderValue, + OutgoingMessage, +}; diff --git a/ext/node/polyfills/_next_tick.ts b/ext/node/polyfills/_next_tick.ts new file mode 100644 index 0000000000..7cbff0ea47 --- /dev/null +++ b/ext/node/polyfills/_next_tick.ts @@ -0,0 +1,173 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. + +// deno-lint-ignore-file no-inner-declarations + +import { core } from "internal:deno_node/polyfills/_core.ts"; +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { _exiting } from "internal:deno_node/polyfills/_process/exiting.ts"; +import { FixedQueue } from "internal:deno_node/polyfills/internal/fixed_queue.ts"; + +interface Tock { + callback: (...args: Array) => void; + args: Array; +} + +const queue = new FixedQueue(); + +// deno-lint-ignore no-explicit-any +let _nextTick: any; + +export function processTicksAndRejections() { + let tock; + do { + // deno-lint-ignore no-cond-assign + while (tock = queue.shift()) { + // FIXME(bartlomieju): Deno currently doesn't support async hooks + // const asyncId = tock[async_id_symbol]; + // emitBefore(asyncId, tock[trigger_async_id_symbol], tock); + + try { + const callback = (tock as Tock).callback; + if ((tock as Tock).args === undefined) { + callback(); + } else { + const args = (tock as Tock).args; + switch (args.length) { + case 1: + callback(args[0]); + break; + case 2: + callback(args[0], args[1]); + break; + case 3: + callback(args[0], args[1], args[2]); + break; + case 4: + callback(args[0], args[1], args[2], args[3]); + break; + default: + callback(...args); + } + } + } finally { + // FIXME(bartlomieju): Deno currently doesn't support async hooks + // if (destroyHooksExist()) + // emitDestroy(asyncId); + } + + // FIXME(bartlomieju): Deno currently doesn't support async hooks + // emitAfter(asyncId); + } + core.runMicrotasks(); + // FIXME(bartlomieju): Deno currently doesn't unhandled rejections + // } while (!queue.isEmpty() || processPromiseRejections()); + } while (!queue.isEmpty()); + core.setHasTickScheduled(false); + // FIXME(bartlomieju): Deno currently doesn't unhandled rejections + // setHasRejectionToWarn(false); +} + +if (typeof core.setNextTickCallback !== "undefined") { + function runNextTicks() { + // FIXME(bartlomieju): Deno currently doesn't unhandled rejections + // if (!hasTickScheduled() && !hasRejectionToWarn()) + // runMicrotasks(); + // if (!hasTickScheduled() && !hasRejectionToWarn()) + // return; + if (!core.hasTickScheduled()) { + core.runMicrotasks(); + } + if (!core.hasTickScheduled()) { + return true; + } + + processTicksAndRejections(); + return true; + } + + core.setNextTickCallback(processTicksAndRejections); + core.setMacrotaskCallback(runNextTicks); + + function __nextTickNative>( + this: unknown, + callback: (...args: T) => void, + ...args: T + ) { + validateFunction(callback, "callback"); + + if (_exiting) { + return; + } + + // TODO(bartlomieju): seems superfluous if we don't depend on `arguments` + let args_; + switch (args.length) { + case 0: + break; + case 1: + args_ = [args[0]]; + break; + case 2: + args_ = [args[0], args[1]]; + break; + case 3: + args_ = [args[0], args[1], args[2]]; + break; + default: + args_ = new Array(args.length); + for (let i = 0; i < args.length; i++) { + args_[i] = args[i]; + } + } + + if (queue.isEmpty()) { + core.setHasTickScheduled(true); + } + // FIXME(bartlomieju): Deno currently doesn't support async hooks + // const asyncId = newAsyncId(); + // const triggerAsyncId = getDefaultTriggerAsyncId(); + const tickObject = { + // FIXME(bartlomieju): Deno currently doesn't support async hooks + // [async_id_symbol]: asyncId, + // [trigger_async_id_symbol]: triggerAsyncId, + callback, + args: args_, + }; + // FIXME(bartlomieju): Deno currently doesn't support async hooks + // if (initHooksExist()) + // emitInit(asyncId, 'TickObject', triggerAsyncId, tickObject); + queue.push(tickObject); + } + _nextTick = __nextTickNative; +} else { + function __nextTickQueueMicrotask>( + this: unknown, + callback: (...args: T) => void, + ...args: T + ) { + if (args) { + queueMicrotask(() => callback.call(this, ...args)); + } else { + queueMicrotask(callback); + } + } + + _nextTick = __nextTickQueueMicrotask; +} + +// `nextTick()` will not enqueue any callback when the process is about to +// exit since the callback would not have a chance to be executed. +export function nextTick(this: unknown, callback: () => void): void; +export function nextTick>( + this: unknown, + callback: (...args: T) => void, + ...args: T +): void; +export function nextTick>( + this: unknown, + callback: (...args: T) => void, + ...args: T +) { + _nextTick(callback, ...args); +} diff --git a/ext/node/polyfills/_pako.mjs b/ext/node/polyfills/_pako.mjs new file mode 100644 index 0000000000..2447ef03bb --- /dev/null +++ b/ext/node/polyfills/_pako.mjs @@ -0,0 +1,7005 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +/*! pako 2.0.4 https://github.com/nodeca/pako @license (MIT AND Zlib) */ +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +// deno-lint-ignore-file + +import { TextDecoder, TextEncoder } from "internal:deno_web/08_text_encoding.js"; + +/* eslint-disable space-unary-ops */ + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + +//const Z_FILTERED = 1; +//const Z_HUFFMAN_ONLY = 2; +//const Z_RLE = 3; +const Z_FIXED$1 = 4; +//const Z_DEFAULT_STRATEGY = 0; + +/* Possible values of the data_type field (though see inflate()) */ +const Z_BINARY = 0; +const Z_TEXT = 1; +//const Z_ASCII = 1; // = Z_TEXT +const Z_UNKNOWN$1 = 2; + +/*============================================================================*/ + +function zero$1(buf) { + let len = buf.length; + while (--len >= 0) buf[len] = 0; +} + +// From zutil.h + +const STORED_BLOCK = 0; +const STATIC_TREES = 1; +const DYN_TREES = 2; +/* The three kinds of block type */ + +const MIN_MATCH$1 = 3; +const MAX_MATCH$1 = 258; +/* The minimum and maximum match lengths */ + +// From deflate.h +/* =========================================================================== + * Internal compression state. + */ + +const LENGTH_CODES$1 = 29; +/* number of length codes, not counting the special END_BLOCK code */ + +const LITERALS$1 = 256; +/* number of literal bytes 0..255 */ + +const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1; +/* number of Literal or Length codes, including the END_BLOCK code */ + +const D_CODES$1 = 30; +/* number of distance codes */ + +const BL_CODES$1 = 19; +/* number of codes used to transfer the bit lengths */ + +const HEAP_SIZE$1 = 2 * L_CODES$1 + 1; +/* maximum heap size */ + +const MAX_BITS$1 = 15; +/* All codes must not exceed MAX_BITS bits */ + +const Buf_size = 16; +/* size of bit buffer in bi_buf */ + +/* =========================================================================== + * Constants + */ + +const MAX_BL_BITS = 7; +/* Bit length codes must not exceed MAX_BL_BITS bits */ + +const END_BLOCK = 256; +/* end of block literal code */ + +const REP_3_6 = 16; +/* repeat previous bit length 3-6 times (2 bits of repeat count) */ + +const REPZ_3_10 = 17; +/* repeat a zero length 3-10 times (3 bits of repeat count) */ + +const REPZ_11_138 = 18; +/* repeat a zero length 11-138 times (7 bits of repeat count) */ + +/* eslint-disable comma-spacing,array-bracket-spacing */ +const extra_lbits = /* extra bits for each length code */ + new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 1, + 1, + 1, + 2, + 2, + 2, + 2, + 3, + 3, + 3, + 3, + 4, + 4, + 4, + 4, + 5, + 5, + 5, + 5, + 0, + ]); + +const extra_dbits = /* extra bits for each distance code */ + new Uint8Array([ + 0, + 0, + 0, + 0, + 1, + 1, + 2, + 2, + 3, + 3, + 4, + 4, + 5, + 5, + 6, + 6, + 7, + 7, + 8, + 8, + 9, + 9, + 10, + 10, + 11, + 11, + 12, + 12, + 13, + 13, + ]); + +const extra_blbits = /* extra bits for each bit length code */ + new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]); + +const bl_order = new Uint8Array([ + 16, + 17, + 18, + 0, + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, +]); +/* eslint-enable comma-spacing,array-bracket-spacing */ + +/* The lengths of the bit length codes are sent in order of decreasing + * probability, to avoid transmitting the lengths for unused bit length codes. + */ + +/* =========================================================================== + * Local data. These are initialized only once. + */ + +// We pre-fill arrays with 0 to avoid uninitialized gaps + +const DIST_CODE_LEN = 512; /* see definition of array dist_code below */ + +// !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1 +const static_ltree = new Array((L_CODES$1 + 2) * 2); +zero$1(static_ltree); +/* The static literal tree. Since the bit lengths are imposed, there is no + * need for the L_CODES extra codes used during heap construction. However + * The codes 286 and 287 are needed to build a canonical tree (see _tr_init + * below). + */ + +const static_dtree = new Array(D_CODES$1 * 2); +zero$1(static_dtree); +/* The static distance tree. (Actually a trivial tree since all codes use + * 5 bits.) + */ + +const _dist_code = new Array(DIST_CODE_LEN); +zero$1(_dist_code); +/* Distance codes. The first 256 values correspond to the distances + * 3 .. 258, the last 256 values correspond to the top 8 bits of + * the 15 bit distances. + */ + +const _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1); +zero$1(_length_code); +/* length code for each normalized match length (0 == MIN_MATCH) */ + +const base_length = new Array(LENGTH_CODES$1); +zero$1(base_length); +/* First normalized length for each code (0 = MIN_MATCH) */ + +const base_dist = new Array(D_CODES$1); +zero$1(base_dist); +/* First normalized distance for each code (0 = distance of 1) */ + +function StaticTreeDesc( + static_tree, + extra_bits, + extra_base, + elems, + max_length, +) { + this.static_tree = static_tree; /* static tree or NULL */ + this.extra_bits = extra_bits; /* extra bits for each code or NULL */ + this.extra_base = extra_base; /* base index for extra_bits */ + this.elems = elems; /* max number of elements in the tree */ + this.max_length = max_length; /* max bit length for the codes */ + + // show if `static_tree` has data or dummy - needed for monomorphic objects + this.has_stree = static_tree && static_tree.length; +} + +let static_l_desc; +let static_d_desc; +let static_bl_desc; + +function TreeDesc(dyn_tree, stat_desc) { + this.dyn_tree = dyn_tree; /* the dynamic tree */ + this.max_code = 0; /* largest code with non zero frequency */ + this.stat_desc = stat_desc; /* the corresponding static tree */ +} + +const d_code = (dist) => { + return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)]; +}; + +/* =========================================================================== + * Output a short LSB first on the stream. + * IN assertion: there is enough room in pendingBuf. + */ +const put_short = (s, w) => { + // put_byte(s, (uch)((w) & 0xff)); + // put_byte(s, (uch)((ush)(w) >> 8)); + s.pending_buf[s.pending++] = (w) & 0xff; + s.pending_buf[s.pending++] = (w >>> 8) & 0xff; +}; + +/* =========================================================================== + * Send a value on a given number of bits. + * IN assertion: length <= 16 and value fits in length bits. + */ +const send_bits = (s, value, length) => { + if (s.bi_valid > (Buf_size - length)) { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + put_short(s, s.bi_buf); + s.bi_buf = value >> (Buf_size - s.bi_valid); + s.bi_valid += length - Buf_size; + } else { + s.bi_buf |= (value << s.bi_valid) & 0xffff; + s.bi_valid += length; + } +}; + +const send_code = (s, c, tree) => { + send_bits(s, tree[c * 2], /*.Code*/ tree[c * 2 + 1] /*.Len*/); +}; + +/* =========================================================================== + * Reverse the first len bits of a code, using straightforward code (a faster + * method would use a table) + * IN assertion: 1 <= len <= 15 + */ +const bi_reverse = (code, len) => { + let res = 0; + do { + res |= code & 1; + code >>>= 1; + res <<= 1; + } while (--len > 0); + return res >>> 1; +}; + +/* =========================================================================== + * Flush the bit buffer, keeping at most 7 bits in it. + */ +const bi_flush = (s) => { + if (s.bi_valid === 16) { + put_short(s, s.bi_buf); + s.bi_buf = 0; + s.bi_valid = 0; + } else if (s.bi_valid >= 8) { + s.pending_buf[s.pending++] = s.bi_buf & 0xff; + s.bi_buf >>= 8; + s.bi_valid -= 8; + } +}; + +/* =========================================================================== + * Compute the optimal bit lengths for a tree and update the total bit length + * for the current block. + * IN assertion: the fields freq and dad are set, heap[heap_max] and + * above are the tree nodes sorted by increasing frequency. + * OUT assertions: the field len is set to the optimal bit length, the + * array bl_count contains the frequencies for each bit length. + * The length opt_len is updated; static_len is also updated if stree is + * not null. + */ +const gen_bitlen = (s, desc) => // deflate_state *s; +// tree_desc *desc; /* the tree descriptor */ +{ + const tree = desc.dyn_tree; + const max_code = desc.max_code; + const stree = desc.stat_desc.static_tree; + const has_stree = desc.stat_desc.has_stree; + const extra = desc.stat_desc.extra_bits; + const base = desc.stat_desc.extra_base; + const max_length = desc.stat_desc.max_length; + let h; /* heap index */ + let n, m; /* iterate over the tree elements */ + let bits; /* bit length */ + let xbits; /* extra bits */ + let f; /* frequency */ + let overflow = 0; /* number of elements with bit length too large */ + + for (bits = 0; bits <= MAX_BITS$1; bits++) { + s.bl_count[bits] = 0; + } + + /* In a first pass, compute the optimal bit lengths (which may + * overflow in the case of the bit length tree). + */ + tree[s.heap[s.heap_max] * 2 + 1] /*.Len*/ = 0; /* root of the heap */ + + for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) { + n = s.heap[h]; + bits = tree[tree[n * 2 + 1] /*.Dad*/ * 2 + 1] /*.Len*/ + 1; + if (bits > max_length) { + bits = max_length; + overflow++; + } + tree[n * 2 + 1] /*.Len*/ = bits; + /* We overwrite tree[n].Dad which is no longer needed */ + + if (n > max_code) continue; /* not a leaf node */ + + s.bl_count[bits]++; + xbits = 0; + if (n >= base) { + xbits = extra[n - base]; + } + f = tree[n * 2] /*.Freq*/; + s.opt_len += f * (bits + xbits); + if (has_stree) { + s.static_len += f * (stree[n * 2 + 1] /*.Len*/ + xbits); + } + } + if (overflow === 0) return; + + // Trace((stderr,"\nbit length overflow\n")); + /* This happens for example on obj2 and pic of the Calgary corpus */ + + /* Find the first bit length which could increase: */ + do { + bits = max_length - 1; + while (s.bl_count[bits] === 0) bits--; + s.bl_count[bits]--; /* move one leaf down the tree */ + s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */ + s.bl_count[max_length]--; + /* The brother of the overflow item also moves one step up, + * but this does not affect bl_count[max_length] + */ + overflow -= 2; + } while (overflow > 0); + + /* Now recompute all bit lengths, scanning in increasing frequency. + * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all + * lengths instead of fixing only the wrong ones. This idea is taken + * from "internal:deno_node/polyfills/ar" written by Haruhiko Okumura.) + */ + for (bits = max_length; bits !== 0; bits--) { + n = s.bl_count[bits]; + while (n !== 0) { + m = s.heap[--h]; + if (m > max_code) continue; + if (tree[m * 2 + 1] /*.Len*/ !== bits) { + // Trace((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits)); + s.opt_len += (bits - tree[m * 2 + 1] /*.Len*/) * tree[m * 2] /*.Freq*/; + tree[m * 2 + 1] /*.Len*/ = bits; + } + n--; + } + } +}; + +/* =========================================================================== + * Generate the codes for a given tree and bit counts (which need not be + * optimal). + * IN assertion: the array bl_count contains the bit length statistics for + * the given tree and the field len is set for all tree elements. + * OUT assertion: the field code is set for all tree elements of non + * zero code length. + */ +const gen_codes = (tree, max_code, bl_count) => // ct_data *tree; /* the tree to decorate */ +// int max_code; /* largest code with non zero frequency */ +// ushf *bl_count; /* number of codes at each bit length */ +{ + const next_code = new Array( + MAX_BITS$1 + 1, + ); /* next code value for each bit length */ + let code = 0; /* running code value */ + let bits; /* bit index */ + let n; /* code index */ + + /* The distribution counts are first used to generate the code values + * without bit reversal. + */ + for (bits = 1; bits <= MAX_BITS$1; bits++) { + next_code[bits] = code = (code + bl_count[bits - 1]) << 1; + } + /* Check that the bit counts in bl_count are consistent. The last code + * must be all ones. + */ + //Assert (code + bl_count[MAX_BITS]-1 == (1< { + let n; /* iterates over tree elements */ + let bits; /* bit counter */ + let length; /* length value */ + let code; /* code value */ + let dist; /* distance index */ + const bl_count = new Array(MAX_BITS$1 + 1); + /* number of codes at each bit length for an optimal tree */ + + // do check in _tr_init() + //if (static_init_done) return; + + /* For some embedded targets, global variables are not initialized: */ + /*#ifdef NO_INIT_GLOBAL_POINTERS + static_l_desc.static_tree = static_ltree; + static_l_desc.extra_bits = extra_lbits; + static_d_desc.static_tree = static_dtree; + static_d_desc.extra_bits = extra_dbits; + static_bl_desc.extra_bits = extra_blbits; +#endif*/ + + /* Initialize the mapping length (0..255) -> length code (0..28) */ + length = 0; + for (code = 0; code < LENGTH_CODES$1 - 1; code++) { + base_length[code] = length; + for (n = 0; n < (1 << extra_lbits[code]); n++) { + _length_code[length++] = code; + } + } + //Assert (length == 256, "tr_static_init: length != 256"); + /* Note that the length 255 (match length 258) can be represented + * in two different ways: code 284 + 5 bits or code 285, so we + * overwrite length_code[255] to use the best encoding: + */ + _length_code[length - 1] = code; + + /* Initialize the mapping dist (0..32K) -> dist code (0..29) */ + dist = 0; + for (code = 0; code < 16; code++) { + base_dist[code] = dist; + for (n = 0; n < (1 << extra_dbits[code]); n++) { + _dist_code[dist++] = code; + } + } + //Assert (dist == 256, "tr_static_init: dist != 256"); + dist >>= 7; /* from now on, all distances are divided by 128 */ + for (; code < D_CODES$1; code++) { + base_dist[code] = dist << 7; + for (n = 0; n < (1 << (extra_dbits[code] - 7)); n++) { + _dist_code[256 + dist++] = code; + } + } + //Assert (dist == 256, "tr_static_init: 256+dist != 512"); + + /* Construct the codes of the static literal tree */ + for (bits = 0; bits <= MAX_BITS$1; bits++) { + bl_count[bits] = 0; + } + + n = 0; + while (n <= 143) { + static_ltree[n * 2 + 1] /*.Len*/ = 8; + n++; + bl_count[8]++; + } + while (n <= 255) { + static_ltree[n * 2 + 1] /*.Len*/ = 9; + n++; + bl_count[9]++; + } + while (n <= 279) { + static_ltree[n * 2 + 1] /*.Len*/ = 7; + n++; + bl_count[7]++; + } + while (n <= 287) { + static_ltree[n * 2 + 1] /*.Len*/ = 8; + n++; + bl_count[8]++; + } + /* Codes 286 and 287 do not exist, but we must include them in the + * tree construction to get a canonical Huffman tree (longest code + * all ones) + */ + gen_codes(static_ltree, L_CODES$1 + 1, bl_count); + + /* The static distance tree is trivial: */ + for (n = 0; n < D_CODES$1; n++) { + static_dtree[n * 2 + 1] /*.Len*/ = 5; + static_dtree[n * 2] /*.Code*/ = bi_reverse(n, 5); + } + + // Now data ready and we can init static trees + static_l_desc = new StaticTreeDesc( + static_ltree, + extra_lbits, + LITERALS$1 + 1, + L_CODES$1, + MAX_BITS$1, + ); + static_d_desc = new StaticTreeDesc( + static_dtree, + extra_dbits, + 0, + D_CODES$1, + MAX_BITS$1, + ); + static_bl_desc = new StaticTreeDesc( + new Array(0), + extra_blbits, + 0, + BL_CODES$1, + MAX_BL_BITS, + ); + + //static_init_done = true; +}; + +/* =========================================================================== + * Initialize a new block. + */ +const init_block = (s) => { + let n; /* iterates over tree elements */ + + /* Initialize the trees. */ + for (n = 0; n < L_CODES$1; n++) s.dyn_ltree[n * 2] /*.Freq*/ = 0; + for (n = 0; n < D_CODES$1; n++) s.dyn_dtree[n * 2] /*.Freq*/ = 0; + for (n = 0; n < BL_CODES$1; n++) s.bl_tree[n * 2] /*.Freq*/ = 0; + + s.dyn_ltree[END_BLOCK * 2] /*.Freq*/ = 1; + s.opt_len = s.static_len = 0; + s.last_lit = s.matches = 0; +}; + +/* =========================================================================== + * Flush the bit buffer and align the output on a byte boundary + */ +const bi_windup = (s) => { + if (s.bi_valid > 8) { + put_short(s, s.bi_buf); + } else if (s.bi_valid > 0) { + //put_byte(s, (Byte)s->bi_buf); + s.pending_buf[s.pending++] = s.bi_buf; + } + s.bi_buf = 0; + s.bi_valid = 0; +}; + +/* =========================================================================== + * Copy a stored block, storing first the length and its + * one's complement if requested. + */ +const copy_block = (s, buf, len, header) => //DeflateState *s; +//charf *buf; /* the input data */ +//unsigned len; /* its length */ +//int header; /* true if block header must be written */ +{ + bi_windup(s); /* align on byte boundary */ + + if (header) { + put_short(s, len); + put_short(s, ~len); + } + // while (len--) { + // put_byte(s, *buf++); + // } + s.pending_buf.set(s.window.subarray(buf, buf + len), s.pending); + s.pending += len; +}; + +/* =========================================================================== + * Compares to subtrees, using the tree depth as tie breaker when + * the subtrees have equal frequency. This minimizes the worst case length. + */ +const smaller = (tree, n, m, depth) => { + const _n2 = n * 2; + const _m2 = m * 2; + return (tree[_n2] /*.Freq*/ < tree[_m2] /*.Freq*/ || + (tree[_n2] /*.Freq*/ === tree[_m2] /*.Freq*/ && depth[n] <= depth[m])); +}; + +/* =========================================================================== + * Restore the heap property by moving down the tree starting at node k, + * exchanging a node with the smallest of its two sons if necessary, stopping + * when the heap property is re-established (each father smaller than its + * two sons). + */ +const pqdownheap = (s, tree, k) => // deflate_state *s; +// ct_data *tree; /* the tree to restore */ +// int k; /* node to move down */ +{ + const v = s.heap[k]; + let j = k << 1; /* left son of k */ + while (j <= s.heap_len) { + /* Set j to the smallest of the two sons: */ + if ( + j < s.heap_len && + smaller(tree, s.heap[j + 1], s.heap[j], s.depth) + ) { + j++; + } + /* Exit if v is smaller than both sons */ + if (smaller(tree, v, s.heap[j], s.depth)) break; + + /* Exchange v with the smallest son */ + s.heap[k] = s.heap[j]; + k = j; + + /* And continue down the tree, setting j to the left son of k */ + j <<= 1; + } + s.heap[k] = v; +}; + +// inlined manually +// const SMALLEST = 1; + +/* =========================================================================== + * Send the block data compressed using the given Huffman trees + */ +const compress_block = (s, ltree, dtree) => // deflate_state *s; +// const ct_data *ltree; /* literal tree */ +// const ct_data *dtree; /* distance tree */ +{ + let dist; /* distance of matched string */ + let lc; /* match length or unmatched char (if dist == 0) */ + let lx = 0; /* running index in l_buf */ + let code; /* the code to send */ + let extra; /* number of extra bits to send */ + + if (s.last_lit !== 0) { + do { + dist = (s.pending_buf[s.d_buf + lx * 2] << 8) | + (s.pending_buf[s.d_buf + lx * 2 + 1]); + lc = s.pending_buf[s.l_buf + lx]; + lx++; + + if (dist === 0) { + send_code(s, lc, ltree); /* send a literal byte */ + //Tracecv(isgraph(lc), (stderr," '%c' ", lc)); + } else { + /* Here, lc is the match length - MIN_MATCH */ + code = _length_code[lc]; + send_code(s, code + LITERALS$1 + 1, ltree); /* send the length code */ + extra = extra_lbits[code]; + if (extra !== 0) { + lc -= base_length[code]; + send_bits(s, lc, extra); /* send the extra length bits */ + } + dist--; /* dist is now the match distance - 1 */ + code = d_code(dist); + //Assert (code < D_CODES, "bad d_code"); + + send_code(s, code, dtree); /* send the distance code */ + extra = extra_dbits[code]; + if (extra !== 0) { + dist -= base_dist[code]; + send_bits(s, dist, extra); /* send the extra distance bits */ + } + } /* literal or match pair ? */ + + /* Check that the overlay between pending_buf and d_buf+l_buf is ok: */ + //Assert((uInt)(s->pending) < s->lit_bufsize + 2*lx, + // "pendingBuf overflow"); + } while (lx < s.last_lit); + } + + send_code(s, END_BLOCK, ltree); +}; + +/* =========================================================================== + * Construct one Huffman tree and assigns the code bit strings and lengths. + * Update the total bit length for the current block. + * IN assertion: the field freq is set for all tree elements. + * OUT assertions: the fields len and code are set to the optimal bit length + * and corresponding code. The length opt_len is updated; static_len is + * also updated if stree is not null. The field max_code is set. + */ +const build_tree = (s, desc) => // deflate_state *s; +// tree_desc *desc; /* the tree descriptor */ +{ + const tree = desc.dyn_tree; + const stree = desc.stat_desc.static_tree; + const has_stree = desc.stat_desc.has_stree; + const elems = desc.stat_desc.elems; + let n, m; /* iterate over heap elements */ + let max_code = -1; /* largest code with non zero frequency */ + let node; /* new node being created */ + + /* Construct the initial heap, with least frequent element in + * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1]. + * heap[0] is not used. + */ + s.heap_len = 0; + s.heap_max = HEAP_SIZE$1; + + for (n = 0; n < elems; n++) { + if (tree[n * 2] /*.Freq*/ !== 0) { + s.heap[++s.heap_len] = max_code = n; + s.depth[n] = 0; + } else { + tree[n * 2 + 1] /*.Len*/ = 0; + } + } + + /* The pkzip format requires that at least one distance code exists, + * and that at least one bit should be sent even if there is only one + * possible code. So to avoid special checks later on we force at least + * two codes of non zero frequency. + */ + while (s.heap_len < 2) { + node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0; + tree[node * 2] /*.Freq*/ = 1; + s.depth[node] = 0; + s.opt_len--; + + if (has_stree) { + s.static_len -= stree[node * 2 + 1] /*.Len*/; + } + /* node is 0 or 1 so it does not have extra bits */ + } + desc.max_code = max_code; + + /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree, + * establish sub-heaps of increasing lengths: + */ + for (n = s.heap_len >> 1 /*int /2*/; n >= 1; n--) pqdownheap(s, tree, n); + + /* Construct the Huffman tree by repeatedly combining the least two + * frequent nodes. + */ + node = elems; /* next internal node of the tree */ + do { + //pqremove(s, tree, n); /* n = node of least frequency */ + /*** pqremove ***/ + n = s.heap[1 /*SMALLEST*/]; + s.heap[1 /*SMALLEST*/] = s.heap[s.heap_len--]; + pqdownheap(s, tree, 1 /*SMALLEST*/); + /***/ + + m = s.heap[1 /*SMALLEST*/]; /* m = node of next least frequency */ + + s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */ + s.heap[--s.heap_max] = m; + + /* Create a new node father of n and m */ + tree[node * 2] /*.Freq*/ = tree[n * 2] /*.Freq*/ + tree[m * 2] /*.Freq*/; + s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1; + tree[n * 2 + 1] /*.Dad*/ = tree[m * 2 + 1] /*.Dad*/ = node; + + /* and insert the new node in the heap */ + s.heap[1 /*SMALLEST*/] = node++; + pqdownheap(s, tree, 1 /*SMALLEST*/); + } while (s.heap_len >= 2); + + s.heap[--s.heap_max] = s.heap[1 /*SMALLEST*/]; + + /* At this point, the fields freq and dad are set. We can now + * generate the bit lengths. + */ + gen_bitlen(s, desc); + + /* The field len is now set, we can generate the bit codes */ + gen_codes(tree, max_code, s.bl_count); +}; + +/* =========================================================================== + * Scan a literal or distance tree to determine the frequencies of the codes + * in the bit length tree. + */ +const scan_tree = (s, tree, max_code) => // deflate_state *s; +// ct_data *tree; /* the tree to be scanned */ +// int max_code; /* and its largest code of non zero frequency */ +{ + let n; /* iterates over all tree elements */ + let prevlen = -1; /* last emitted length */ + let curlen; /* length of current code */ + + let nextlen = tree[0 * 2 + 1] /*.Len*/; /* length of next code */ + + let count = 0; /* repeat count of the current code */ + let max_count = 7; /* max repeat count */ + let min_count = 4; /* min repeat count */ + + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } + tree[(max_code + 1) * 2 + 1] /*.Len*/ = 0xffff; /* guard */ + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1] /*.Len*/; + + if (++count < max_count && curlen === nextlen) { + continue; + } else if (count < min_count) { + s.bl_tree[curlen * 2] /*.Freq*/ += count; + } else if (curlen !== 0) { + if (curlen !== prevlen) s.bl_tree[curlen * 2] /*.Freq*/++; + s.bl_tree[REP_3_6 * 2] /*.Freq*/++; + } else if (count <= 10) { + s.bl_tree[REPZ_3_10 * 2] /*.Freq*/++; + } else { + s.bl_tree[REPZ_11_138 * 2] /*.Freq*/++; + } + + count = 0; + prevlen = curlen; + + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } else if (curlen === nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } +}; + +/* =========================================================================== + * Send a literal or distance tree in compressed form, using the codes in + * bl_tree. + */ +const send_tree = (s, tree, max_code) => // deflate_state *s; +// ct_data *tree; /* the tree to be scanned */ +// int max_code; /* and its largest code of non zero frequency */ +{ + let n; /* iterates over all tree elements */ + let prevlen = -1; /* last emitted length */ + let curlen; /* length of current code */ + + let nextlen = tree[0 * 2 + 1] /*.Len*/; /* length of next code */ + + let count = 0; /* repeat count of the current code */ + let max_count = 7; /* max repeat count */ + let min_count = 4; /* min repeat count */ + + /* tree[max_code+1].Len = -1; */ + /* guard already set */ + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } + + for (n = 0; n <= max_code; n++) { + curlen = nextlen; + nextlen = tree[(n + 1) * 2 + 1] /*.Len*/; + + if (++count < max_count && curlen === nextlen) { + continue; + } else if (count < min_count) { + do { + send_code(s, curlen, s.bl_tree); + } while (--count !== 0); + } else if (curlen !== 0) { + if (curlen !== prevlen) { + send_code(s, curlen, s.bl_tree); + count--; + } + //Assert(count >= 3 && count <= 6, " 3_6?"); + send_code(s, REP_3_6, s.bl_tree); + send_bits(s, count - 3, 2); + } else if (count <= 10) { + send_code(s, REPZ_3_10, s.bl_tree); + send_bits(s, count - 3, 3); + } else { + send_code(s, REPZ_11_138, s.bl_tree); + send_bits(s, count - 11, 7); + } + + count = 0; + prevlen = curlen; + if (nextlen === 0) { + max_count = 138; + min_count = 3; + } else if (curlen === nextlen) { + max_count = 6; + min_count = 3; + } else { + max_count = 7; + min_count = 4; + } + } +}; + +/* =========================================================================== + * Construct the Huffman tree for the bit lengths and return the index in + * bl_order of the last bit length code to send. + */ +const build_bl_tree = (s) => { + let max_blindex; /* index of last bit length code of non zero freq */ + + /* Determine the bit length frequencies for literal and distance trees */ + scan_tree(s, s.dyn_ltree, s.l_desc.max_code); + scan_tree(s, s.dyn_dtree, s.d_desc.max_code); + + /* Build the bit length tree: */ + build_tree(s, s.bl_desc); + /* opt_len now includes the length of the tree representations, except + * the lengths of the bit lengths codes and the 5+5+4 bits for the counts. + */ + + /* Determine the number of bit length codes to send. The pkzip format + * requires that at least 4 bit length codes be sent. (appnote.txt says + * 3 but the actual value used is 4.) + */ + for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) { + if (s.bl_tree[bl_order[max_blindex] * 2 + 1] /*.Len*/ !== 0) { + break; + } + } + /* Update opt_len to include the bit length tree and counts */ + s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4; + //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld", + // s->opt_len, s->static_len)); + + return max_blindex; +}; + +/* =========================================================================== + * Send the header for a block using dynamic Huffman trees: the counts, the + * lengths of the bit length codes, the literal tree and the distance tree. + * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4. + */ +const send_all_trees = (s, lcodes, dcodes, blcodes) => // deflate_state *s; +// int lcodes, dcodes, blcodes; /* number of codes for each tree */ +{ + let rank; /* index in bl_order */ + + //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes"); + //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES, + // "too many codes"); + //Tracev((stderr, "\nbl counts: ")); + send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */ + send_bits(s, dcodes - 1, 5); + send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */ + for (rank = 0; rank < blcodes; rank++) { + //Tracev((stderr, "\nbl code %2d ", bl_order[rank])); + send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1], /*.Len*/ 3); + } + //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */ + //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent)); + + send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */ + //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent)); +}; + +/* =========================================================================== + * Check if the data type is TEXT or BINARY, using the following algorithm: + * - TEXT if the two conditions below are satisfied: + * a) There are no non-portable control characters belonging to the + * "black list" (0..6, 14..25, 28..31). + * b) There is at least one printable character belonging to the + * "white list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255). + * - BINARY otherwise. + * - The following partially-portable control characters form a + * "gray list" that is ignored in this detection algorithm: + * (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}). + * IN assertion: the fields Freq of dyn_ltree are set. + */ +const detect_data_type = (s) => { + /* black_mask is the bit mask of black-listed bytes + * set bits 0..6, 14..25, and 28..31 + * 0xf3ffc07f = binary 11110011111111111100000001111111 + */ + let black_mask = 0xf3ffc07f; + let n; + + /* Check for non-textual ("black-listed") bytes. */ + for (n = 0; n <= 31; n++, black_mask >>>= 1) { + if ((black_mask & 1) && (s.dyn_ltree[n * 2] /*.Freq*/ !== 0)) { + return Z_BINARY; + } + } + + /* Check for textual ("white-listed") bytes. */ + if ( + s.dyn_ltree[9 * 2] /*.Freq*/ !== 0 || s.dyn_ltree[10 * 2] /*.Freq*/ !== 0 || + s.dyn_ltree[13 * 2] /*.Freq*/ !== 0 + ) { + return Z_TEXT; + } + for (n = 32; n < LITERALS$1; n++) { + if (s.dyn_ltree[n * 2] /*.Freq*/ !== 0) { + return Z_TEXT; + } + } + + /* There are no "black-listed" or "white-listed" bytes: + * this stream either is empty or has tolerated ("gray-listed") bytes only. + */ + return Z_BINARY; +}; + +let static_init_done = false; + +/* =========================================================================== + * Initialize the tree data structures for a new zlib stream. + */ +const _tr_init$1 = (s) => { + if (!static_init_done) { + tr_static_init(); + static_init_done = true; + } + + s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc); + s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc); + s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc); + + s.bi_buf = 0; + s.bi_valid = 0; + + /* Initialize the first block of the first file: */ + init_block(s); +}; + +/* =========================================================================== + * Send a stored block + */ +const _tr_stored_block$1 = (s, buf, stored_len, last) => //DeflateState *s; +//charf *buf; /* input block */ +//ulg stored_len; /* length of input block */ +//int last; /* one if this is the last block for a file */ +{ + send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */ + copy_block(s, buf, stored_len, true); /* with header */ +}; + +/* =========================================================================== + * Send one empty static block to give enough lookahead for inflate. + * This takes 10 bits, of which 7 may remain in the bit buffer. + */ +const _tr_align$1 = (s) => { + send_bits(s, STATIC_TREES << 1, 3); + send_code(s, END_BLOCK, static_ltree); + bi_flush(s); +}; + +/* =========================================================================== + * Determine the best encoding for the current block: dynamic trees, static + * trees or store, and output the encoded block to the zip file. + */ +const _tr_flush_block$1 = (s, buf, stored_len, last) => //DeflateState *s; +//charf *buf; /* input block, or NULL if too old */ +//ulg stored_len; /* length of input block */ +//int last; /* one if this is the last block for a file */ +{ + let opt_lenb, static_lenb; /* opt_len and static_len in bytes */ + let max_blindex = 0; /* index of last bit length code of non zero freq */ + + /* Build the Huffman trees unless a stored block is forced */ + if (s.level > 0) { + /* Check if the file is binary or text */ + if (s.strm.data_type === Z_UNKNOWN$1) { + s.strm.data_type = detect_data_type(s); + } + + /* Construct the literal and distance trees */ + build_tree(s, s.l_desc); + // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + + build_tree(s, s.d_desc); + // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len, + // s->static_len)); + /* At this point, opt_len and static_len are the total bit lengths of + * the compressed block data, excluding the tree representations. + */ + + /* Build the bit length tree for the above two trees, and get the index + * in bl_order of the last bit length code to send. + */ + max_blindex = build_bl_tree(s); + + /* Determine the best encoding. Compute the block lengths in bytes. */ + opt_lenb = (s.opt_len + 3 + 7) >>> 3; + static_lenb = (s.static_len + 3 + 7) >>> 3; + + // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ", + // opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len, + // s->last_lit)); + + if (static_lenb <= opt_lenb) opt_lenb = static_lenb; + } else { + // Assert(buf != (char*)0, "lost buf"); + opt_lenb = static_lenb = stored_len + 5; /* force a stored block */ + } + + if ((stored_len + 4 <= opt_lenb) && (buf !== -1)) { + /* 4: two words for the lengths */ + + /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE. + * Otherwise we can't have processed more than WSIZE input bytes since + * the last block flush, because compression would have been + * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to + * transform a block into a stored block. + */ + _tr_stored_block$1(s, buf, stored_len, last); + } else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) { + send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3); + compress_block(s, static_ltree, static_dtree); + } else { + send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3); + send_all_trees( + s, + s.l_desc.max_code + 1, + s.d_desc.max_code + 1, + max_blindex + 1, + ); + compress_block(s, s.dyn_ltree, s.dyn_dtree); + } + // Assert (s->compressed_len == s->bits_sent, "bad compressed size"); + /* The above check is made mod 2^32, for files larger than 512 MB + * and uLong implemented on 32 bits. + */ + init_block(s); + + if (last) { + bi_windup(s); + } + // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3, + // s->compressed_len-7*last)); +}; + +/* =========================================================================== + * Save the match info and tally the frequency counts. Return true if + * the current block must be flushed. + */ +const _tr_tally$1 = (s, dist, lc) => // deflate_state *s; +// unsigned dist; /* distance of matched string */ +// unsigned lc; /* match length-MIN_MATCH or unmatched char (if dist==0) */ +{ + //let out_length, in_length, dcode; + + s.pending_buf[s.d_buf + s.last_lit * 2] = (dist >>> 8) & 0xff; + s.pending_buf[s.d_buf + s.last_lit * 2 + 1] = dist & 0xff; + + s.pending_buf[s.l_buf + s.last_lit] = lc & 0xff; + s.last_lit++; + + if (dist === 0) { + /* lc is the unmatched char */ + s.dyn_ltree[lc * 2] /*.Freq*/++; + } else { + s.matches++; + /* Here, lc is the match length - MIN_MATCH */ + dist--; /* dist = match distance - 1 */ + //Assert((ush)dist < (ush)MAX_DIST(s) && + // (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) && + // (ush)d_code(dist) < (ush)D_CODES, "_tr_tally: bad match"); + + s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2] /*.Freq*/++; + s.dyn_dtree[d_code(dist) * 2] /*.Freq*/++; + } + + // (!) This block is disabled in zlib defaults, + // don't enable it for binary compatibility + + //#ifdef TRUNCATE_BLOCK + // /* Try to guess if it is profitable to stop the current block here */ + // if ((s.last_lit & 0x1fff) === 0 && s.level > 2) { + // /* Compute an upper bound for the compressed length */ + // out_length = s.last_lit*8; + // in_length = s.strstart - s.block_start; + // + // for (dcode = 0; dcode < D_CODES; dcode++) { + // out_length += s.dyn_dtree[dcode*2]/*.Freq*/ * (5 + extra_dbits[dcode]); + // } + // out_length >>>= 3; + // //Tracev((stderr,"\nlast_lit %u, in %ld, out ~%ld(%ld%%) ", + // // s->last_lit, in_length, out_length, + // // 100L - out_length*100L/in_length)); + // if (s.matches < (s.last_lit>>1)/*int /2*/ && out_length < (in_length>>1)/*int /2*/) { + // return true; + // } + // } + //#endif + + return (s.last_lit === s.lit_bufsize - 1); + /* We avoid equality with lit_bufsize because of wraparound at 64K + * on 16 bit machines and because stored blocks are restricted to + * 64K-1 bytes. + */ +}; + +var _tr_init_1 = _tr_init$1; +var _tr_stored_block_1 = _tr_stored_block$1; +var _tr_flush_block_1 = _tr_flush_block$1; +var _tr_tally_1 = _tr_tally$1; +var _tr_align_1 = _tr_align$1; + +var trees = { + _tr_init: _tr_init_1, + _tr_stored_block: _tr_stored_block_1, + _tr_flush_block: _tr_flush_block_1, + _tr_tally: _tr_tally_1, + _tr_align: _tr_align_1, +}; + +// Note: adler32 takes 12% for level 0 and 2% for level 6. +// It isn't worth it to make additional optimizations as in original. +// Small size is preferable. + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +const adler32 = (adler, buf, len, pos) => { + let s1 = (adler & 0xffff) | 0, + s2 = ((adler >>> 16) & 0xffff) | 0, + n = 0; + + while (len !== 0) { + // Set limit ~ twice less than 5552, to keep + // s2 in 31-bits, because we force signed ints. + // in other case %= will fail. + n = len > 2000 ? 2000 : len; + len -= n; + + do { + s1 = (s1 + buf[pos++]) | 0; + s2 = (s2 + s1) | 0; + } while (--n); + + s1 %= 65521; + s2 %= 65521; + } + + return (s1 | (s2 << 16)) | 0; +}; + +var adler32_1 = adler32; + +// Note: we can't get significant speed boost here. +// So write code to minimize size - no pregenerated tables +// and array tools dependencies. + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +// Use ordinary array, since untyped makes no boost here +const makeTable = () => { + let c, table = []; + + for (var n = 0; n < 256; n++) { + c = n; + for (var k = 0; k < 8; k++) { + c = (c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1); + } + table[n] = c; + } + + return table; +}; + +// Create table on load. Just 255 signed longs. Not a problem. +const crcTable = new Uint32Array(makeTable()); + +const crc32 = (crc, buf, len, pos) => { + const t = crcTable; + const end = pos + len; + + crc ^= -1; + + for (let i = pos; i < end; i++) { + crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF]; + } + + return (crc ^ (-1)); // >>> 0; +}; + +var crc32_1 = crc32; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +var messages = { + 2: "need dictionary", /* Z_NEED_DICT 2 */ + 1: "stream end", /* Z_STREAM_END 1 */ + 0: "", /* Z_OK 0 */ + "-1": "file error", /* Z_ERRNO (-1) */ + "-2": "stream error", /* Z_STREAM_ERROR (-2) */ + "-3": "data error", /* Z_DATA_ERROR (-3) */ + "-4": "insufficient memory", /* Z_MEM_ERROR (-4) */ + "-5": "buffer error", /* Z_BUF_ERROR (-5) */ + "-6": "incompatible version", /* Z_VERSION_ERROR (-6) */ +}; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +var constants$2 = { + /* Allowed flush values; see deflate() and inflate() below for details */ + Z_NO_FLUSH: 0, + Z_PARTIAL_FLUSH: 1, + Z_SYNC_FLUSH: 2, + Z_FULL_FLUSH: 3, + Z_FINISH: 4, + Z_BLOCK: 5, + Z_TREES: 6, + + /* Return codes for the compression/decompression functions. Negative values + * are errors, positive values are used for special but normal events. + */ + Z_OK: 0, + Z_STREAM_END: 1, + Z_NEED_DICT: 2, + Z_ERRNO: -1, + Z_STREAM_ERROR: -2, + Z_DATA_ERROR: -3, + Z_MEM_ERROR: -4, + Z_BUF_ERROR: -5, + Z_VERSION_ERROR: -6, + + /* compression levels */ + Z_NO_COMPRESSION: 0, + Z_BEST_SPEED: 1, + Z_BEST_COMPRESSION: 9, + Z_DEFAULT_COMPRESSION: -1, + + Z_FILTERED: 1, + Z_HUFFMAN_ONLY: 2, + Z_RLE: 3, + Z_FIXED: 4, + Z_DEFAULT_STRATEGY: 0, + + /* Possible values of the data_type field (though see inflate()) */ + Z_BINARY: 0, + Z_TEXT: 1, + //Z_ASCII: 1, // = Z_TEXT (deprecated) + Z_UNKNOWN: 2, + + /* The deflate compression method */ + Z_DEFLATED: 8, + //Z_NULL: null // Use -1 or null inline, depending on var type +}; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +const { _tr_init, _tr_stored_block, _tr_flush_block, _tr_tally, _tr_align } = + trees; + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + +const { + Z_NO_FLUSH: Z_NO_FLUSH$2, + Z_PARTIAL_FLUSH, + Z_FULL_FLUSH: Z_FULL_FLUSH$1, + Z_FINISH: Z_FINISH$3, + Z_BLOCK: Z_BLOCK$1, + Z_OK: Z_OK$3, + Z_STREAM_END: Z_STREAM_END$3, + Z_STREAM_ERROR: Z_STREAM_ERROR$2, + Z_DATA_ERROR: Z_DATA_ERROR$2, + Z_BUF_ERROR: Z_BUF_ERROR$1, + Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1, + Z_FILTERED, + Z_HUFFMAN_ONLY, + Z_RLE, + Z_FIXED, + Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1, + Z_UNKNOWN, + Z_DEFLATED: Z_DEFLATED$2, +} = constants$2; + +/*============================================================================*/ + +const MAX_MEM_LEVEL = 9; +/* Maximum value for memLevel in deflateInit2 */ +const MAX_WBITS$1 = 15; +/* 32K LZ77 window */ +const DEF_MEM_LEVEL = 8; + +const LENGTH_CODES = 29; +/* number of length codes, not counting the special END_BLOCK code */ +const LITERALS = 256; +/* number of literal bytes 0..255 */ +const L_CODES = LITERALS + 1 + LENGTH_CODES; +/* number of Literal or Length codes, including the END_BLOCK code */ +const D_CODES = 30; +/* number of distance codes */ +const BL_CODES = 19; +/* number of codes used to transfer the bit lengths */ +const HEAP_SIZE = 2 * L_CODES + 1; +/* maximum heap size */ +const MAX_BITS = 15; +/* All codes must not exceed MAX_BITS bits */ + +const MIN_MATCH = 3; +const MAX_MATCH = 258; +const MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; + +const PRESET_DICT = 0x20; + +const INIT_STATE = 42; +const EXTRA_STATE = 69; +const NAME_STATE = 73; +const COMMENT_STATE = 91; +const HCRC_STATE = 103; +const BUSY_STATE = 113; +const FINISH_STATE = 666; + +const BS_NEED_MORE = + 1; /* block not completed, need more input or more output */ +const BS_BLOCK_DONE = 2; /* block flush performed */ +const BS_FINISH_STARTED = + 3; /* finish started, need only more output at next deflate */ +const BS_FINISH_DONE = 4; /* finish done, accept no more input or output */ + +const OS_CODE = 0x03; // Unix :) . Don't detect, use this default. + +const err = (strm, errorCode) => { + strm.msg = messages[errorCode]; + return errorCode; +}; + +const rank = (f) => { + return ((f) << 1) - ((f) > 4 ? 9 : 0); +}; + +const zero = (buf) => { + let len = buf.length; + while (--len >= 0) buf[len] = 0; +}; + +/* eslint-disable new-cap */ +let HASH_ZLIB = (s, prev, data) => + ((prev << s.hash_shift) ^ data) & s.hash_mask; +// This hash causes less collisions, https://github.com/nodeca/pako/issues/135 +// But breaks binary compatibility +//let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask; +let HASH = HASH_ZLIB; + +/* ========================================================================= + * Flush as much pending output as possible. All deflate() output goes + * through this function so some applications may wish to modify it + * to avoid allocating a large strm->output buffer and copying into it. + * (See also read_buf()). + */ +const flush_pending = (strm) => { + const s = strm.state; + + //_tr_flush_bits(s); + let len = s.pending; + if (len > strm.avail_out) { + len = strm.avail_out; + } + if (len === 0) return; + + strm.output.set( + s.pending_buf.subarray(s.pending_out, s.pending_out + len), + strm.next_out, + ); + strm.next_out += len; + s.pending_out += len; + strm.total_out += len; + strm.avail_out -= len; + s.pending -= len; + if (s.pending === 0) { + s.pending_out = 0; + } +}; + +const flush_block_only = (s, last) => { + _tr_flush_block( + s, + s.block_start >= 0 ? s.block_start : -1, + s.strstart - s.block_start, + last, + ); + s.block_start = s.strstart; + flush_pending(s.strm); +}; + +const put_byte = (s, b) => { + s.pending_buf[s.pending++] = b; +}; + +/* ========================================================================= + * Put a short in the pending buffer. The 16-bit value is put in MSB order. + * IN assertion: the stream state is correct and there is enough room in + * pending_buf. + */ +const putShortMSB = (s, b) => { + // put_byte(s, (Byte)(b >> 8)); + // put_byte(s, (Byte)(b & 0xff)); + s.pending_buf[s.pending++] = (b >>> 8) & 0xff; + s.pending_buf[s.pending++] = b & 0xff; +}; + +/* =========================================================================== + * Read a new buffer from the current input stream, update the adler32 + * and total number of bytes read. All deflate() input goes through + * this function so some applications may wish to modify it to avoid + * allocating a large strm->input buffer and copying from it. + * (See also flush_pending()). + */ +const read_buf = (strm, buf, start, size) => { + let len = strm.avail_in; + + if (len > size) len = size; + if (len === 0) return 0; + + strm.avail_in -= len; + + // zmemcpy(buf, strm->next_in, len); + buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start); + if (strm.state.wrap === 1) { + strm.adler = adler32_1(strm.adler, buf, len, start); + } else if (strm.state.wrap === 2) { + strm.adler = crc32_1(strm.adler, buf, len, start); + } + + strm.next_in += len; + strm.total_in += len; + + return len; +}; + +/* =========================================================================== + * Set match_start to the longest match starting at the given string and + * return its length. Matches shorter or equal to prev_length are discarded, + * in which case the result is equal to prev_length and match_start is + * garbage. + * IN assertions: cur_match is the head of the hash chain for the current + * string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1 + * OUT assertion: the match length is not greater than s->lookahead. + */ +const longest_match = (s, cur_match) => { + let chain_length = s.max_chain_length; /* max hash chain length */ + let scan = s.strstart; /* current string */ + let match; /* matched string */ + let len; /* length of current match */ + let best_len = s.prev_length; /* best match length so far */ + let nice_match = s.nice_match; /* stop if match long enough */ + const limit = (s.strstart > (s.w_size - MIN_LOOKAHEAD)) + ? s.strstart - (s.w_size - MIN_LOOKAHEAD) + : 0 /*NIL*/; + + const _win = s.window; // shortcut + + const wmask = s.w_mask; + const prev = s.prev; + + /* Stop when cur_match becomes <= limit. To simplify the code, + * we prevent matches with the string of window index 0. + */ + + const strend = s.strstart + MAX_MATCH; + let scan_end1 = _win[scan + best_len - 1]; + let scan_end = _win[scan + best_len]; + + /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16. + * It is easy to get rid of this optimization if necessary. + */ + // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever"); + + /* Do not waste too much time if we already have a good match: */ + if (s.prev_length >= s.good_match) { + chain_length >>= 2; + } + /* Do not look for matches beyond the end of the input. This is necessary + * to make deflate deterministic. + */ + if (nice_match > s.lookahead) nice_match = s.lookahead; + + // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead"); + + do { + // Assert(cur_match < s->strstart, "no future"); + match = cur_match; + + /* Skip to next match if the match length cannot increase + * or if the match length is less than 2. Note that the checks below + * for insufficient lookahead only occur occasionally for performance + * reasons. Therefore uninitialized memory will be accessed, and + * conditional jumps will be made that depend on those values. + * However the length of the match is limited to the lookahead, so + * the output of deflate is not affected by the uninitialized values. + */ + + if ( + _win[match + best_len] !== scan_end || + _win[match + best_len - 1] !== scan_end1 || + _win[match] !== _win[scan] || + _win[++match] !== _win[scan + 1] + ) { + continue; + } + + /* The check at best_len-1 can be removed because it will be made + * again later. (This heuristic is not always a win.) + * It is not necessary to compare scan[2] and match[2] since they + * are always equal when the other bytes match, given that + * the hash keys are equal and that HASH_BITS >= 8. + */ + scan += 2; + match++; + // Assert(*scan == *match, "match[2]?"); + + /* We check for insufficient lookahead only every 8th comparison; + * the 256th check will be made at strstart+258. + */ + do { + /*jshint noempty:false*/ + } while ( + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && + scan < strend + ); + + // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan"); + + len = MAX_MATCH - (strend - scan); + scan = strend - MAX_MATCH; + + if (len > best_len) { + s.match_start = cur_match; + best_len = len; + if (len >= nice_match) { + break; + } + scan_end1 = _win[scan + best_len - 1]; + scan_end = _win[scan + best_len]; + } + } while ( + (cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0 + ); + + if (best_len <= s.lookahead) { + return best_len; + } + return s.lookahead; +}; + +/* =========================================================================== + * Fill the window when the lookahead becomes insufficient. + * Updates strstart and lookahead. + * + * IN assertion: lookahead < MIN_LOOKAHEAD + * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD + * At least one byte has been read, or avail_in == 0; reads are + * performed for at least two bytes (required for the zip translate_eol + * option -- not supported here). + */ +const fill_window = (s) => { + const _w_size = s.w_size; + let p, n, m, more, str; + + //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead"); + + do { + more = s.window_size - s.lookahead - s.strstart; + + // JS ints have 32 bit, block below not needed + /* Deal with !@#$% 64K limit: */ + //if (sizeof(int) <= 2) { + // if (more == 0 && s->strstart == 0 && s->lookahead == 0) { + // more = wsize; + // + // } else if (more == (unsigned)(-1)) { + // /* Very unlikely, but possible on 16 bit machine if + // * strstart == 0 && lookahead == 1 (input done a byte at time) + // */ + // more--; + // } + //} + + /* If the window is almost full and there is insufficient lookahead, + * move the upper half to the lower one to make room in the upper half. + */ + if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) { + s.window.set(s.window.subarray(_w_size, _w_size + _w_size), 0); + s.match_start -= _w_size; + s.strstart -= _w_size; + /* we now have strstart >= MAX_DIST */ + s.block_start -= _w_size; + + /* Slide the hash table (could be avoided with 32 bit values + at the expense of memory usage). We slide even when level == 0 + to keep the hash table consistent if we switch back to level > 0 + later. (Using level 0 permanently is not an optimal usage of + zlib, so we don't care about this pathological case.) + */ + + n = s.hash_size; + p = n; + + do { + m = s.head[--p]; + s.head[p] = m >= _w_size ? m - _w_size : 0; + } while (--n); + + n = _w_size; + p = n; + + do { + m = s.prev[--p]; + s.prev[p] = m >= _w_size ? m - _w_size : 0; + /* If n is not on any hash chain, prev[n] is garbage but + * its value will never be used. + */ + } while (--n); + + more += _w_size; + } + if (s.strm.avail_in === 0) { + break; + } + + /* If there was no sliding: + * strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 && + * more == window_size - lookahead - strstart + * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1) + * => more >= window_size - 2*WSIZE + 2 + * In the BIG_MEM or MMAP case (not yet supported), + * window_size == input_size + MIN_LOOKAHEAD && + * strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD. + * Otherwise, window_size == 2*WSIZE so more >= 2. + * If there was sliding, more >= WSIZE. So in all cases, more >= 2. + */ + //Assert(more >= 2, "more < 2"); + n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more); + s.lookahead += n; + + /* Initialize the hash value now that we have some input: */ + if (s.lookahead + s.insert >= MIN_MATCH) { + str = s.strstart - s.insert; + s.ins_h = s.window[str]; + + /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[str + 1]); + //#if MIN_MATCH != 3 + // Call update_hash() MIN_MATCH-3 more times + //#endif + while (s.insert) { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = str; + str++; + s.insert--; + if (s.lookahead + s.insert < MIN_MATCH) { + break; + } + } + } + /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage, + * but this is not important since only literal bytes will be emitted. + */ + } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0); + + /* If the WIN_INIT bytes after the end of the current data have never been + * written, then zero those bytes in order to avoid memory check reports of + * the use of uninitialized (or uninitialised as Julian writes) bytes by + * the longest match routines. Update the high water mark for the next + * time through here. WIN_INIT is set to MAX_MATCH since the longest match + * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead. + */ + // if (s.high_water < s.window_size) { + // const curr = s.strstart + s.lookahead; + // let init = 0; + // + // if (s.high_water < curr) { + // /* Previous high water mark below current data -- zero WIN_INIT + // * bytes or up to end of window, whichever is less. + // */ + // init = s.window_size - curr; + // if (init > WIN_INIT) + // init = WIN_INIT; + // zmemzero(s->window + curr, (unsigned)init); + // s->high_water = curr + init; + // } + // else if (s->high_water < (ulg)curr + WIN_INIT) { + // /* High water mark at or above current data, but below current data + // * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up + // * to end of window, whichever is less. + // */ + // init = (ulg)curr + WIN_INIT - s->high_water; + // if (init > s->window_size - s->high_water) + // init = s->window_size - s->high_water; + // zmemzero(s->window + s->high_water, (unsigned)init); + // s->high_water += init; + // } + // } + // + // Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD, + // "not enough room for search"); +}; + +/* =========================================================================== + * Copy without compression as much as possible from the input stream, return + * the current block state. + * This function does not insert new strings in the dictionary since + * uncompressible data is probably not useful. This function is used + * only for the level=0 compression option. + * NOTE: this function should be optimized to avoid extra copying from + * window to pending_buf. + */ +const deflate_stored = (s, flush) => { + /* Stored blocks are limited to 0xffff bytes, pending_buf is limited + * to pending_buf_size, and each stored block has a 5 byte header: + */ + let max_block_size = 0xffff; + + if (max_block_size > s.pending_buf_size - 5) { + max_block_size = s.pending_buf_size - 5; + } + + /* Copy as much as possible from input to output: */ + for (;;) { + /* Fill the window as much as possible: */ + if (s.lookahead <= 1) { + //Assert(s->strstart < s->w_size+MAX_DIST(s) || + // s->block_start >= (long)s->w_size, "slide too late"); + // if (!(s.strstart < s.w_size + (s.w_size - MIN_LOOKAHEAD) || + // s.block_start >= s.w_size)) { + // throw new Error("slide too late"); + // } + + fill_window(s); + if (s.lookahead === 0 && flush === Z_NO_FLUSH$2) { + return BS_NEED_MORE; + } + + if (s.lookahead === 0) { + break; + } + /* flush the current block */ + } + //Assert(s->block_start >= 0L, "block gone"); + // if (s.block_start < 0) throw new Error("block gone"); + + s.strstart += s.lookahead; + s.lookahead = 0; + + /* Emit a stored block if pending_buf will be full: */ + const max_start = s.block_start + max_block_size; + + if (s.strstart === 0 || s.strstart >= max_start) { + /* strstart == 0 is possible when wraparound on 16-bit machine */ + s.lookahead = s.strstart - max_start; + s.strstart = max_start; + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + /* Flush if we may have to slide, otherwise block_start may become + * negative and the data will be gone: + */ + if (s.strstart - s.block_start >= (s.w_size - MIN_LOOKAHEAD)) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + + s.insert = 0; + + if (flush === Z_FINISH$3) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + + if (s.strstart > s.block_start) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_NEED_MORE; +}; + +/* =========================================================================== + * Compress as much as possible from the input stream, return the current + * block state. + * This function does not perform lazy evaluation of matches and inserts + * new strings in the dictionary only for unmatched strings or for short + * matches. It is used only for the fast compression options. + */ +const deflate_fast = (s, flush) => { + let hash_head; /* head of the hash chain */ + let bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) { + break; /* flush the current block */ + } + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0 /*NIL*/; + if (s.lookahead >= MIN_MATCH) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + * At this point we have always match_length < MIN_MATCH + */ + if ( + hash_head !== 0 /*NIL*/ && + ((s.strstart - hash_head) <= (s.w_size - MIN_LOOKAHEAD)) + ) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + } + if (s.match_length >= MIN_MATCH) { + // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only + + /*** _tr_tally_dist(s, s.strstart - s.match_start, + s.match_length - MIN_MATCH, bflush); ***/ + bflush = _tr_tally( + s, + s.strstart - s.match_start, + s.match_length - MIN_MATCH, + ); + + s.lookahead -= s.match_length; + + /* Insert new strings in the hash table only if the match length + * is not too large. This saves time but degrades compression. + */ + if ( + s.match_length <= s.max_lazy_match /*max_insert_length*/ && + s.lookahead >= MIN_MATCH + ) { + s.match_length--; /* string at strstart already in table */ + do { + s.strstart++; + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + /* strstart never exceeds WSIZE-MAX_MATCH, so there are + * always MIN_MATCH bytes ahead. + */ + } while (--s.match_length !== 0); + s.strstart++; + } else { + s.strstart += s.match_length; + s.match_length = 0; + s.ins_h = s.window[s.strstart]; + /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]); + + //#if MIN_MATCH != 3 + // Call UPDATE_HASH() MIN_MATCH-3 more times + //#endif + /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not + * matter since it will be recomputed at next deflate call. + */ + } + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s.window[s.strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = _tr_tally(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = (s.strstart < (MIN_MATCH - 1)) ? s.strstart : MIN_MATCH - 1; + if (flush === Z_FINISH$3) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; +}; + +/* =========================================================================== + * Same as above, but achieves better compression. We use a lazy + * evaluation for matches: a match is finally adopted only if there is + * no better match at the next window position. + */ +const deflate_slow = (s, flush) => { + let hash_head; /* head of hash chain */ + let bflush; /* set if current block must be flushed */ + + let max_insert; + + /* Process the input block. */ + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the next match, plus MIN_MATCH bytes to insert the + * string following the next match. + */ + if (s.lookahead < MIN_LOOKAHEAD) { + fill_window(s); + if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) break; /* flush the current block */ + } + + /* Insert the string window[strstart .. strstart+2] in the + * dictionary, and set hash_head to the head of the hash chain: + */ + hash_head = 0 /*NIL*/; + if (s.lookahead >= MIN_MATCH) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + + /* Find the longest match, discarding those <= prev_length. + */ + s.prev_length = s.match_length; + s.prev_match = s.match_start; + s.match_length = MIN_MATCH - 1; + + if ( + hash_head !== 0 /*NIL*/ && s.prev_length < s.max_lazy_match && + s.strstart - hash_head <= (s.w_size - MIN_LOOKAHEAD) /*MAX_DIST(s)*/ + ) { + /* To simplify the code, we prevent matches with the string + * of window index 0 (in particular we have to avoid a match + * of the string with itself at the start of the input file). + */ + s.match_length = longest_match(s, hash_head); + /* longest_match() sets match_start */ + + if ( + s.match_length <= 5 && + (s.strategy === Z_FILTERED || + (s.match_length === MIN_MATCH && + s.strstart - s.match_start > 4096 /*TOO_FAR*/)) + ) { + /* If prev_match is also MIN_MATCH, match_start is garbage + * but we will ignore the current match anyway. + */ + s.match_length = MIN_MATCH - 1; + } + } + /* If there was a match at the previous step and the current + * match is not better, output the previous match: + */ + if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) { + max_insert = s.strstart + s.lookahead - MIN_MATCH; + /* Do not insert strings in hash table beyond this. */ + + //check_match(s, s.strstart-1, s.prev_match, s.prev_length); + + /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match, + s.prev_length - MIN_MATCH, bflush);***/ + bflush = _tr_tally( + s, + s.strstart - 1 - s.prev_match, + s.prev_length - MIN_MATCH, + ); + /* Insert in hash table all strings up to the end of the match. + * strstart-1 and strstart are already inserted. If there is not + * enough lookahead, the last two strings are not inserted in + * the hash table. + */ + s.lookahead -= s.prev_length - 1; + s.prev_length -= 2; + do { + if (++s.strstart <= max_insert) { + /*** INSERT_STRING(s, s.strstart, hash_head); ***/ + s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]); + hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h]; + s.head[s.ins_h] = s.strstart; + /***/ + } + } while (--s.prev_length !== 0); + s.match_available = 0; + s.match_length = MIN_MATCH - 1; + s.strstart++; + + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } else if (s.match_available) { + /* If there was no match at the previous position, output a + * single literal. If there was a match but the current match + * is longer, truncate the previous match to a single literal. + */ + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); + + if (bflush) { + /*** FLUSH_BLOCK_ONLY(s, 0) ***/ + flush_block_only(s, false); + /***/ + } + s.strstart++; + s.lookahead--; + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + } else { + /* There is no previous match to compare with, wait for + * the next step to decide. + */ + s.match_available = 1; + s.strstart++; + s.lookahead--; + } + } + //Assert (flush != Z_NO_FLUSH, "no flush?"); + if (s.match_available) { + //Tracevv((stderr,"%c", s->window[s->strstart-1])); + /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/ + bflush = _tr_tally(s, 0, s.window[s.strstart - 1]); + + s.match_available = 0; + } + s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1; + if (flush === Z_FINISH$3) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + + return BS_BLOCK_DONE; +}; + +/* =========================================================================== + * For Z_RLE, simply look for runs of bytes, generate matches only of distance + * one. Do not maintain a hash table. (It will be regenerated if this run of + * deflate switches away from Z_RLE.) + */ +const deflate_rle = (s, flush) => { + let bflush; /* set if current block must be flushed */ + let prev; /* byte at distance one to match */ + let scan, strend; /* scan goes up to strend for length of run */ + + const _win = s.window; + + for (;;) { + /* Make sure that we always have enough lookahead, except + * at the end of the input file. We need MAX_MATCH bytes + * for the longest run, plus one for the unrolled loop. + */ + if (s.lookahead <= MAX_MATCH) { + fill_window(s); + if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) { + return BS_NEED_MORE; + } + if (s.lookahead === 0) break; /* flush the current block */ + } + + /* See how many times the previous byte repeats */ + s.match_length = 0; + if (s.lookahead >= MIN_MATCH && s.strstart > 0) { + scan = s.strstart - 1; + prev = _win[scan]; + if ( + prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] + ) { + strend = s.strstart + MAX_MATCH; + do { + /*jshint noempty:false*/ + } while ( + prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + prev === _win[++scan] && prev === _win[++scan] && + scan < strend + ); + s.match_length = MAX_MATCH - (strend - scan); + if (s.match_length > s.lookahead) { + s.match_length = s.lookahead; + } + } + //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan"); + } + + /* Emit match if have run of MIN_MATCH or longer, else emit literal */ + if (s.match_length >= MIN_MATCH) { + //check_match(s, s.strstart, s.strstart - 1, s.match_length); + + /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/ + bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH); + + s.lookahead -= s.match_length; + s.strstart += s.match_length; + s.match_length = 0; + } else { + /* No match, output a literal byte */ + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = _tr_tally(s, 0, s.window[s.strstart]); + + s.lookahead--; + s.strstart++; + } + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush === Z_FINISH$3) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; +}; + +/* =========================================================================== + * For Z_HUFFMAN_ONLY, do not look for matches. Do not maintain a hash table. + * (It will be regenerated if this run of deflate switches away from Huffman.) + */ +const deflate_huff = (s, flush) => { + let bflush; /* set if current block must be flushed */ + + for (;;) { + /* Make sure that we have a literal to write. */ + if (s.lookahead === 0) { + fill_window(s); + if (s.lookahead === 0) { + if (flush === Z_NO_FLUSH$2) { + return BS_NEED_MORE; + } + break; /* flush the current block */ + } + } + + /* Output a literal byte */ + s.match_length = 0; + //Tracevv((stderr,"%c", s->window[s->strstart])); + /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/ + bflush = _tr_tally(s, 0, s.window[s.strstart]); + s.lookahead--; + s.strstart++; + if (bflush) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + } + s.insert = 0; + if (flush === Z_FINISH$3) { + /*** FLUSH_BLOCK(s, 1); ***/ + flush_block_only(s, true); + if (s.strm.avail_out === 0) { + return BS_FINISH_STARTED; + } + /***/ + return BS_FINISH_DONE; + } + if (s.last_lit) { + /*** FLUSH_BLOCK(s, 0); ***/ + flush_block_only(s, false); + if (s.strm.avail_out === 0) { + return BS_NEED_MORE; + } + /***/ + } + return BS_BLOCK_DONE; +}; + +/* Values for max_lazy_match, good_match and max_chain_length, depending on + * the desired pack level (0..9). The values given below have been tuned to + * exclude worst case performance for pathological files. Better values may be + * found for specific files. + */ +function Config(good_length, max_lazy, nice_length, max_chain, func) { + this.good_length = good_length; + this.max_lazy = max_lazy; + this.nice_length = nice_length; + this.max_chain = max_chain; + this.func = func; +} + +const configuration_table = [ + /* good lazy nice chain */ + new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */ + new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */ + new Config(4, 5, 16, 8, deflate_fast), /* 2 */ + new Config(4, 6, 32, 32, deflate_fast), /* 3 */ + + new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */ + new Config(8, 16, 32, 32, deflate_slow), /* 5 */ + new Config(8, 16, 128, 128, deflate_slow), /* 6 */ + new Config(8, 32, 128, 256, deflate_slow), /* 7 */ + new Config(32, 128, 258, 1024, deflate_slow), /* 8 */ + new Config(32, 258, 258, 4096, deflate_slow), /* 9 max compression */ +]; + +/* =========================================================================== + * Initialize the "longest match" routines for a new zlib stream + */ +const lm_init = (s) => { + s.window_size = 2 * s.w_size; + + /*** CLEAR_HASH(s); ***/ + zero(s.head); // Fill with NIL (= 0); + + /* Set the default configuration parameters: + */ + s.max_lazy_match = configuration_table[s.level].max_lazy; + s.good_match = configuration_table[s.level].good_length; + s.nice_match = configuration_table[s.level].nice_length; + s.max_chain_length = configuration_table[s.level].max_chain; + + s.strstart = 0; + s.block_start = 0; + s.lookahead = 0; + s.insert = 0; + s.match_length = s.prev_length = MIN_MATCH - 1; + s.match_available = 0; + s.ins_h = 0; +}; + +function DeflateState() { + this.strm = null; /* pointer back to this zlib stream */ + this.status = 0; /* as the name implies */ + this.pending_buf = null; /* output still pending */ + this.pending_buf_size = 0; /* size of pending_buf */ + this.pending_out = 0; /* next pending byte to output to the stream */ + this.pending = 0; /* nb of bytes in the pending buffer */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.gzhead = null; /* gzip header information to write */ + this.gzindex = 0; /* where in extra, name, or comment */ + this.method = Z_DEFLATED$2; /* can only be DEFLATED */ + this.last_flush = -1; /* value of flush param for previous deflate call */ + + this.w_size = 0; /* LZ77 window size (32K by default) */ + this.w_bits = 0; /* log2(w_size) (8..16) */ + this.w_mask = 0; /* w_size - 1 */ + + this.window = null; + /* Sliding window. Input bytes are read into the second half of the window, + * and move to the first half later to keep a dictionary of at least wSize + * bytes. With this organization, matches are limited to a distance of + * wSize-MAX_MATCH bytes, but this ensures that IO is always + * performed with a length multiple of the block size. + */ + + this.window_size = 0; + /* Actual size of window: 2*wSize, except when the user input buffer + * is directly used as sliding window. + */ + + this.prev = null; + /* Link to older string with same hash index. To limit the size of this + * array to 64K, this link is maintained only for the last 32K strings. + * An index in this array is thus a window index modulo 32K. + */ + + this.head = null; /* Heads of the hash chains or NIL. */ + + this.ins_h = 0; /* hash index of string to be inserted */ + this.hash_size = 0; /* number of elements in hash table */ + this.hash_bits = 0; /* log2(hash_size) */ + this.hash_mask = 0; /* hash_size-1 */ + + this.hash_shift = 0; + /* Number of bits by which ins_h must be shifted at each input + * step. It must be such that after MIN_MATCH steps, the oldest + * byte no longer takes part in the hash key, that is: + * hash_shift * MIN_MATCH >= hash_bits + */ + + this.block_start = 0; + /* Window position at the beginning of the current output block. Gets + * negative when the window is moved backwards. + */ + + this.match_length = 0; /* length of best match */ + this.prev_match = 0; /* previous match */ + this.match_available = 0; /* set if previous match exists */ + this.strstart = 0; /* start of string to insert */ + this.match_start = 0; /* start of matching string */ + this.lookahead = 0; /* number of valid bytes ahead in window */ + + this.prev_length = 0; + /* Length of the best match at previous step. Matches not greater than this + * are discarded. This is used in the lazy match evaluation. + */ + + this.max_chain_length = 0; + /* To speed up deflation, hash chains are never searched beyond this + * length. A higher limit improves compression ratio but degrades the + * speed. + */ + + this.max_lazy_match = 0; + /* Attempt to find a better match only when the current match is strictly + * smaller than this value. This mechanism is used only for compression + * levels >= 4. + */ + // That's alias to max_lazy_match, don't use directly + //this.max_insert_length = 0; + /* Insert new strings in the hash table only if the match length is not + * greater than this length. This saves time but degrades compression. + * max_insert_length is used only for compression levels <= 3. + */ + + this.level = 0; /* compression level (1..9) */ + this.strategy = 0; /* favor or force Huffman coding*/ + + this.good_match = 0; + /* Use a faster search when the previous match is longer than this */ + + this.nice_match = 0; /* Stop searching when current match exceeds this */ + + /* used by trees.c: */ + + /* Didn't use ct_data typedef below to suppress compiler warning */ + + // struct ct_data_s dyn_ltree[HEAP_SIZE]; /* literal and length tree */ + // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */ + // struct ct_data_s bl_tree[2*BL_CODES+1]; /* Huffman tree for bit lengths */ + + // Use flat array of DOUBLE size, with interleaved fata, + // because JS does not support effective + this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2); + this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2); + this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2); + zero(this.dyn_ltree); + zero(this.dyn_dtree); + zero(this.bl_tree); + + this.l_desc = null; /* desc. for literal tree */ + this.d_desc = null; /* desc. for distance tree */ + this.bl_desc = null; /* desc. for bit length tree */ + + //ush bl_count[MAX_BITS+1]; + this.bl_count = new Uint16Array(MAX_BITS + 1); + /* number of codes at each bit length for an optimal tree */ + + //int heap[2*L_CODES+1]; /* heap used to build the Huffman trees */ + this.heap = new Uint16Array( + 2 * L_CODES + 1, + ); /* heap used to build the Huffman trees */ + zero(this.heap); + + this.heap_len = 0; /* number of elements in the heap */ + this.heap_max = 0; /* element of largest frequency */ + /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used. + * The same heap array is used to build all trees. + */ + + this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1]; + zero(this.depth); + /* Depth of each subtree used as tie breaker for trees of equal frequency + */ + + this.l_buf = 0; /* buffer index for literals or lengths */ + + this.lit_bufsize = 0; + /* Size of match buffer for literals/lengths. There are 4 reasons for + * limiting lit_bufsize to 64K: + * - frequencies can be kept in 16 bit counters + * - if compression is not successful for the first block, all input + * data is still in the window so we can still emit a stored block even + * when input comes from standard input. (This can also be done for + * all blocks if lit_bufsize is not greater than 32K.) + * - if compression is not successful for a file smaller than 64K, we can + * even emit a stored file instead of a stored block (saving 5 bytes). + * This is applicable only for zip (not gzip or zlib). + * - creating new Huffman trees less frequently may not provide fast + * adaptation to changes in the input data statistics. (Take for + * example a binary file with poorly compressible code followed by + * a highly compressible string table.) Smaller buffer sizes give + * fast adaptation but have of course the overhead of transmitting + * trees more frequently. + * - I can't count above 4 + */ + + this.last_lit = 0; /* running index in l_buf */ + + this.d_buf = 0; + /* Buffer index for distances. To simplify the code, d_buf and l_buf have + * the same number of elements. To use different lengths, an extra flag + * array would be necessary. + */ + + this.opt_len = 0; /* bit length of current block with optimal trees */ + this.static_len = 0; /* bit length of current block with static trees */ + this.matches = 0; /* number of string matches in current block */ + this.insert = 0; /* bytes at end of window left to insert */ + + this.bi_buf = 0; + /* Output buffer. bits are inserted starting at the bottom (least + * significant bits). + */ + this.bi_valid = 0; + /* Number of valid bits in bi_buf. All bits above the last valid bit + * are always zero. + */ + + // Used for window memory init. We safely ignore it for JS. That makes + // sense only for pointers and memory check tools. + //this.high_water = 0; + /* High water mark offset in window for initialized bytes -- bytes above + * this are set to zero in order to avoid memory check warnings when + * longest match routines access bytes past the input. This is then + * updated to the new high water mark. + */ +} + +const deflateResetKeep = (strm) => { + if (!strm || !strm.state) { + return err(strm, Z_STREAM_ERROR$2); + } + + strm.total_in = strm.total_out = 0; + strm.data_type = Z_UNKNOWN; + + const s = strm.state; + s.pending = 0; + s.pending_out = 0; + + if (s.wrap < 0) { + s.wrap = -s.wrap; + /* was made negative by deflate(..., Z_FINISH); */ + } + s.status = s.wrap ? INIT_STATE : BUSY_STATE; + strm.adler = (s.wrap === 2) + ? 0 // crc32(0, Z_NULL, 0) + : 1; // adler32(0, Z_NULL, 0) + s.last_flush = Z_NO_FLUSH$2; + _tr_init(s); + return Z_OK$3; +}; + +const deflateReset = (strm) => { + const ret = deflateResetKeep(strm); + if (ret === Z_OK$3) { + lm_init(strm.state); + } + return ret; +}; + +const deflateSetHeader = (strm, head) => { + if (!strm || !strm.state) return Z_STREAM_ERROR$2; + if (strm.state.wrap !== 2) return Z_STREAM_ERROR$2; + strm.state.gzhead = head; + return Z_OK$3; +}; + +const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => { + if (!strm) { // === Z_NULL + return Z_STREAM_ERROR$2; + } + let wrap = 1; + + if (level === Z_DEFAULT_COMPRESSION$1) { + level = 6; + } + + if (windowBits < 0) { + /* suppress zlib wrapper */ + wrap = 0; + windowBits = -windowBits; + } else if (windowBits > 15) { + wrap = 2; /* write gzip wrapper instead */ + windowBits -= 16; + } + + if ( + memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || + windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || + strategy < 0 || strategy > Z_FIXED + ) { + return err(strm, Z_STREAM_ERROR$2); + } + + if (windowBits === 8) { + windowBits = 9; + } + /* until 256-byte window bug fixed */ + + const s = new DeflateState(); + + strm.state = s; + s.strm = strm; + + s.wrap = wrap; + s.gzhead = null; + s.w_bits = windowBits; + s.w_size = 1 << s.w_bits; + s.w_mask = s.w_size - 1; + + s.hash_bits = memLevel + 7; + s.hash_size = 1 << s.hash_bits; + s.hash_mask = s.hash_size - 1; + s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH); + + s.window = new Uint8Array(s.w_size * 2); + s.head = new Uint16Array(s.hash_size); + s.prev = new Uint16Array(s.w_size); + + // Don't need mem init magic for JS. + //s.high_water = 0; /* nothing written to s->window yet */ + + s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */ + + s.pending_buf_size = s.lit_bufsize * 4; + + //overlay = (ushf *) ZALLOC(strm, s->lit_bufsize, sizeof(ush)+2); + //s->pending_buf = (uchf *) overlay; + s.pending_buf = new Uint8Array(s.pending_buf_size); + + // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`) + //s->d_buf = overlay + s->lit_bufsize/sizeof(ush); + s.d_buf = 1 * s.lit_bufsize; + + //s->l_buf = s->pending_buf + (1+sizeof(ush))*s->lit_bufsize; + s.l_buf = (1 + 2) * s.lit_bufsize; + + s.level = level; + s.strategy = strategy; + s.method = method; + + return deflateReset(strm); +}; + +const deflateInit = (strm, level) => { + return deflateInit2( + strm, + level, + Z_DEFLATED$2, + MAX_WBITS$1, + DEF_MEM_LEVEL, + Z_DEFAULT_STRATEGY$1, + ); +}; + +const deflate$2 = (strm, flush) => { + let beg, val; // for gzip header write only + + if ( + !strm || !strm.state || + flush > Z_BLOCK$1 || flush < 0 + ) { + return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2; + } + + const s = strm.state; + + if ( + !strm.output || + (!strm.input && strm.avail_in !== 0) || + (s.status === FINISH_STATE && flush !== Z_FINISH$3) + ) { + return err(strm, (strm.avail_out === 0) ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2); + } + + s.strm = strm; /* just in case */ + const old_flush = s.last_flush; + s.last_flush = flush; + + /* Write the header */ + if (s.status === INIT_STATE) { + if (s.wrap === 2) { // GZIP header + strm.adler = 0; //crc32(0L, Z_NULL, 0); + put_byte(s, 31); + put_byte(s, 139); + put_byte(s, 8); + if (!s.gzhead) { // s->gzhead == Z_NULL + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte(s, 0); + put_byte( + s, + s.level === 9 + ? 2 + : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0), + ); + put_byte(s, OS_CODE); + s.status = BUSY_STATE; + } else { + put_byte( + s, + (s.gzhead.text ? 1 : 0) + + (s.gzhead.hcrc ? 2 : 0) + + (!s.gzhead.extra ? 0 : 4) + + (!s.gzhead.name ? 0 : 8) + + (!s.gzhead.comment ? 0 : 16), + ); + put_byte(s, s.gzhead.time & 0xff); + put_byte(s, (s.gzhead.time >> 8) & 0xff); + put_byte(s, (s.gzhead.time >> 16) & 0xff); + put_byte(s, (s.gzhead.time >> 24) & 0xff); + put_byte( + s, + s.level === 9 + ? 2 + : (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0), + ); + put_byte(s, s.gzhead.os & 0xff); + if (s.gzhead.extra && s.gzhead.extra.length) { + put_byte(s, s.gzhead.extra.length & 0xff); + put_byte(s, (s.gzhead.extra.length >> 8) & 0xff); + } + if (s.gzhead.hcrc) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0); + } + s.gzindex = 0; + s.status = EXTRA_STATE; + } + } // DEFLATE header + else { + let header = (Z_DEFLATED$2 + ((s.w_bits - 8) << 4)) << 8; + let level_flags = -1; + + if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) { + level_flags = 0; + } else if (s.level < 6) { + level_flags = 1; + } else if (s.level === 6) { + level_flags = 2; + } else { + level_flags = 3; + } + header |= level_flags << 6; + if (s.strstart !== 0) header |= PRESET_DICT; + header += 31 - (header % 31); + + s.status = BUSY_STATE; + putShortMSB(s, header); + + /* Save the adler32 of the preset dictionary: */ + if (s.strstart !== 0) { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + strm.adler = 1; // adler32(0L, Z_NULL, 0); + } + } + + //#ifdef GZIP + if (s.status === EXTRA_STATE) { + if (s.gzhead.extra /* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + + while (s.gzindex < (s.gzhead.extra.length & 0xffff)) { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1( + strm.adler, + s.pending_buf, + s.pending - beg, + beg, + ); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + break; + } + } + put_byte(s, s.gzhead.extra[s.gzindex] & 0xff); + s.gzindex++; + } + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (s.gzindex === s.gzhead.extra.length) { + s.gzindex = 0; + s.status = NAME_STATE; + } + } else { + s.status = NAME_STATE; + } + } + if (s.status === NAME_STATE) { + if (s.gzhead.name /* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1( + strm.adler, + s.pending_buf, + s.pending - beg, + beg, + ); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.name.length) { + val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val !== 0); + + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val === 0) { + s.gzindex = 0; + s.status = COMMENT_STATE; + } + } else { + s.status = COMMENT_STATE; + } + } + if (s.status === COMMENT_STATE) { + if (s.gzhead.comment /* != Z_NULL*/) { + beg = s.pending; /* start of bytes to update crc */ + //int val; + + do { + if (s.pending === s.pending_buf_size) { + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1( + strm.adler, + s.pending_buf, + s.pending - beg, + beg, + ); + } + flush_pending(strm); + beg = s.pending; + if (s.pending === s.pending_buf_size) { + val = 1; + break; + } + } + // JS specific: little magic to add zero terminator to end of string + if (s.gzindex < s.gzhead.comment.length) { + val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff; + } else { + val = 0; + } + put_byte(s, val); + } while (val !== 0); + + if (s.gzhead.hcrc && s.pending > beg) { + strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg); + } + if (val === 0) { + s.status = HCRC_STATE; + } + } else { + s.status = HCRC_STATE; + } + } + if (s.status === HCRC_STATE) { + if (s.gzhead.hcrc) { + if (s.pending + 2 > s.pending_buf_size) { + flush_pending(strm); + } + if (s.pending + 2 <= s.pending_buf_size) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + strm.adler = 0; //crc32(0L, Z_NULL, 0); + s.status = BUSY_STATE; + } + } else { + s.status = BUSY_STATE; + } + } + //#endif + + /* Flush as much pending output as possible */ + if (s.pending !== 0) { + flush_pending(strm); + if (strm.avail_out === 0) { + /* Since avail_out is 0, deflate will be called again with + * more output space, but possibly with both pending and + * avail_in equal to zero. There won't be anything to do, + * but this is not an error situation so make sure we + * return OK instead of BUF_ERROR at next call of deflate: + */ + s.last_flush = -1; + return Z_OK$3; + } + + /* Make sure there is something to do and avoid duplicate consecutive + * flushes. For repeated and useless calls with Z_FINISH, we keep + * returning Z_STREAM_END instead of Z_BUF_ERROR. + */ + } else if ( + strm.avail_in === 0 && rank(flush) <= rank(old_flush) && + flush !== Z_FINISH$3 + ) { + return err(strm, Z_BUF_ERROR$1); + } + + /* User must not provide more input after the first FINISH: */ + if (s.status === FINISH_STATE && strm.avail_in !== 0) { + return err(strm, Z_BUF_ERROR$1); + } + + /* Start a new block or continue the current one. + */ + if ( + strm.avail_in !== 0 || s.lookahead !== 0 || + (flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE) + ) { + let bstate = (s.strategy === Z_HUFFMAN_ONLY) + ? deflate_huff(s, flush) + : (s.strategy === Z_RLE + ? deflate_rle(s, flush) + : configuration_table[s.level].func(s, flush)); + + if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) { + s.status = FINISH_STATE; + } + if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) { + if (strm.avail_out === 0) { + s.last_flush = -1; + /* avoid BUF_ERROR next call, see above */ + } + return Z_OK$3; + /* If flush != Z_NO_FLUSH && avail_out == 0, the next call + * of deflate should use the same flush parameter to make sure + * that the flush is complete. So we don't have to output an + * empty block here, this will be done at next call. This also + * ensures that for a very small output buffer, we emit at most + * one empty block. + */ + } + if (bstate === BS_BLOCK_DONE) { + if (flush === Z_PARTIAL_FLUSH) { + _tr_align(s); + } else if (flush !== Z_BLOCK$1) { + /* FULL_FLUSH or SYNC_FLUSH */ + + _tr_stored_block(s, 0, 0, false); + /* For a full flush, this empty block will be recognized + * as a special marker by inflate_sync(). + */ + if (flush === Z_FULL_FLUSH$1) { + /*** CLEAR_HASH(s); ***/ + /* forget history */ + zero(s.head); // Fill with NIL (= 0); + + if (s.lookahead === 0) { + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + } + } + flush_pending(strm); + if (strm.avail_out === 0) { + s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */ + return Z_OK$3; + } + } + } + //Assert(strm->avail_out > 0, "bug2"); + //if (strm.avail_out <= 0) { throw new Error("bug2");} + + if (flush !== Z_FINISH$3) return Z_OK$3; + if (s.wrap <= 0) return Z_STREAM_END$3; + + /* Write the trailer */ + if (s.wrap === 2) { + put_byte(s, strm.adler & 0xff); + put_byte(s, (strm.adler >> 8) & 0xff); + put_byte(s, (strm.adler >> 16) & 0xff); + put_byte(s, (strm.adler >> 24) & 0xff); + put_byte(s, strm.total_in & 0xff); + put_byte(s, (strm.total_in >> 8) & 0xff); + put_byte(s, (strm.total_in >> 16) & 0xff); + put_byte(s, (strm.total_in >> 24) & 0xff); + } else { + putShortMSB(s, strm.adler >>> 16); + putShortMSB(s, strm.adler & 0xffff); + } + + flush_pending(strm); + /* If avail_out is zero, the application will call deflate again + * to flush the rest. + */ + if (s.wrap > 0) s.wrap = -s.wrap; + /* write the trailer only once! */ + return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3; +}; + +const deflateEnd = (strm) => { + if (!strm /*== Z_NULL*/ || !strm.state /*== Z_NULL*/) { + return Z_STREAM_ERROR$2; + } + + const status = strm.state.status; + if ( + status !== INIT_STATE && + status !== EXTRA_STATE && + status !== NAME_STATE && + status !== COMMENT_STATE && + status !== HCRC_STATE && + status !== BUSY_STATE && + status !== FINISH_STATE + ) { + return err(strm, Z_STREAM_ERROR$2); + } + + strm.state = null; + + return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3; +}; + +/* ========================================================================= + * Initializes the compression dictionary from the given byte + * sequence without producing any compressed output. + */ +const deflateSetDictionary = (strm, dictionary) => { + let dictLength = dictionary.length; + + if (!strm /*== Z_NULL*/ || !strm.state /*== Z_NULL*/) { + return Z_STREAM_ERROR$2; + } + + const s = strm.state; + const wrap = s.wrap; + + if (wrap === 2 || (wrap === 1 && s.status !== INIT_STATE) || s.lookahead) { + return Z_STREAM_ERROR$2; + } + + /* when using zlib wrappers, compute Adler-32 for provided dictionary */ + if (wrap === 1) { + /* adler32(strm->adler, dictionary, dictLength); */ + strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0); + } + + s.wrap = 0; /* avoid computing Adler-32 in read_buf */ + + /* if dictionary would fill window, just replace the history */ + if (dictLength >= s.w_size) { + if (wrap === 0) { + /* already empty otherwise */ + /*** CLEAR_HASH(s); ***/ + zero(s.head); // Fill with NIL (= 0); + s.strstart = 0; + s.block_start = 0; + s.insert = 0; + } + /* use the tail */ + // dictionary = dictionary.slice(dictLength - s.w_size); + let tmpDict = new Uint8Array(s.w_size); + tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0); + dictionary = tmpDict; + dictLength = s.w_size; + } + /* insert dictionary into window and hash */ + const avail = strm.avail_in; + const next = strm.next_in; + const input = strm.input; + strm.avail_in = dictLength; + strm.next_in = 0; + strm.input = dictionary; + fill_window(s); + while (s.lookahead >= MIN_MATCH) { + let str = s.strstart; + let n = s.lookahead - (MIN_MATCH - 1); + do { + /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */ + s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]); + + s.prev[str & s.w_mask] = s.head[s.ins_h]; + + s.head[s.ins_h] = str; + str++; + } while (--n); + s.strstart = str; + s.lookahead = MIN_MATCH - 1; + fill_window(s); + } + s.strstart += s.lookahead; + s.block_start = s.strstart; + s.insert = s.lookahead; + s.lookahead = 0; + s.match_length = s.prev_length = MIN_MATCH - 1; + s.match_available = 0; + strm.next_in = next; + strm.input = input; + strm.avail_in = avail; + s.wrap = wrap; + return Z_OK$3; +}; + +var deflateInit_1 = deflateInit; +var deflateInit2_1 = deflateInit2; +var deflateReset_1 = deflateReset; +var deflateResetKeep_1 = deflateResetKeep; +var deflateSetHeader_1 = deflateSetHeader; +var deflate_2$1 = deflate$2; +var deflateEnd_1 = deflateEnd; +var deflateSetDictionary_1 = deflateSetDictionary; +var deflateInfo = "pako deflate (from Nodeca project)"; + +/* Not implemented +module.exports.deflateBound = deflateBound; +module.exports.deflateCopy = deflateCopy; +module.exports.deflateParams = deflateParams; +module.exports.deflatePending = deflatePending; +module.exports.deflatePrime = deflatePrime; +module.exports.deflateTune = deflateTune; +*/ + +var deflate_1$2 = { + deflateInit: deflateInit_1, + deflateInit2: deflateInit2_1, + deflateReset: deflateReset_1, + deflateResetKeep: deflateResetKeep_1, + deflateSetHeader: deflateSetHeader_1, + deflate: deflate_2$1, + deflateEnd: deflateEnd_1, + deflateSetDictionary: deflateSetDictionary_1, + deflateInfo: deflateInfo, +}; + +const _has = (obj, key) => { + return Object.prototype.hasOwnProperty.call(obj, key); +}; + +var assign = function (obj /*from1, from2, from3, ...*/) { + const sources = Array.prototype.slice.call(arguments, 1); + while (sources.length) { + const source = sources.shift(); + if (!source) continue; + + if (typeof source !== "object") { + throw new TypeError(source + "must be non-object"); + } + + for (const p in source) { + if (_has(source, p)) { + obj[p] = source[p]; + } + } + } + + return obj; +}; + +// Join array of chunks to single array. +var flattenChunks = (chunks) => { + // calculate data length + let len = 0; + + for (let i = 0, l = chunks.length; i < l; i++) { + len += chunks[i].length; + } + + // join chunks + const result = new Uint8Array(len); + + for (let i = 0, pos = 0, l = chunks.length; i < l; i++) { + let chunk = chunks[i]; + result.set(chunk, pos); + pos += chunk.length; + } + + return result; +}; + +var common = { + assign: assign, + flattenChunks: flattenChunks, +}; + +// String encode/decode helpers + +// Quick check if we can use fast array to bin string conversion +// +// - apply(Array) can fail on Android 2.2 +// - apply(Uint8Array) can fail on iOS 5.1 Safari +// +let STR_APPLY_UIA_OK = true; + +try { + String.fromCharCode.apply(null, new Uint8Array(1)); +} catch (__) { + STR_APPLY_UIA_OK = false; +} + +// Table with utf8 lengths (calculated by first byte of sequence) +// Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS, +// because max possible codepoint is 0x10ffff +const _utf8len = new Uint8Array(256); +for (let q = 0; q < 256; q++) { + _utf8len[q] = q >= 252 + ? 6 + : q >= 248 + ? 5 + : q >= 240 + ? 4 + : q >= 224 + ? 3 + : q >= 192 + ? 2 + : 1; +} +_utf8len[254] = _utf8len[254] = 1; // Invalid sequence start + +// convert string to array (typed, when possible) +var string2buf = (str) => { + if (typeof TextEncoder === "function" && TextEncoder.prototype.encode) { + return new TextEncoder().encode(str); + } + + let buf, c, c2, m_pos, i, str_len = str.length, buf_len = 0; + + // count binary size + for (m_pos = 0; m_pos < str_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4; + } + + // allocate buffer + buf = new Uint8Array(buf_len); + + // convert + for (i = 0, m_pos = 0; i < buf_len; m_pos++) { + c = str.charCodeAt(m_pos); + if ((c & 0xfc00) === 0xd800 && (m_pos + 1 < str_len)) { + c2 = str.charCodeAt(m_pos + 1); + if ((c2 & 0xfc00) === 0xdc00) { + c = 0x10000 + ((c - 0xd800) << 10) + (c2 - 0xdc00); + m_pos++; + } + } + if (c < 0x80) { + /* one byte */ + buf[i++] = c; + } else if (c < 0x800) { + /* two bytes */ + buf[i++] = 0xC0 | (c >>> 6); + buf[i++] = 0x80 | (c & 0x3f); + } else if (c < 0x10000) { + /* three bytes */ + buf[i++] = 0xE0 | (c >>> 12); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } else { + /* four bytes */ + buf[i++] = 0xf0 | (c >>> 18); + buf[i++] = 0x80 | (c >>> 12 & 0x3f); + buf[i++] = 0x80 | (c >>> 6 & 0x3f); + buf[i++] = 0x80 | (c & 0x3f); + } + } + + return buf; +}; + +// Helper +const buf2binstring = (buf, len) => { + // On Chrome, the arguments in a function call that are allowed is `65534`. + // If the length of the buffer is smaller than that, we can use this optimization, + // otherwise we will take a slower path. + if (len < 65534) { + if (buf.subarray && STR_APPLY_UIA_OK) { + return String.fromCharCode.apply( + null, + buf.length === len ? buf : buf.subarray(0, len), + ); + } + } + + let result = ""; + for (let i = 0; i < len; i++) { + result += String.fromCharCode(buf[i]); + } + return result; +}; + +// convert array to string +var buf2string = (buf, max) => { + const len = max || buf.length; + + if (typeof TextDecoder === "function" && TextDecoder.prototype.decode) { + return new TextDecoder().decode(buf.subarray(0, max)); + } + + let i, out; + + // Reserve max possible length (2 words per char) + // NB: by unknown reasons, Array is significantly faster for + // String.fromCharCode.apply than Uint16Array. + const utf16buf = new Array(len * 2); + + for (out = 0, i = 0; i < len;) { + let c = buf[i++]; + // quick process ascii + if (c < 0x80) { + utf16buf[out++] = c; + continue; + } + + let c_len = _utf8len[c]; + // skip 5 & 6 byte codes + if (c_len > 4) { + utf16buf[out++] = 0xfffd; + i += c_len - 1; + continue; + } + + // apply mask on first byte + c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07; + // join the rest + while (c_len > 1 && i < len) { + c = (c << 6) | (buf[i++] & 0x3f); + c_len--; + } + + // terminated by end of string? + if (c_len > 1) { + utf16buf[out++] = 0xfffd; + continue; + } + + if (c < 0x10000) { + utf16buf[out++] = c; + } else { + c -= 0x10000; + utf16buf[out++] = 0xd800 | ((c >> 10) & 0x3ff); + utf16buf[out++] = 0xdc00 | (c & 0x3ff); + } + } + + return buf2binstring(utf16buf, out); +}; + +// Calculate max possible position in utf8 buffer, +// that will not break sequence. If that's not possible +// - (very small limits) return max size as is. +// +// buf[] - utf8 bytes array +// max - length limit (mandatory); +var utf8border = (buf, max) => { + max = max || buf.length; + if (max > buf.length) max = buf.length; + + // go back from last position, until start of sequence found + let pos = max - 1; + while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) pos--; + + // Very small and broken sequence, + // return max, because we should return something anyway. + if (pos < 0) return max; + + // If we came to start of buffer - that means buffer is too small, + // return max too. + if (pos === 0) return max; + + return (pos + _utf8len[buf[pos]] > max) ? pos : max; +}; + +var strings = { + string2buf: string2buf, + buf2string: buf2string, + utf8border: utf8border, +}; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +function ZStream() { + /* next input byte */ + this.input = null; // JS specific, because we have no pointers + this.next_in = 0; + /* number of bytes available at input */ + this.avail_in = 0; + /* total number of input bytes read so far */ + this.total_in = 0; + /* next output byte should be put there */ + this.output = null; // JS specific, because we have no pointers + this.next_out = 0; + /* remaining free space at output */ + this.avail_out = 0; + /* total number of bytes output so far */ + this.total_out = 0; + /* last error message, NULL if no error */ + this.msg = "" /*Z_NULL*/; + /* not visible by applications */ + this.state = null; + /* best guess about the data type: binary or text */ + this.data_type = 2 /*Z_UNKNOWN*/; + /* adler32 value of the uncompressed data */ + this.adler = 0; +} + +var zstream = ZStream; + +const toString$1 = Object.prototype.toString; + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + +const { + Z_NO_FLUSH: Z_NO_FLUSH$1, + Z_SYNC_FLUSH, + Z_FULL_FLUSH, + Z_FINISH: Z_FINISH$2, + Z_OK: Z_OK$2, + Z_STREAM_END: Z_STREAM_END$2, + Z_DEFAULT_COMPRESSION, + Z_DEFAULT_STRATEGY, + Z_DEFLATED: Z_DEFLATED$1, +} = constants$2; + +/* ===========================================================================*/ + +/** + * class Deflate + * + * Generic JS-style wrapper for zlib calls. If you don't need + * streaming behaviour - use more simple functions: [[deflate]], + * [[deflateRaw]] and [[gzip]]. + */ + +/* internal + * Deflate.chunks -> Array + * + * Chunks of output data, if [[Deflate#onData]] not overridden. + **/ + +/** + * Deflate.result -> Uint8Array + * + * Compressed result, generated by default [[Deflate#onData]] + * and [[Deflate#onEnd]] handlers. Filled after you push last chunk + * (call [[Deflate#push]] with `Z_FINISH` / `true` param). + */ + +/** + * Deflate.err -> Number + * + * Error code after deflate finished. 0 (Z_OK) on success. + * You will not need it in real life, because deflate errors + * are possible only on wrong options or bad `onData` / `onEnd` + * custom handlers. + */ + +/** + * Deflate.msg -> String + * + * Error message, if [[Deflate.err]] != 0 + */ + +/** + * new Deflate(options) + * - options (Object): zlib deflate options. + * + * Creates new deflator instance with specified params. Throws exception + * on bad params. Supported options: + * + * - `level` + * - `windowBits` + * - `memLevel` + * - `strategy` + * - `dictionary` + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Additional options, for internal needs: + * + * - `chunkSize` - size of generated data chunks (16K by default) + * - `raw` (Boolean) - do raw deflate + * - `gzip` (Boolean) - create gzip wrapper + * - `header` (Object) - custom header for gzip + * - `text` (Boolean) - true if compressed data believed to be text + * - `time` (Number) - modification time, unix timestamp + * - `os` (Number) - operation system code + * - `extra` (Array) - array of bytes with extra data (max 65536) + * - `name` (String) - file name (binary string) + * - `comment` (String) - comment (binary string) + * - `hcrc` (Boolean) - true if header crc should be added + * + * ##### Example: + * + * ```javascript + * const pako = require('pako') + * , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) + * , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); + * + * const deflate = new pako.Deflate({ level: 3}); + * + * deflate.push(chunk1, false); + * deflate.push(chunk2, true); // true -> last chunk + * + * if (deflate.err) { throw new Error(deflate.err); } + * + * console.log(deflate.result); + * ``` + */ +function Deflate$1(options) { + this.options = common.assign({ + level: Z_DEFAULT_COMPRESSION, + method: Z_DEFLATED$1, + chunkSize: 16384, + windowBits: 15, + memLevel: 8, + strategy: Z_DEFAULT_STRATEGY, + }, options || {}); + + let opt = this.options; + + if (opt.raw && (opt.windowBits > 0)) { + opt.windowBits = -opt.windowBits; + } else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) { + opt.windowBits += 16; + } + + this.err = 0; // error code, if happens (0 = Z_OK) + this.msg = ""; // error message + this.ended = false; // used to avoid multiple onEnd() calls + this.chunks = []; // chunks of compressed data + + this.strm = new zstream(); + this.strm.avail_out = 0; + + let status = deflate_1$2.deflateInit2( + this.strm, + opt.level, + opt.method, + opt.windowBits, + opt.memLevel, + opt.strategy, + ); + + if (status !== Z_OK$2) { + throw new Error(messages[status]); + } + + if (opt.header) { + deflate_1$2.deflateSetHeader(this.strm, opt.header); + } + + if (opt.dictionary) { + let dict; + // Convert data if needed + if (typeof opt.dictionary === "string") { + // If we need to compress text, change encoding to utf8. + dict = strings.string2buf(opt.dictionary); + } else if (toString$1.call(opt.dictionary) === "[object ArrayBuffer]") { + dict = new Uint8Array(opt.dictionary); + } else { + dict = opt.dictionary; + } + + status = deflate_1$2.deflateSetDictionary(this.strm, dict); + + if (status !== Z_OK$2) { + throw new Error(messages[status]); + } + + this._dict_set = true; + } +} + +/** + * Deflate#push(data[, flush_mode]) -> Boolean + * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be + * converted to utf8 byte sequence. + * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. + * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH. + * + * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with + * new compressed chunks. Returns `true` on success. The last data block must + * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending + * buffers and call [[Deflate#onEnd]]. + * + * On fail call [[Deflate#onEnd]] with error code and return false. + * + * ##### Example + * + * ```javascript + * push(chunk, false); // push one of data chunks + * ... + * push(chunk, true); // push last chunk + * ``` + */ +Deflate$1.prototype.push = function (data, flush_mode) { + const strm = this.strm; + const chunkSize = this.options.chunkSize; + let status, _flush_mode; + + if (this.ended) return false; + + if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; + else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1; + + // Convert data if needed + if (typeof data === "string") { + // If we need to compress text, change encoding to utf8. + strm.input = strings.string2buf(data); + } else if (toString$1.call(data) === "[object ArrayBuffer]") { + strm.input = new Uint8Array(data); + } else { + strm.input = data; + } + + strm.next_in = 0; + strm.avail_in = strm.input.length; + + for (;;) { + if (strm.avail_out === 0) { + strm.output = new Uint8Array(chunkSize); + strm.next_out = 0; + strm.avail_out = chunkSize; + } + + // Make sure avail_out > 6 to avoid repeating markers + if ( + (_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && + strm.avail_out <= 6 + ) { + this.onData(strm.output.subarray(0, strm.next_out)); + strm.avail_out = 0; + continue; + } + + status = deflate_1$2.deflate(strm, _flush_mode); + + // Ended => flush and finish + if (status === Z_STREAM_END$2) { + if (strm.next_out > 0) { + this.onData(strm.output.subarray(0, strm.next_out)); + } + status = deflate_1$2.deflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return status === Z_OK$2; + } + + // Flush if out buffer full + if (strm.avail_out === 0) { + this.onData(strm.output); + continue; + } + + // Flush if requested and has data + if (_flush_mode > 0 && strm.next_out > 0) { + this.onData(strm.output.subarray(0, strm.next_out)); + strm.avail_out = 0; + continue; + } + + if (strm.avail_in === 0) break; + } + + return true; +}; + +/** + * Deflate#onData(chunk) -> Void + * - chunk (Uint8Array): output data. + * + * By default, stores data blocks in `chunks[]` property and glue + * those in `onEnd`. Override this handler, if you need another behaviour. + */ +Deflate$1.prototype.onData = function (chunk) { + this.chunks.push(chunk); +}; + +/** + * Deflate#onEnd(status) -> Void + * - status (Number): deflate status. 0 (Z_OK) on success, + * other if not. + * + * Called once after you tell deflate that the input stream is + * complete (Z_FINISH). By default - join collected chunks, + * free memory and fill `results` / `err` properties. + */ +Deflate$1.prototype.onEnd = function (status) { + // On success - join + if (status === Z_OK$2) { + this.result = common.flattenChunks(this.chunks); + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; +}; + +/** + * deflate(data[, options]) -> Uint8Array + * - data (Uint8Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * Compress `data` with deflate algorithm and `options`. + * + * Supported options are: + * + * - level + * - windowBits + * - memLevel + * - strategy + * - dictionary + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Sugar (options): + * + * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify + * negative windowBits implicitly. + * + * ##### Example: + * + * ```javascript + * const pako = require('pako') + * const data = new Uint8Array([1,2,3,4,5,6,7,8,9]); + * + * console.log(pako.deflate(data)); + * ``` + */ +function deflate$1(input, options) { + const deflator = new Deflate$1(options); + + deflator.push(input, true); + + // That will never happens, if you don't cheat with options :) + if (deflator.err) throw deflator.msg || messages[deflator.err]; + + return deflator.result; +} + +/** + * deflateRaw(data[, options]) -> Uint8Array + * - data (Uint8Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * The same as [[deflate]], but creates raw data, without wrapper + * (header and adler32 crc). + */ +function deflateRaw$1(input, options) { + options = options || {}; + options.raw = true; + return deflate$1(input, options); +} + +/** + * gzip(data[, options]) -> Uint8Array + * - data (Uint8Array|String): input data to compress. + * - options (Object): zlib deflate options. + * + * The same as [[deflate]], but create gzip wrapper instead of + * deflate one. + */ +function gzip$1(input, options) { + options = options || {}; + options.gzip = true; + return deflate$1(input, options); +} + +var Deflate_1$1 = Deflate$1; +var deflate_2 = deflate$1; +var deflateRaw_1$1 = deflateRaw$1; +var gzip_1$1 = gzip$1; +var constants$1 = constants$2; + +var deflate_1$1 = { + Deflate: Deflate_1$1, + deflate: deflate_2, + deflateRaw: deflateRaw_1$1, + gzip: gzip_1$1, + constants: constants$1, +}; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +// See state defs from inflate.js +const BAD$1 = 30; /* got a data error -- remain here until reset */ +const TYPE$1 = 12; /* i: waiting for type bits, including last-flag bit */ + +/* + Decode literal, length, and distance codes and write out the resulting + literal and match bytes until either not enough input or output is + available, an end-of-block is encountered, or a data error is encountered. + When large enough input and output buffers are supplied to inflate(), for + example, a 16K input buffer and a 64K output buffer, more than 95% of the + inflate execution time is spent in this routine. + + Entry assumptions: + + state.mode === LEN + strm.avail_in >= 6 + strm.avail_out >= 258 + start >= strm.avail_out + state.bits < 8 + + On return, state.mode is one of: + + LEN -- ran out of enough output space or enough available input + TYPE -- reached end of block code, inflate() to interpret next block + BAD -- error in block data + + Notes: + + - The maximum input bits used by a length/distance pair is 15 bits for the + length code, 5 bits for the length extra, 15 bits for the distance code, + and 13 bits for the distance extra. This totals 48 bits, or six bytes. + Therefore if strm.avail_in >= 6, then there is enough input to avoid + checking for available input while decoding. + + - The maximum bytes that a single length/distance pair can output is 258 + bytes, which is the maximum length that can be coded. inflate_fast() + requires strm.avail_out >= 258 for each loop to avoid checking for + output space. + */ +var inffast = function inflate_fast(strm, start) { + let _in; /* local strm.input */ + let last; /* have enough input while in < last */ + let _out; /* local strm.output */ + let beg; /* inflate()'s initial strm.output */ + let end; /* while out < end, enough space available */ + //#ifdef INFLATE_STRICT + let dmax; /* maximum distance from zlib header */ + //#endif + let wsize; /* window size or zero if not using window */ + let whave; /* valid bytes in the window */ + let wnext; /* window write index */ + // Use `s_window` instead `window`, avoid conflict with instrumentation tools + let s_window; /* allocated sliding window, if wsize != 0 */ + let hold; /* local strm.hold */ + let bits; /* local strm.bits */ + let lcode; /* local strm.lencode */ + let dcode; /* local strm.distcode */ + let lmask; /* mask for first level of length codes */ + let dmask; /* mask for first level of distance codes */ + let here; /* retrieved table entry */ + let op; /* code bits, operation, extra bits, or */ + /* window position, window bytes to copy */ + let len; /* match length, unused bytes */ + let dist; /* match distance */ + let from; /* where to copy match from */ + let from_source; + + let input, output; // JS specific, because we have no pointers + + /* copy state to local variables */ + const state = strm.state; + //here = state.here; + _in = strm.next_in; + input = strm.input; + last = _in + (strm.avail_in - 5); + _out = strm.next_out; + output = strm.output; + beg = _out - (start - strm.avail_out); + end = _out + (strm.avail_out - 257); + //#ifdef INFLATE_STRICT + dmax = state.dmax; + //#endif + wsize = state.wsize; + whave = state.whave; + wnext = state.wnext; + s_window = state.window; + hold = state.hold; + bits = state.bits; + lcode = state.lencode; + dcode = state.distcode; + lmask = (1 << state.lenbits) - 1; + dmask = (1 << state.distbits) - 1; + + /* decode literals and length/distances until end-of-block or not enough + input data or output space */ + + top: + do { + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + + here = lcode[hold & lmask]; + + dolen: + for (;;) { // Goto emulation + op = here >>> 24 /*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff /*here.op*/; + if (op === 0) { + /* literal */ + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + output[_out++] = here & 0xffff /*here.val*/; + } else if (op & 16) { + /* length base */ + len = here & 0xffff /*here.val*/; + op &= 15; /* number of extra bits */ + if (op) { + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + len += hold & ((1 << op) - 1); + hold >>>= op; + bits -= op; + } + //Tracevv((stderr, "inflate: length %u\n", len)); + if (bits < 15) { + hold += input[_in++] << bits; + bits += 8; + hold += input[_in++] << bits; + bits += 8; + } + here = dcode[hold & dmask]; + + dodist: + for (;;) { // goto emulation + op = here >>> 24 /*here.bits*/; + hold >>>= op; + bits -= op; + op = (here >>> 16) & 0xff /*here.op*/; + + if (op & 16) { + /* distance base */ + dist = here & 0xffff /*here.val*/; + op &= 15; /* number of extra bits */ + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + if (bits < op) { + hold += input[_in++] << bits; + bits += 8; + } + } + dist += hold & ((1 << op) - 1); + //#ifdef INFLATE_STRICT + if (dist > dmax) { + strm.msg = "invalid distance too far back"; + state.mode = BAD$1; + break top; + } + //#endif + hold >>>= op; + bits -= op; + //Tracevv((stderr, "inflate: distance %u\n", dist)); + op = _out - beg; /* max distance in output */ + if (dist > op) { + /* see if copy from window */ + op = dist - op; /* distance back in window */ + if (op > whave) { + if (state.sane) { + strm.msg = "invalid distance too far back"; + state.mode = BAD$1; + break top; + } + + // (!) This block is disabled in zlib defaults, + // don't enable it for binary compatibility + //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + // if (len <= op - whave) { + // do { + // output[_out++] = 0; + // } while (--len); + // continue top; + // } + // len -= op - whave; + // do { + // output[_out++] = 0; + // } while (--op > whave); + // if (op === 0) { + // from = _out - dist; + // do { + // output[_out++] = output[from++]; + // } while (--len); + // continue top; + // } + //#endif + } + from = 0; // window index + from_source = s_window; + if (wnext === 0) { + /* very common case */ + from += wsize - op; + if (op < len) { + /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } else if (wnext < op) { + /* wrap around window */ + from += wsize + wnext - op; + op -= wnext; + if (op < len) { + /* some from end of window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = 0; + if (wnext < len) { + /* some from start of window */ + op = wnext; + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + } else { + /* contiguous in window */ + from += wnext - op; + if (op < len) { + /* some from window */ + len -= op; + do { + output[_out++] = s_window[from++]; + } while (--op); + from = _out - dist; /* rest from output */ + from_source = output; + } + } + while (len > 2) { + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + output[_out++] = from_source[from++]; + len -= 3; + } + if (len) { + output[_out++] = from_source[from++]; + if (len > 1) { + output[_out++] = from_source[from++]; + } + } + } else { + from = _out - dist; /* copy direct from output */ + do { + /* minimum length is three */ + output[_out++] = output[from++]; + output[_out++] = output[from++]; + output[_out++] = output[from++]; + len -= 3; + } while (len > 2); + if (len) { + output[_out++] = output[from++]; + if (len > 1) { + output[_out++] = output[from++]; + } + } + } + } else if ((op & 64) === 0) { + /* 2nd level distance code */ + here = + dcode[(here & 0xffff) /*here.val*/ + (hold & ((1 << op) - 1))]; + continue dodist; + } else { + strm.msg = "invalid distance code"; + state.mode = BAD$1; + break top; + } + + break; // need to emulate goto via "continue" + } + } else if ((op & 64) === 0) { + /* 2nd level length code */ + here = lcode[(here & 0xffff) /*here.val*/ + (hold & ((1 << op) - 1))]; + continue dolen; + } else if (op & 32) { + /* end-of-block */ + //Tracevv((stderr, "inflate: end of block\n")); + state.mode = TYPE$1; + break top; + } else { + strm.msg = "invalid literal/length code"; + state.mode = BAD$1; + break top; + } + + break; // need to emulate goto via "continue" + } + } while (_in < last && _out < end); + + /* return unused bytes (on entry, bits < 8, so in won't go too far back) */ + len = bits >> 3; + _in -= len; + bits -= len << 3; + hold &= (1 << bits) - 1; + + /* update state and return */ + strm.next_in = _in; + strm.next_out = _out; + strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last); + strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end); + state.hold = hold; + state.bits = bits; + return; +}; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +const MAXBITS = 15; +const ENOUGH_LENS$1 = 852; +const ENOUGH_DISTS$1 = 592; +//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + +const CODES$1 = 0; +const LENS$1 = 1; +const DISTS$1 = 2; + +const lbase = new Uint16Array([ + /* Length codes 257..285 base */ + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 13, + 15, + 17, + 19, + 23, + 27, + 31, + 35, + 43, + 51, + 59, + 67, + 83, + 99, + 115, + 131, + 163, + 195, + 227, + 258, + 0, + 0, +]); + +const lext = new Uint8Array([ + /* Length codes 257..285 extra */ + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 16, + 17, + 17, + 17, + 17, + 18, + 18, + 18, + 18, + 19, + 19, + 19, + 19, + 20, + 20, + 20, + 20, + 21, + 21, + 21, + 21, + 16, + 72, + 78, +]); + +const dbase = new Uint16Array([ + /* Distance codes 0..29 base */ + 1, + 2, + 3, + 4, + 5, + 7, + 9, + 13, + 17, + 25, + 33, + 49, + 65, + 97, + 129, + 193, + 257, + 385, + 513, + 769, + 1025, + 1537, + 2049, + 3073, + 4097, + 6145, + 8193, + 12289, + 16385, + 24577, + 0, + 0, +]); + +const dext = new Uint8Array([ + /* Distance codes 0..29 extra */ + 16, + 16, + 16, + 16, + 17, + 17, + 18, + 18, + 19, + 19, + 20, + 20, + 21, + 21, + 22, + 22, + 23, + 23, + 24, + 24, + 25, + 25, + 26, + 26, + 27, + 27, + 28, + 28, + 29, + 29, + 64, + 64, +]); + +const inflate_table = ( + type, + lens, + lens_index, + codes, + table, + table_index, + work, + opts, +) => { + const bits = opts.bits; + //here = opts.here; /* table entry for duplication */ + + let len = 0; /* a code's length in bits */ + let sym = 0; /* index of code symbols */ + let min = 0, max = 0; /* minimum and maximum code lengths */ + let root = 0; /* number of index bits for root table */ + let curr = 0; /* number of index bits for current table */ + let drop = 0; /* code bits to drop for sub-table */ + let left = 0; /* number of prefix codes available */ + let used = 0; /* code entries in table used */ + let huff = 0; /* Huffman code */ + let incr; /* for incrementing code, index */ + let fill; /* index for replicating entries */ + let low; /* low bits for current root entry */ + let mask; /* mask for low root bits */ + let next; /* next available space in table */ + let base = null; /* base value table to use */ + let base_index = 0; + // let shoextra; /* extra bits table to use */ + let end; /* use base and extra for symbol > end */ + const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */ + const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */ + let extra = null; + let extra_index = 0; + + let here_bits, here_op, here_val; + + /* + Process a set of code lengths to create a canonical Huffman code. The + code lengths are lens[0..codes-1]. Each length corresponds to the + symbols 0..codes-1. The Huffman code is generated by first sorting the + symbols by length from short to long, and retaining the symbol order + for codes with equal lengths. Then the code starts with all zero bits + for the first code of the shortest length, and the codes are integer + increments for the same length, and zeros are appended as the length + increases. For the deflate format, these bits are stored backwards + from their more natural integer increment ordering, and so when the + decoding tables are built in the large loop below, the integer codes + are incremented backwards. + + This routine assumes, but does not check, that all of the entries in + lens[] are in the range 0..MAXBITS. The caller must assure this. + 1..MAXBITS is interpreted as that code length. zero means that that + symbol does not occur in this code. + + The codes are sorted by computing a count of codes for each length, + creating from that a table of starting indices for each length in the + sorted table, and then entering the symbols in order in the sorted + table. The sorted table is work[], with that space being provided by + the caller. + + The length counts are used for other purposes as well, i.e. finding + the minimum and maximum length codes, determining if there are any + codes at all, checking for a valid set of lengths, and looking ahead + at length counts to determine sub-table sizes when building the + decoding tables. + */ + + /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */ + for (len = 0; len <= MAXBITS; len++) { + count[len] = 0; + } + for (sym = 0; sym < codes; sym++) { + count[lens[lens_index + sym]]++; + } + + /* bound code lengths, force root to be within code lengths */ + root = bits; + for (max = MAXBITS; max >= 1; max--) { + if (count[max] !== 0) break; + } + if (root > max) { + root = max; + } + if (max === 0) { + /* no symbols to code at all */ + //table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */ + //table.bits[opts.table_index] = 1; //here.bits = (var char)1; + //table.val[opts.table_index++] = 0; //here.val = (var short)0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + //table.op[opts.table_index] = 64; + //table.bits[opts.table_index] = 1; + //table.val[opts.table_index++] = 0; + table[table_index++] = (1 << 24) | (64 << 16) | 0; + + opts.bits = 1; + return 0; /* no symbols, but wait for decoding to report error */ + } + for (min = 1; min < max; min++) { + if (count[min] !== 0) break; + } + if (root < min) { + root = min; + } + + /* check for an over-subscribed or incomplete set of lengths */ + left = 1; + for (len = 1; len <= MAXBITS; len++) { + left <<= 1; + left -= count[len]; + if (left < 0) { + return -1; + } /* over-subscribed */ + } + if (left > 0 && (type === CODES$1 || max !== 1)) { + return -1; /* incomplete set */ + } + + /* generate offsets into symbol table for each length for sorting */ + offs[1] = 0; + for (len = 1; len < MAXBITS; len++) { + offs[len + 1] = offs[len] + count[len]; + } + + /* sort symbols by length, by symbol order within each length */ + for (sym = 0; sym < codes; sym++) { + if (lens[lens_index + sym] !== 0) { + work[offs[lens[lens_index + sym]]++] = sym; + } + } + + /* + Create and fill in decoding tables. In this loop, the table being + filled is at next and has curr index bits. The code being used is huff + with length len. That code is converted to an index by dropping drop + bits off of the bottom. For codes where len is less than drop + curr, + those top drop + curr - len bits are incremented through all values to + fill the table with replicated entries. + + root is the number of index bits for the root table. When len exceeds + root, sub-tables are created pointed to by the root entry with an index + of the low root bits of huff. This is saved in low to check for when a + new sub-table should be started. drop is zero when the root table is + being filled, and drop is root when sub-tables are being filled. + + When a new sub-table is needed, it is necessary to look ahead in the + code lengths to determine what size sub-table is needed. The length + counts are used for this, and so count[] is decremented as codes are + entered in the tables. + + used keeps track of how many table entries have been allocated from the + provided *table space. It is checked for LENS and DIST tables against + the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in + the initial root table size constants. See the comments in inftrees.h + for more information. + + sym increments through all symbols, and the loop terminates when + all codes of length max, i.e. all codes, have been processed. This + routine permits incomplete codes, so another loop after this one fills + in the rest of the decoding tables with invalid code markers. + */ + + /* set up for code type */ + // poor man optimization - use if-else instead of switch, + // to avoid deopts in old v8 + if (type === CODES$1) { + base = extra = work; /* dummy value--not used */ + end = 19; + } else if (type === LENS$1) { + base = lbase; + base_index -= 257; + extra = lext; + extra_index -= 257; + end = 256; + } else { + /* DISTS */ + base = dbase; + extra = dext; + end = -1; + } + + /* initialize opts for loop */ + huff = 0; /* starting code */ + sym = 0; /* starting code symbol */ + len = min; /* starting code length */ + next = table_index; /* current table to fill in */ + curr = root; /* current table index bits */ + drop = 0; /* current bits to drop from code for index */ + low = -1; /* trigger new sub-table when len > root */ + used = 1 << root; /* use root table entries */ + mask = used - 1; /* mask for comparing low */ + + /* check available table space */ + if ( + (type === LENS$1 && used > ENOUGH_LENS$1) || + (type === DISTS$1 && used > ENOUGH_DISTS$1) + ) { + return 1; + } + + /* process all codes and make table entries */ + for (;;) { + /* create table entry */ + here_bits = len - drop; + if (work[sym] < end) { + here_op = 0; + here_val = work[sym]; + } else if (work[sym] > end) { + here_op = extra[extra_index + work[sym]]; + here_val = base[base_index + work[sym]]; + } else { + here_op = 32 + 64; /* end of block */ + here_val = 0; + } + + /* replicate for those indices with low len bits equal to huff */ + incr = 1 << (len - drop); + fill = 1 << curr; + min = fill; /* save offset to next table */ + do { + fill -= incr; + table[next + (huff >> drop) + fill] = (here_bits << 24) | + (here_op << 16) | here_val | 0; + } while (fill !== 0); + + /* backwards increment the len-bit code huff */ + incr = 1 << (len - 1); + while (huff & incr) { + incr >>= 1; + } + if (incr !== 0) { + huff &= incr - 1; + huff += incr; + } else { + huff = 0; + } + + /* go to next symbol, update count, len */ + sym++; + if (--count[len] === 0) { + if (len === max) break; + len = lens[lens_index + work[sym]]; + } + + /* create new sub-table if needed */ + if (len > root && (huff & mask) !== low) { + /* if first time, transition to sub-tables */ + if (drop === 0) { + drop = root; + } + + /* increment past last table */ + next += min; /* here min is 1 << curr */ + + /* determine length of next table */ + curr = len - drop; + left = 1 << curr; + while (curr + drop < max) { + left -= count[curr + drop]; + if (left <= 0) break; + curr++; + left <<= 1; + } + + /* check for enough space */ + used += 1 << curr; + if ( + (type === LENS$1 && used > ENOUGH_LENS$1) || + (type === DISTS$1 && used > ENOUGH_DISTS$1) + ) { + return 1; + } + + /* point entry in root table to sub-table */ + low = huff & mask; + /*table.op[low] = curr; + table.bits[low] = root; + table.val[low] = next - opts.table_index;*/ + table[low] = (root << 24) | (curr << 16) | (next - table_index) | 0; + } + } + + /* fill in remaining table entry if code is incomplete (guaranteed to have + at most one remaining entry, since if the code is incomplete, the + maximum code length that was allowed to get this far is one bit) */ + if (huff !== 0) { + //table.op[next + huff] = 64; /* invalid code marker */ + //table.bits[next + huff] = len - drop; + //table.val[next + huff] = 0; + table[next + huff] = ((len - drop) << 24) | (64 << 16) | 0; + } + + /* set return parameters */ + //opts.table_index += used; + opts.bits = root; + return 0; +}; + +var inftrees = inflate_table; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +const CODES = 0; +const LENS = 1; +const DISTS = 2; + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + +const { + Z_FINISH: Z_FINISH$1, + Z_BLOCK, + Z_TREES, + Z_OK: Z_OK$1, + Z_STREAM_END: Z_STREAM_END$1, + Z_NEED_DICT: Z_NEED_DICT$1, + Z_STREAM_ERROR: Z_STREAM_ERROR$1, + Z_DATA_ERROR: Z_DATA_ERROR$1, + Z_MEM_ERROR: Z_MEM_ERROR$1, + Z_BUF_ERROR, + Z_DEFLATED, +} = constants$2; + +/* STATES ====================================================================*/ +/* ===========================================================================*/ + +const HEAD = 1; /* i: waiting for magic header */ +const FLAGS = 2; /* i: waiting for method and flags (gzip) */ +const TIME = 3; /* i: waiting for modification time (gzip) */ +const OS = 4; /* i: waiting for extra flags and operating system (gzip) */ +const EXLEN = 5; /* i: waiting for extra length (gzip) */ +const EXTRA = 6; /* i: waiting for extra bytes (gzip) */ +const NAME = 7; /* i: waiting for end of file name (gzip) */ +const COMMENT = 8; /* i: waiting for end of comment (gzip) */ +const HCRC = 9; /* i: waiting for header crc (gzip) */ +const DICTID = 10; /* i: waiting for dictionary check value */ +const DICT = 11; /* waiting for inflateSetDictionary() call */ +const TYPE = 12; /* i: waiting for type bits, including last-flag bit */ +const TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */ +const STORED = 14; /* i: waiting for stored size (length and complement) */ +const COPY_ = 15; /* i/o: same as COPY below, but only first time in */ +const COPY = 16; /* i/o: waiting for input or output to copy stored block */ +const TABLE = 17; /* i: waiting for dynamic block table lengths */ +const LENLENS = 18; /* i: waiting for code length code lengths */ +const CODELENS = 19; /* i: waiting for length/lit and distance code lengths */ +const LEN_ = 20; /* i: same as LEN below, but only first time in */ +const LEN = 21; /* i: waiting for length/lit/eob code */ +const LENEXT = 22; /* i: waiting for length extra bits */ +const DIST = 23; /* i: waiting for distance code */ +const DISTEXT = 24; /* i: waiting for distance extra bits */ +const MATCH = 25; /* o: waiting for output space to copy string */ +const LIT = 26; /* o: waiting for output space to write literal */ +const CHECK = 27; /* i: waiting for 32-bit check value */ +const LENGTH = 28; /* i: waiting for 32-bit length (gzip) */ +const DONE = 29; /* finished check, done -- remain here until reset */ +const BAD = 30; /* got a data error -- remain here until reset */ +const MEM = 31; /* got an inflate() memory error -- remain here until reset */ +const SYNC = 32; /* looking for synchronization bytes to restart inflate() */ + +/* ===========================================================================*/ + +const ENOUGH_LENS = 852; +const ENOUGH_DISTS = 592; +//const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS); + +const MAX_WBITS = 15; +/* 32K LZ77 window */ +const DEF_WBITS = MAX_WBITS; + +const zswap32 = (q) => { + return (((q >>> 24) & 0xff) + + ((q >>> 8) & 0xff00) + + ((q & 0xff00) << 8) + + ((q & 0xff) << 24)); +}; + +function InflateState() { + this.mode = 0; /* current inflate mode */ + this.last = false; /* true if processing last block */ + this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */ + this.havedict = false; /* true if dictionary provided */ + this.flags = 0; /* gzip header method and flags (0 if zlib) */ + this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */ + this.check = 0; /* protected copy of check value */ + this.total = 0; /* protected copy of output count */ + // TODO: may be {} + this.head = null; /* where to save gzip header information */ + + /* sliding window */ + this.wbits = 0; /* log base 2 of requested window size */ + this.wsize = 0; /* window size or zero if not using window */ + this.whave = 0; /* valid bytes in the window */ + this.wnext = 0; /* window write index */ + this.window = null; /* allocated sliding window, if needed */ + + /* bit accumulator */ + this.hold = 0; /* input bit accumulator */ + this.bits = 0; /* number of bits in "in" */ + + /* for string and stored block copying */ + this.length = 0; /* literal or length of data to copy */ + this.offset = 0; /* distance back to copy string from */ + + /* for table and code decoding */ + this.extra = 0; /* extra bits needed */ + + /* fixed and dynamic code tables */ + this.lencode = null; /* starting table for length/literal codes */ + this.distcode = null; /* starting table for distance codes */ + this.lenbits = 0; /* index bits for lencode */ + this.distbits = 0; /* index bits for distcode */ + + /* dynamic table building */ + this.ncode = 0; /* number of code length code lengths */ + this.nlen = 0; /* number of length code lengths */ + this.ndist = 0; /* number of distance code lengths */ + this.have = 0; /* number of code lengths in lens[] */ + this.next = null; /* next available space in codes[] */ + + this.lens = new Uint16Array(320); /* temporary storage for code lengths */ + this.work = new Uint16Array(288); /* work area for code table building */ + + /* + because we don't have pointers in js, we use lencode and distcode directly + as buffers so we don't need codes + */ + //this.codes = new Int32Array(ENOUGH); /* space for code tables */ + this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */ + this.distdyn = null; /* dynamic table for distance codes (JS specific) */ + this.sane = 0; /* if false, allow invalid distance too far */ + this.back = 0; /* bits back of last unprocessed length/lit */ + this.was = 0; /* initial length of match */ +} + +const inflateResetKeep = (strm) => { + if (!strm || !strm.state) return Z_STREAM_ERROR$1; + const state = strm.state; + strm.total_in = strm.total_out = state.total = 0; + strm.msg = ""; /*Z_NULL*/ + if (state.wrap) { + /* to support ill-conceived Java test suite */ + strm.adler = state.wrap & 1; + } + state.mode = HEAD; + state.last = 0; + state.havedict = 0; + state.dmax = 32768; + state.head = null /*Z_NULL*/; + state.hold = 0; + state.bits = 0; + //state.lencode = state.distcode = state.next = state.codes; + state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS); + state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS); + + state.sane = 1; + state.back = -1; + //Tracev((stderr, "inflate: reset\n")); + return Z_OK$1; +}; + +const inflateReset = (strm) => { + if (!strm || !strm.state) return Z_STREAM_ERROR$1; + const state = strm.state; + state.wsize = 0; + state.whave = 0; + state.wnext = 0; + return inflateResetKeep(strm); +}; + +const inflateReset2 = (strm, windowBits) => { + let wrap; + + /* get the state */ + if (!strm || !strm.state) return Z_STREAM_ERROR$1; + const state = strm.state; + + /* extract wrap request from windowBits parameter */ + if (windowBits < 0) { + wrap = 0; + windowBits = -windowBits; + } else { + wrap = (windowBits >> 4) + 1; + if (windowBits < 48) { + windowBits &= 15; + } + } + + /* set number of window bits, free window if different */ + if (windowBits && (windowBits < 8 || windowBits > 15)) { + return Z_STREAM_ERROR$1; + } + if (state.window !== null && state.wbits !== windowBits) { + state.window = null; + } + + /* update state and reset the rest of it */ + state.wrap = wrap; + state.wbits = windowBits; + return inflateReset(strm); +}; + +const inflateInit2 = (strm, windowBits) => { + if (!strm) return Z_STREAM_ERROR$1; + //strm.msg = Z_NULL; /* in case we return an error */ + + const state = new InflateState(); + + //if (state === Z_NULL) return Z_MEM_ERROR; + //Tracev((stderr, "inflate: allocated\n")); + strm.state = state; + state.window = null /*Z_NULL*/; + const ret = inflateReset2(strm, windowBits); + if (ret !== Z_OK$1) { + strm.state = null /*Z_NULL*/; + } + return ret; +}; + +const inflateInit = (strm) => { + return inflateInit2(strm, DEF_WBITS); +}; + +/* + Return state with length and distance decoding tables and index sizes set to + fixed code decoding. Normally this returns fixed tables from inffixed.h. + If BUILDFIXED is defined, then instead this routine builds the tables the + first time it's called, and returns those tables the first time and + thereafter. This reduces the size of the code by about 2K bytes, in + exchange for a little execution time. However, BUILDFIXED should not be + used for threaded applications, since the rewriting of the tables and virgin + may not be thread-safe. + */ +let virgin = true; + +let lenfix, distfix; // We have no pointers in JS, so keep tables separate + +const fixedtables = (state) => { + /* build fixed huffman tables if first call (may not be thread safe) */ + if (virgin) { + lenfix = new Int32Array(512); + distfix = new Int32Array(32); + + /* literal/length table */ + let sym = 0; + while (sym < 144) state.lens[sym++] = 8; + while (sym < 256) state.lens[sym++] = 9; + while (sym < 280) state.lens[sym++] = 7; + while (sym < 288) state.lens[sym++] = 8; + + inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, { bits: 9 }); + + /* distance table */ + sym = 0; + while (sym < 32) state.lens[sym++] = 5; + + inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, { bits: 5 }); + + /* do this just once */ + virgin = false; + } + + state.lencode = lenfix; + state.lenbits = 9; + state.distcode = distfix; + state.distbits = 5; +}; + +/* + Update the window with the last wsize (normally 32K) bytes written before + returning. If window does not exist yet, create it. This is only called + when a window is already in use, or when output has been written during this + inflate call, but the end of the deflate stream has not been reached yet. + It is also called to create a window for dictionary data when a dictionary + is loaded. + + Providing output buffers larger than 32K to inflate() should provide a speed + advantage, since only the last 32K of output is copied to the sliding window + upon return from inflate(), and since all distances after the first 32K of + output will fall in the output data, making match copies simpler and faster. + The advantage may be dependent on the size of the processor's data caches. + */ +const updatewindow = (strm, src, end, copy) => { + let dist; + const state = strm.state; + + /* if it hasn't been done already, allocate space for the window */ + if (state.window === null) { + state.wsize = 1 << state.wbits; + state.wnext = 0; + state.whave = 0; + + state.window = new Uint8Array(state.wsize); + } + + /* copy state->wsize or less output bytes into the circular window */ + if (copy >= state.wsize) { + state.window.set(src.subarray(end - state.wsize, end), 0); + state.wnext = 0; + state.whave = state.wsize; + } else { + dist = state.wsize - state.wnext; + if (dist > copy) { + dist = copy; + } + //zmemcpy(state->window + state->wnext, end - copy, dist); + state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext); + copy -= dist; + if (copy) { + //zmemcpy(state->window, end - copy, copy); + state.window.set(src.subarray(end - copy, end), 0); + state.wnext = copy; + state.whave = state.wsize; + } else { + state.wnext += dist; + if (state.wnext === state.wsize) state.wnext = 0; + if (state.whave < state.wsize) state.whave += dist; + } + } + return 0; +}; + +const inflate$2 = (strm, flush) => { + let state; + let input, output; // input/output buffers + let next; /* next input INDEX */ + let put; /* next output INDEX */ + let have, left; /* available input and output */ + let hold; /* bit buffer */ + let bits; /* bits in bit buffer */ + let _in, _out; /* save starting available input and output */ + let copy; /* number of stored or match bytes to copy */ + let from; /* where to copy match bytes from */ + let from_source; + let here = 0; /* current decoding table entry */ + let here_bits, here_op, here_val; // paked "here" denormalized (JS specific) + //let last; /* parent table entry */ + let last_bits, last_op, last_val; // paked "last" denormalized (JS specific) + let len; /* length to copy for repeats, bits to drop */ + let ret; /* return code */ + const hbuf = new Uint8Array(4); /* buffer for gzip header crc calculation */ + let opts; + + let n; // temporary variable for NEED_BITS + + const order = /* permutation of code lengths */ + new Uint8Array([ + 16, + 17, + 18, + 0, + 8, + 7, + 9, + 6, + 10, + 5, + 11, + 4, + 12, + 3, + 13, + 2, + 14, + 1, + 15, + ]); + + if ( + !strm || !strm.state || !strm.output || + (!strm.input && strm.avail_in !== 0) + ) { + return Z_STREAM_ERROR$1; + } + + state = strm.state; + if (state.mode === TYPE) state.mode = TYPEDO; /* skip check */ + + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + _in = have; + _out = left; + ret = Z_OK$1; + + inf_leave: + // goto emulation + for (;;) { + switch (state.mode) { + case HEAD: + if (state.wrap === 0) { + state.mode = TYPEDO; + break; + } + //=== NEEDBITS(16); + while (bits < 16) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((state.wrap & 2) && hold === 0x8b1f) { + /* gzip header */ + state.check = 0 /*crc32(0L, Z_NULL, 0)*/; + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = FLAGS; + break; + } + state.flags = 0; /* expect zlib header */ + if (state.head) { + state.head.done = false; + } + if ( + !(state.wrap & 1) || /* check if zlib header allowed */ + (((hold & 0xff) /*BITS(8)*/ << 8) + (hold >> 8)) % 31 + ) { + strm.msg = "incorrect header check"; + state.mode = BAD; + break; + } + if ((hold & 0x0f) /*BITS(4)*/ !== Z_DEFLATED) { + strm.msg = "unknown compression method"; + state.mode = BAD; + break; + } + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + len = (hold & 0x0f) /*BITS(4)*/ + 8; + if (state.wbits === 0) { + state.wbits = len; + } else if (len > state.wbits) { + strm.msg = "invalid window size"; + state.mode = BAD; + break; + } + + // !!! pako patch. Force use `options.windowBits` if passed. + // Required to always use max window size by default. + state.dmax = 1 << state.wbits; + //state.dmax = 1 << len; + + //Tracev((stderr, "inflate: zlib header ok\n")); + strm.adler = state.check = 1 /*adler32(0L, Z_NULL, 0)*/; + state.mode = hold & 0x200 ? DICTID : TYPE; + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + break; + case FLAGS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.flags = hold; + if ((state.flags & 0xff) !== Z_DEFLATED) { + strm.msg = "unknown compression method"; + state.mode = BAD; + break; + } + if (state.flags & 0xe000) { + strm.msg = "unknown header flags set"; + state.mode = BAD; + break; + } + if (state.head) { + state.head.text = (hold >> 8) & 1; + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = TIME; + /* falls through */ + case TIME: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.time = hold; + } + if (state.flags & 0x0200) { + //=== CRC4(state.check, hold) + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + hbuf[2] = (hold >>> 16) & 0xff; + hbuf[3] = (hold >>> 24) & 0xff; + state.check = crc32_1(state.check, hbuf, 4, 0); + //=== + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = OS; + /* falls through */ + case OS: + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (state.head) { + state.head.xflags = hold & 0xff; + state.head.os = hold >> 8; + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = EXLEN; + /* falls through */ + case EXLEN: + if (state.flags & 0x0400) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length = hold; + if (state.head) { + state.head.extra_len = hold; + } + if (state.flags & 0x0200) { + //=== CRC2(state.check, hold); + hbuf[0] = hold & 0xff; + hbuf[1] = (hold >>> 8) & 0xff; + state.check = crc32_1(state.check, hbuf, 2, 0); + //===// + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } else if (state.head) { + state.head.extra = null /*Z_NULL*/; + } + state.mode = EXTRA; + /* falls through */ + case EXTRA: + if (state.flags & 0x0400) { + copy = state.length; + if (copy > have) copy = have; + if (copy) { + if (state.head) { + len = state.head.extra_len - state.length; + if (!state.head.extra) { + // Use untyped array for more convenient processing later + state.head.extra = new Uint8Array(state.head.extra_len); + } + state.head.extra.set( + input.subarray( + next, + // extra field is limited to 65536 bytes + // - no need for additional size check + next + copy, + ), + /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/ + len, + ); + //zmemcpy(state.head.extra + len, next, + // len + copy > state.head.extra_max ? + // state.head.extra_max - len : copy); + } + if (state.flags & 0x0200) { + state.check = crc32_1(state.check, input, copy, next); + } + have -= copy; + next += copy; + state.length -= copy; + } + if (state.length) break inf_leave; + } + state.length = 0; + state.mode = NAME; + /* falls through */ + case NAME: + if (state.flags & 0x0800) { + if (have === 0) break inf_leave; + copy = 0; + do { + // TODO: 2 or 1 bytes? + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if ( + state.head && len && + (state.length < 65536 /*state.head.name_max*/) + ) { + state.head.name += String.fromCharCode(len); + } + } while (len && copy < have); + + if (state.flags & 0x0200) { + state.check = crc32_1(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) break inf_leave; + } else if (state.head) { + state.head.name = null; + } + state.length = 0; + state.mode = COMMENT; + /* falls through */ + case COMMENT: + if (state.flags & 0x1000) { + if (have === 0) break inf_leave; + copy = 0; + do { + len = input[next + copy++]; + /* use constant limit because in js we should not preallocate memory */ + if ( + state.head && len && + (state.length < 65536 /*state.head.comm_max*/) + ) { + state.head.comment += String.fromCharCode(len); + } + } while (len && copy < have); + if (state.flags & 0x0200) { + state.check = crc32_1(state.check, input, copy, next); + } + have -= copy; + next += copy; + if (len) break inf_leave; + } else if (state.head) { + state.head.comment = null; + } + state.mode = HCRC; + /* falls through */ + case HCRC: + if (state.flags & 0x0200) { + //=== NEEDBITS(16); */ + while (bits < 16) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.check & 0xffff)) { + strm.msg = "header crc mismatch"; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + } + if (state.head) { + state.head.hcrc = (state.flags >> 9) & 1; + state.head.done = true; + } + strm.adler = state.check = 0; + state.mode = TYPE; + break; + case DICTID: + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + strm.adler = state.check = zswap32(hold); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = DICT; + /* falls through */ + case DICT: + if (state.havedict === 0) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + return Z_NEED_DICT$1; + } + strm.adler = state.check = 1 /*adler32(0L, Z_NULL, 0)*/; + state.mode = TYPE; + /* falls through */ + case TYPE: + if (flush === Z_BLOCK || flush === Z_TREES) break inf_leave; + /* falls through */ + case TYPEDO: + if (state.last) { + //--- BYTEBITS() ---// + hold >>>= bits & 7; + bits -= bits & 7; + //---// + state.mode = CHECK; + break; + } + //=== NEEDBITS(3); */ + while (bits < 3) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.last = hold & 0x01 /*BITS(1)*/; + //--- DROPBITS(1) ---// + hold >>>= 1; + bits -= 1; + //---// + + switch ((hold & 0x03) /*BITS(2)*/) { + case 0: /* stored block */ + //Tracev((stderr, "inflate: stored block%s\n", + // state.last ? " (last)" : "")); + state.mode = STORED; + break; + case 1: /* fixed block */ + fixedtables(state); + //Tracev((stderr, "inflate: fixed codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = LEN_; /* decode codes */ + if (flush === Z_TREES) { + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break inf_leave; + } + break; + case 2: /* dynamic block */ + //Tracev((stderr, "inflate: dynamic codes block%s\n", + // state.last ? " (last)" : "")); + state.mode = TABLE; + break; + case 3: + strm.msg = "invalid block type"; + state.mode = BAD; + } + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + break; + case STORED: + //--- BYTEBITS() ---// /* go to byte boundary */ + hold >>>= bits & 7; + bits -= bits & 7; + //---// + //=== NEEDBITS(32); */ + while (bits < 32) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) { + strm.msg = "invalid stored block lengths"; + state.mode = BAD; + break; + } + state.length = hold & 0xffff; + //Tracev((stderr, "inflate: stored length %u\n", + // state.length)); + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + state.mode = COPY_; + if (flush === Z_TREES) break inf_leave; + /* falls through */ + case COPY_: + state.mode = COPY; + /* falls through */ + case COPY: + copy = state.length; + if (copy) { + if (copy > have) copy = have; + if (copy > left) copy = left; + if (copy === 0) break inf_leave; + //--- zmemcpy(put, next, copy); --- + output.set(input.subarray(next, next + copy), put); + //---// + have -= copy; + next += copy; + left -= copy; + put += copy; + state.length -= copy; + break; + } + //Tracev((stderr, "inflate: stored end\n")); + state.mode = TYPE; + break; + case TABLE: + //=== NEEDBITS(14); */ + while (bits < 14) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.nlen = (hold & 0x1f) /*BITS(5)*/ + 257; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ndist = (hold & 0x1f) /*BITS(5)*/ + 1; + //--- DROPBITS(5) ---// + hold >>>= 5; + bits -= 5; + //---// + state.ncode = (hold & 0x0f) /*BITS(4)*/ + 4; + //--- DROPBITS(4) ---// + hold >>>= 4; + bits -= 4; + //---// + //#ifndef PKZIP_BUG_WORKAROUND + if (state.nlen > 286 || state.ndist > 30) { + strm.msg = "too many length or distance symbols"; + state.mode = BAD; + break; + } + //#endif + //Tracev((stderr, "inflate: table sizes ok\n")); + state.have = 0; + state.mode = LENLENS; + /* falls through */ + case LENLENS: + while (state.have < state.ncode) { + //=== NEEDBITS(3); + while (bits < 3) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.lens[order[state.have++]] = hold & 0x07; //BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } + while (state.have < 19) { + state.lens[order[state.have++]] = 0; + } + // We have separate tables & no pointers. 2 commented lines below not needed. + //state.next = state.codes; + //state.lencode = state.next; + // Switch to use dynamic table + state.lencode = state.lendyn; + state.lenbits = 7; + + opts = { bits: state.lenbits }; + ret = inftrees( + CODES, + state.lens, + 0, + 19, + state.lencode, + 0, + state.work, + opts, + ); + state.lenbits = opts.bits; + + if (ret) { + strm.msg = "invalid code lengths set"; + state.mode = BAD; + break; + } + //Tracev((stderr, "inflate: code lengths ok\n")); + state.have = 0; + state.mode = CODELENS; + /* falls through */ + case CODELENS: + while (state.have < state.nlen + state.ndist) { + for (;;) { + here = state + .lencode[ + hold & ((1 << state.lenbits) - 1) + ]; /*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) break; + //--- PULLBYTE() ---// + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_val < 16) { + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.lens[state.have++] = here_val; + } else { + if (here_val === 16) { + //=== NEEDBITS(here.bits + 2); + n = here_bits + 2; + while (bits < n) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + if (state.have === 0) { + strm.msg = "invalid bit length repeat"; + state.mode = BAD; + break; + } + len = state.lens[state.have - 1]; + copy = 3 + (hold & 0x03); //BITS(2); + //--- DROPBITS(2) ---// + hold >>>= 2; + bits -= 2; + //---// + } else if (here_val === 17) { + //=== NEEDBITS(here.bits + 3); + n = here_bits + 3; + while (bits < n) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 3 + (hold & 0x07); //BITS(3); + //--- DROPBITS(3) ---// + hold >>>= 3; + bits -= 3; + //---// + } else { + //=== NEEDBITS(here.bits + 7); + n = here_bits + 7; + while (bits < n) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + len = 0; + copy = 11 + (hold & 0x7f); //BITS(7); + //--- DROPBITS(7) ---// + hold >>>= 7; + bits -= 7; + //---// + } + if (state.have + copy > state.nlen + state.ndist) { + strm.msg = "invalid bit length repeat"; + state.mode = BAD; + break; + } + while (copy--) { + state.lens[state.have++] = len; + } + } + } + + /* handle error breaks in while */ + if (state.mode === BAD) break; + + /* check for end-of-block code (better have one) */ + if (state.lens[256] === 0) { + strm.msg = "invalid code -- missing end-of-block"; + state.mode = BAD; + break; + } + + /* build code tables -- note: do not change the lenbits or distbits + values here (9 and 6) without reading the comments in inftrees.h + concerning the ENOUGH constants, which depend on those values */ + state.lenbits = 9; + + opts = { bits: state.lenbits }; + ret = inftrees( + LENS, + state.lens, + 0, + state.nlen, + state.lencode, + 0, + state.work, + opts, + ); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.lenbits = opts.bits; + // state.lencode = state.next; + + if (ret) { + strm.msg = "invalid literal/lengths set"; + state.mode = BAD; + break; + } + + state.distbits = 6; + //state.distcode.copy(state.codes); + // Switch to use dynamic table + state.distcode = state.distdyn; + opts = { bits: state.distbits }; + ret = inftrees( + DISTS, + state.lens, + state.nlen, + state.ndist, + state.distcode, + 0, + state.work, + opts, + ); + // We have separate tables & no pointers. 2 commented lines below not needed. + // state.next_index = opts.table_index; + state.distbits = opts.bits; + // state.distcode = state.next; + + if (ret) { + strm.msg = "invalid distances set"; + state.mode = BAD; + break; + } + //Tracev((stderr, 'inflate: codes ok\n')); + state.mode = LEN_; + if (flush === Z_TREES) break inf_leave; + /* falls through */ + case LEN_: + state.mode = LEN; + /* falls through */ + case LEN: + if (have >= 6 && left >= 258) { + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + inffast(strm, _out); + //--- LOAD() --- + put = strm.next_out; + output = strm.output; + left = strm.avail_out; + next = strm.next_in; + input = strm.input; + have = strm.avail_in; + hold = state.hold; + bits = state.bits; + //--- + + if (state.mode === TYPE) { + state.back = -1; + } + break; + } + state.back = 0; + for (;;) { + here = state + .lencode[ + hold & ((1 << state.lenbits) - 1) + ]; /*BITS(state.lenbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if (here_bits <= bits) break; + //--- PULLBYTE() ---// + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if (here_op && (here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.lencode[ + last_val + + ((hold & + ((1 << (last_bits + last_op)) - + 1)) /*BITS(last.bits + last.op)*/ >> last_bits) + ]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) break; + //--- PULLBYTE() ---// + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + state.length = here_val; + if (here_op === 0) { + //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ? + // "inflate: literal '%c'\n" : + // "inflate: literal 0x%02x\n", here.val)); + state.mode = LIT; + break; + } + if (here_op & 32) { + //Tracevv((stderr, "inflate: end of block\n")); + state.back = -1; + state.mode = TYPE; + break; + } + if (here_op & 64) { + strm.msg = "invalid literal/length code"; + state.mode = BAD; + break; + } + state.extra = here_op & 15; + state.mode = LENEXT; + /* falls through */ + case LENEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.length += hold & ((1 << state.extra) - 1) /*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //Tracevv((stderr, "inflate: length %u\n", state.length)); + state.was = state.length; + state.mode = DIST; + /* falls through */ + case DIST: + for (;;) { + here = state + .distcode[ + hold & ((1 << state.distbits) - 1) + ]; /*BITS(state.distbits)*/ + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((here_bits) <= bits) break; + //--- PULLBYTE() ---// + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + if ((here_op & 0xf0) === 0) { + last_bits = here_bits; + last_op = here_op; + last_val = here_val; + for (;;) { + here = state.distcode[ + last_val + + ((hold & + ((1 << (last_bits + last_op)) - + 1)) /*BITS(last.bits + last.op)*/ >> last_bits) + ]; + here_bits = here >>> 24; + here_op = (here >>> 16) & 0xff; + here_val = here & 0xffff; + + if ((last_bits + here_bits) <= bits) break; + //--- PULLBYTE() ---// + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + //---// + } + //--- DROPBITS(last.bits) ---// + hold >>>= last_bits; + bits -= last_bits; + //---// + state.back += last_bits; + } + //--- DROPBITS(here.bits) ---// + hold >>>= here_bits; + bits -= here_bits; + //---// + state.back += here_bits; + if (here_op & 64) { + strm.msg = "invalid distance code"; + state.mode = BAD; + break; + } + state.offset = here_val; + state.extra = (here_op) & 15; + state.mode = DISTEXT; + /* falls through */ + case DISTEXT: + if (state.extra) { + //=== NEEDBITS(state.extra); + n = state.extra; + while (bits < n) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + state.offset += hold & ((1 << state.extra) - 1) /*BITS(state.extra)*/; + //--- DROPBITS(state.extra) ---// + hold >>>= state.extra; + bits -= state.extra; + //---// + state.back += state.extra; + } + //#ifdef INFLATE_STRICT + if (state.offset > state.dmax) { + strm.msg = "invalid distance too far back"; + state.mode = BAD; + break; + } + //#endif + //Tracevv((stderr, "inflate: distance %u\n", state.offset)); + state.mode = MATCH; + /* falls through */ + case MATCH: + if (left === 0) break inf_leave; + copy = _out - left; + if (state.offset > copy) { + /* copy from window */ + copy = state.offset - copy; + if (copy > state.whave) { + if (state.sane) { + strm.msg = "invalid distance too far back"; + state.mode = BAD; + break; + } + // (!) This block is disabled in zlib defaults, + // don't enable it for binary compatibility + //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR + // Trace((stderr, "inflate.c too far\n")); + // copy -= state.whave; + // if (copy > state.length) { copy = state.length; } + // if (copy > left) { copy = left; } + // left -= copy; + // state.length -= copy; + // do { + // output[put++] = 0; + // } while (--copy); + // if (state.length === 0) { state.mode = LEN; } + // break; + //#endif + } + if (copy > state.wnext) { + copy -= state.wnext; + from = state.wsize - copy; + } else { + from = state.wnext - copy; + } + if (copy > state.length) copy = state.length; + from_source = state.window; + } else { + /* copy from output */ + from_source = output; + from = put - state.offset; + copy = state.length; + } + if (copy > left) copy = left; + left -= copy; + state.length -= copy; + do { + output[put++] = from_source[from++]; + } while (--copy); + if (state.length === 0) state.mode = LEN; + break; + case LIT: + if (left === 0) break inf_leave; + output[put++] = state.length; + left--; + state.mode = LEN; + break; + case CHECK: + if (state.wrap) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) break inf_leave; + have--; + // Use '|' instead of '+' to make sure that result is signed + hold |= input[next++] << bits; + bits += 8; + } + //===// + _out -= left; + strm.total_out += _out; + state.total += _out; + if (_out) { + strm.adler = + state.check = + /*UPDATE(state.check, put - _out, _out);*/ + state.flags + ? crc32_1(state.check, output, _out, put - _out) + : adler32_1(state.check, output, _out, put - _out); + } + _out = left; + // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too + if ((state.flags ? hold : zswap32(hold)) !== state.check) { + strm.msg = "incorrect data check"; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: check matches trailer\n")); + } + state.mode = LENGTH; + /* falls through */ + case LENGTH: + if (state.wrap && state.flags) { + //=== NEEDBITS(32); + while (bits < 32) { + if (have === 0) break inf_leave; + have--; + hold += input[next++] << bits; + bits += 8; + } + //===// + if (hold !== (state.total & 0xffffffff)) { + strm.msg = "incorrect length check"; + state.mode = BAD; + break; + } + //=== INITBITS(); + hold = 0; + bits = 0; + //===// + //Tracev((stderr, "inflate: length matches trailer\n")); + } + state.mode = DONE; + /* falls through */ + case DONE: + ret = Z_STREAM_END$1; + break inf_leave; + case BAD: + ret = Z_DATA_ERROR$1; + break inf_leave; + case MEM: + return Z_MEM_ERROR$1; + case SYNC: + /* falls through */ + default: + return Z_STREAM_ERROR$1; + } + } + + // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave" + + /* + Return from inflate(), updating the total counts and the check value. + If there was no progress during the inflate() call, return a buffer + error. Call updatewindow() to create and/or update the window state. + Note: a memory error from inflate() is non-recoverable. + */ + + //--- RESTORE() --- + strm.next_out = put; + strm.avail_out = left; + strm.next_in = next; + strm.avail_in = have; + state.hold = hold; + state.bits = bits; + //--- + + if ( + state.wsize || (_out !== strm.avail_out && state.mode < BAD && + (state.mode < CHECK || flush !== Z_FINISH$1)) + ) { + if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)); + } + _in -= strm.avail_in; + _out -= strm.avail_out; + strm.total_in += _in; + strm.total_out += _out; + state.total += _out; + if (state.wrap && _out) { + strm.adler = + state + .check = /*UPDATE(state.check, strm.next_out - _out, _out);*/ + state.flags + ? crc32_1(state.check, output, _out, strm.next_out - _out) + : adler32_1(state.check, output, _out, strm.next_out - _out); + } + strm.data_type = state.bits + (state.last ? 64 : 0) + + (state.mode === TYPE ? 128 : 0) + + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0); + if (((_in === 0 && _out === 0) || flush === Z_FINISH$1) && ret === Z_OK$1) { + ret = Z_BUF_ERROR; + } + return ret; +}; + +const inflateEnd = (strm) => { + if (!strm || !strm.state /*|| strm->zfree == (free_func)0*/) { + return Z_STREAM_ERROR$1; + } + + let state = strm.state; + if (state.window) { + state.window = null; + } + strm.state = null; + return Z_OK$1; +}; + +const inflateGetHeader = (strm, head) => { + /* check state */ + if (!strm || !strm.state) return Z_STREAM_ERROR$1; + const state = strm.state; + if ((state.wrap & 2) === 0) return Z_STREAM_ERROR$1; + + /* save header structure */ + state.head = head; + head.done = false; + return Z_OK$1; +}; + +const inflateSetDictionary = (strm, dictionary) => { + const dictLength = dictionary.length; + + let state; + let dictid; + let ret; + + /* check state */ + if (!strm /* == Z_NULL */ || !strm.state /* == Z_NULL */) { + return Z_STREAM_ERROR$1; + } + state = strm.state; + + if (state.wrap !== 0 && state.mode !== DICT) { + return Z_STREAM_ERROR$1; + } + + /* check for correct dictionary identifier */ + if (state.mode === DICT) { + dictid = 1; /* adler32(0, null, 0)*/ + /* dictid = adler32(dictid, dictionary, dictLength); */ + dictid = adler32_1(dictid, dictionary, dictLength, 0); + if (dictid !== state.check) { + return Z_DATA_ERROR$1; + } + } + /* copy dictionary to window using updatewindow(), which will amend the + existing dictionary if appropriate */ + ret = updatewindow(strm, dictionary, dictLength, dictLength); + if (ret) { + state.mode = MEM; + return Z_MEM_ERROR$1; + } + state.havedict = 1; + // Tracev((stderr, "inflate: dictionary set\n")); + return Z_OK$1; +}; + +var inflateReset_1 = inflateReset; +var inflateReset2_1 = inflateReset2; +var inflateResetKeep_1 = inflateResetKeep; +var inflateInit_1 = inflateInit; +var inflateInit2_1 = inflateInit2; +var inflate_2$1 = inflate$2; +var inflateEnd_1 = inflateEnd; +var inflateGetHeader_1 = inflateGetHeader; +var inflateSetDictionary_1 = inflateSetDictionary; +var inflateInfo = "pako inflate (from Nodeca project)"; + +/* Not implemented +module.exports.inflateCopy = inflateCopy; +module.exports.inflateGetDictionary = inflateGetDictionary; +module.exports.inflateMark = inflateMark; +module.exports.inflatePrime = inflatePrime; +module.exports.inflateSync = inflateSync; +module.exports.inflateSyncPoint = inflateSyncPoint; +module.exports.inflateUndermine = inflateUndermine; +*/ + +var inflate_1$2 = { + inflateReset: inflateReset_1, + inflateReset2: inflateReset2_1, + inflateResetKeep: inflateResetKeep_1, + inflateInit: inflateInit_1, + inflateInit2: inflateInit2_1, + inflate: inflate_2$1, + inflateEnd: inflateEnd_1, + inflateGetHeader: inflateGetHeader_1, + inflateSetDictionary: inflateSetDictionary_1, + inflateInfo: inflateInfo, +}; + +// (C) 1995-2013 Jean-loup Gailly and Mark Adler +// (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would be +// appreciated but is not required. +// 2. Altered source versions must be plainly marked as such, and must not be +// misrepresented as being the original software. +// 3. This notice may not be removed or altered from any source distribution. + +function GZheader() { + /* true if compressed data believed to be text */ + this.text = 0; + /* modification time */ + this.time = 0; + /* extra flags (not used when writing a gzip file) */ + this.xflags = 0; + /* operating system */ + this.os = 0; + /* pointer to extra field or Z_NULL if none */ + this.extra = null; + /* extra field length (valid if extra != Z_NULL) */ + this.extra_len = 0; // Actually, we don't need it in JS, + // but leave for few code modifications + + // + // Setup limits is not necessary because in js we should not preallocate memory + // for inflate use constant limit in 65536 bytes + // + + /* space at extra (only when reading header) */ + // this.extra_max = 0; + /* pointer to zero-terminated file name or Z_NULL */ + this.name = ""; + /* space at name (only when reading header) */ + // this.name_max = 0; + /* pointer to zero-terminated comment or Z_NULL */ + this.comment = ""; + /* space at comment (only when reading header) */ + // this.comm_max = 0; + /* true if there was or will be a header crc */ + this.hcrc = 0; + /* true when done reading gzip header (not used when writing a gzip file) */ + this.done = false; +} + +var gzheader = GZheader; + +const toString = Object.prototype.toString; + +/* Public constants ==========================================================*/ +/* ===========================================================================*/ + +const { + Z_NO_FLUSH, + Z_FINISH, + Z_OK, + Z_STREAM_END, + Z_NEED_DICT, + Z_STREAM_ERROR, + Z_DATA_ERROR, + Z_MEM_ERROR, +} = constants$2; + +/* ===========================================================================*/ + +/** + * class Inflate + * + * Generic JS-style wrapper for zlib calls. If you don't need + * streaming behaviour - use more simple functions: [[inflate]] + * and [[inflateRaw]]. + */ + +/* internal + * inflate.chunks -> Array + * + * Chunks of output data, if [[Inflate#onData]] not overridden. + **/ + +/** + * Inflate.result -> Uint8Array|String + * + * Uncompressed result, generated by default [[Inflate#onData]] + * and [[Inflate#onEnd]] handlers. Filled after you push last chunk + * (call [[Inflate#push]] with `Z_FINISH` / `true` param). + */ + +/** + * Inflate.err -> Number + * + * Error code after inflate finished. 0 (Z_OK) on success. + * Should be checked if broken data possible. + */ + +/** + * Inflate.msg -> String + * + * Error message, if [[Inflate.err]] != 0 + */ + +/** + * new Inflate(options) + * - options (Object): zlib inflate options. + * + * Creates new inflator instance with specified params. Throws exception + * on bad params. Supported options: + * + * - `windowBits` + * - `dictionary` + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information on these. + * + * Additional options, for internal needs: + * + * - `chunkSize` - size of generated data chunks (16K by default) + * - `raw` (Boolean) - do raw inflate + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * By default, when no options set, autodetect deflate/gzip data format via + * wrapper header. + * + * ##### Example: + * + * ```javascript + * const pako = require('pako') + * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9]) + * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]); + * + * const inflate = new pako.Inflate({ level: 3}); + * + * inflate.push(chunk1, false); + * inflate.push(chunk2, true); // true -> last chunk + * + * if (inflate.err) { throw new Error(inflate.err); } + * + * console.log(inflate.result); + * ``` + */ +function Inflate$1(options) { + this.options = common.assign({ + chunkSize: 1024 * 64, + windowBits: 15, + to: "", + }, options || {}); + + const opt = this.options; + + // Force window size for `raw` data, if not set directly, + // because we have no header for autodetect. + if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) { + opt.windowBits = -opt.windowBits; + if (opt.windowBits === 0) opt.windowBits = -15; + } + + // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate + if ( + (opt.windowBits >= 0) && (opt.windowBits < 16) && + !(options && options.windowBits) + ) { + opt.windowBits += 32; + } + + // Gzip header has no info about windows size, we can do autodetect only + // for deflate. So, if window size not set, force it to max when gzip possible + if ((opt.windowBits > 15) && (opt.windowBits < 48)) { + // bit 3 (16) -> gzipped data + // bit 4 (32) -> autodetect gzip/deflate + if ((opt.windowBits & 15) === 0) { + opt.windowBits |= 15; + } + } + + this.err = 0; // error code, if happens (0 = Z_OK) + this.msg = ""; // error message + this.ended = false; // used to avoid multiple onEnd() calls + this.chunks = []; // chunks of compressed data + + this.strm = new zstream(); + this.strm.avail_out = 0; + + let status = inflate_1$2.inflateInit2( + this.strm, + opt.windowBits, + ); + + if (status !== Z_OK) { + throw new Error(messages[status]); + } + + this.header = new gzheader(); + + inflate_1$2.inflateGetHeader(this.strm, this.header); + + // Setup dictionary + if (opt.dictionary) { + // Convert data if needed + if (typeof opt.dictionary === "string") { + opt.dictionary = strings.string2buf(opt.dictionary); + } else if (toString.call(opt.dictionary) === "[object ArrayBuffer]") { + opt.dictionary = new Uint8Array(opt.dictionary); + } + if (opt.raw) { //In raw mode we need to set the dictionary early + status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary); + if (status !== Z_OK) { + throw new Error(messages[status]); + } + } + } +} + +/** + * Inflate#push(data[, flush_mode]) -> Boolean + * - data (Uint8Array|ArrayBuffer): input data + * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE + * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH, + * `true` means Z_FINISH. + * + * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with + * new output chunks. Returns `true` on success. If end of stream detected, + * [[Inflate#onEnd]] will be called. + * + * `flush_mode` is not needed for normal operation, because end of stream + * detected automatically. You may try to use it for advanced things, but + * this functionality was not tested. + * + * On fail call [[Inflate#onEnd]] with error code and return false. + * + * ##### Example + * + * ```javascript + * push(chunk, false); // push one of data chunks + * ... + * push(chunk, true); // push last chunk + * ``` + */ +Inflate$1.prototype.push = function (data, flush_mode) { + const strm = this.strm; + const chunkSize = this.options.chunkSize; + const dictionary = this.options.dictionary; + let status, _flush_mode, last_avail_out; + + if (this.ended) return false; + + if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; + else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; + + // Convert data if needed + if (toString.call(data) === "[object ArrayBuffer]") { + strm.input = new Uint8Array(data); + } else { + strm.input = data; + } + + strm.next_in = 0; + strm.avail_in = strm.input.length; + + for (;;) { + if (strm.avail_out === 0) { + strm.output = new Uint8Array(chunkSize); + strm.next_out = 0; + strm.avail_out = chunkSize; + } + + status = inflate_1$2.inflate(strm, _flush_mode); + + if (status === Z_NEED_DICT && dictionary) { + status = inflate_1$2.inflateSetDictionary(strm, dictionary); + + if (status === Z_OK) { + status = inflate_1$2.inflate(strm, _flush_mode); + } else if (status === Z_DATA_ERROR) { + // Replace code with more verbose + status = Z_NEED_DICT; + } + } + + // Skip snyc markers if more data follows and not raw mode + while ( + strm.avail_in > 0 && + status === Z_STREAM_END && + strm.state.wrap > 0 && + data[strm.next_in] !== 0 + ) { + inflate_1$2.inflateReset(strm); + status = inflate_1$2.inflate(strm, _flush_mode); + } + + switch (status) { + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_NEED_DICT: + case Z_MEM_ERROR: + this.onEnd(status); + this.ended = true; + return false; + } + + // Remember real `avail_out` value, because we may patch out buffer content + // to align utf8 strings boundaries. + last_avail_out = strm.avail_out; + + if (strm.next_out) { + if (strm.avail_out === 0 || status === Z_STREAM_END) { + if (this.options.to === "string") { + let next_out_utf8 = strings.utf8border(strm.output, strm.next_out); + + let tail = strm.next_out - next_out_utf8; + let utf8str = strings.buf2string(strm.output, next_out_utf8); + + // move tail & realign counters + strm.next_out = tail; + strm.avail_out = chunkSize - tail; + if (tail) { + strm.output.set( + strm.output.subarray(next_out_utf8, next_out_utf8 + tail), + 0, + ); + } + + this.onData(utf8str); + } else { + this.onData( + strm.output.length === strm.next_out + ? strm.output + : strm.output.subarray(0, strm.next_out), + ); + } + } + } + + // Must repeat iteration if out buffer is full + if (status === Z_OK && last_avail_out === 0) continue; + + // Finalize if end of stream reached. + if (status === Z_STREAM_END) { + status = inflate_1$2.inflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return true; + } + + if (strm.avail_in === 0) break; + } + + return true; +}; + +/** + * Inflate#onData(chunk) -> Void + * - chunk (Uint8Array|String): output data. When string output requested, + * each chunk will be string. + * + * By default, stores data blocks in `chunks[]` property and glue + * those in `onEnd`. Override this handler, if you need another behaviour. + */ +Inflate$1.prototype.onData = function (chunk) { + this.chunks.push(chunk); +}; + +/** + * Inflate#onEnd(status) -> Void + * - status (Number): inflate status. 0 (Z_OK) on success, + * other if not. + * + * Called either after you tell inflate that the input stream is + * complete (Z_FINISH). By default - join collected chunks, + * free memory and fill `results` / `err` properties. + */ +Inflate$1.prototype.onEnd = function (status) { + // On success - join + if (status === Z_OK) { + if (this.options.to === "string") { + this.result = this.chunks.join(""); + } else { + this.result = common.flattenChunks(this.chunks); + } + } + this.chunks = []; + this.err = status; + this.msg = this.strm.msg; +}; + +/** + * inflate(data[, options]) -> Uint8Array|String + * - data (Uint8Array): input data to decompress. + * - options (Object): zlib inflate options. + * + * Decompress `data` with inflate/ungzip and `options`. Autodetect + * format via wrapper header by default. That's why we don't provide + * separate `ungzip` method. + * + * Supported options are: + * + * - windowBits + * + * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced) + * for more information. + * + * Sugar (options): + * + * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify + * negative windowBits implicitly. + * - `to` (String) - if equal to 'string', then result will be converted + * from utf8 to utf16 (javascript) string. When string output requested, + * chunk length can differ from `chunkSize`, depending on content. + * + * ##### Example: + * + * ```javascript + * const pako = require('pako'); + * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9])); + * let output; + * + * try { + * output = pako.inflate(input); + * } catch (err) { + * console.log(err); + * } + * ``` + */ +function inflate$1(input, options) { + const inflator = new Inflate$1(options); + + inflator.push(input); + + // That will never happens, if you don't cheat with options :) + if (inflator.err) throw inflator.msg || messages[inflator.err]; + + return inflator.result; +} + +/** + * inflateRaw(data[, options]) -> Uint8Array|String + * - data (Uint8Array): input data to decompress. + * - options (Object): zlib inflate options. + * + * The same as [[inflate]], but creates raw data, without wrapper + * (header and adler32 crc). + */ +function inflateRaw$1(input, options) { + options = options || {}; + options.raw = true; + return inflate$1(input, options); +} + +/** + * ungzip(data[, options]) -> Uint8Array|String + * - data (Uint8Array): input data to decompress. + * - options (Object): zlib inflate options. + * + * Just shortcut to [[inflate]], because it autodetects format + * by header.content. Done for convenience. + */ + +var Inflate_1$1 = Inflate$1; +var inflate_2 = inflate$1; +var inflateRaw_1$1 = inflateRaw$1; +var ungzip$1 = inflate$1; +var constants = constants$2; + +var inflate_1$1 = { + Inflate: Inflate_1$1, + inflate: inflate_2, + inflateRaw: inflateRaw_1$1, + ungzip: ungzip$1, + constants: constants, +}; + +const { Deflate, deflate, deflateRaw, gzip } = deflate_1$1; + +const { Inflate, inflate, inflateRaw, ungzip } = inflate_1$1; + +var Deflate_1 = Deflate; +var deflate_1 = deflate; +var deflateRaw_1 = deflateRaw; +var gzip_1 = gzip; +var Inflate_1 = Inflate; +var inflate_1 = inflate; +var inflateRaw_1 = inflateRaw; +var ungzip_1 = ungzip; +var constants_1 = constants$2; + +var pako = { + Deflate: Deflate_1, + deflate: deflate_1, + deflateRaw: deflateRaw_1, + gzip: gzip_1, + Inflate: Inflate_1, + inflate: inflate_1, + inflateRaw: inflateRaw_1, + ungzip: ungzip_1, + constants: constants_1, +}; + +// NOTE(@bartlomieju): Zstream is also exported here, even though it's not exported in regular dist file +export { + constants_1 as constants, + Deflate_1 as Deflate, + deflate_1 as deflate, + deflate_1$2 as zlib_deflate, + deflateRaw_1 as deflateRaw, + gzip_1 as gzip, + Inflate_1 as Inflate, + inflate_1 as inflate, + inflate_1$2 as zlib_inflate, + inflateRaw_1 as inflateRaw, + pako as default, + ungzip_1 as ungzip, + ZStream as Zstream, +}; diff --git a/ext/node/polyfills/_process/exiting.ts b/ext/node/polyfills/_process/exiting.ts new file mode 100644 index 0000000000..8cc37eef83 --- /dev/null +++ b/ext/node/polyfills/_process/exiting.ts @@ -0,0 +1,4 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore prefer-const +export let _exiting = false; diff --git a/ext/node/polyfills/_process/process.ts b/ext/node/polyfills/_process/process.ts new file mode 100644 index 0000000000..7ba44e431a --- /dev/null +++ b/ext/node/polyfills/_process/process.ts @@ -0,0 +1,129 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +// The following are all the process APIs that don't depend on the stream module +// They have to be split this way to prevent a circular dependency + +import { build } from "internal:runtime/js/01_build.js"; +import { nextTick as _nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +import { _exiting } from "internal:deno_node/polyfills/_process/exiting.ts"; + +/** Returns the operating system CPU architecture for which the Deno binary was compiled */ +export function arch(): string { + if (build.arch == "x86_64") { + return "x64"; + } else if (build.arch == "aarch64") { + return "arm64"; + } else { + throw Error("unreachable"); + } +} + +/** https://nodejs.org/api/process.html#process_process_chdir_directory */ +export const chdir = Deno.chdir; + +/** https://nodejs.org/api/process.html#process_process_cwd */ +export const cwd = Deno.cwd; + +/** https://nodejs.org/api/process.html#process_process_nexttick_callback_args */ +export const nextTick = _nextTick; + +/** Wrapper of Deno.env.get, which doesn't throw type error when + * the env name has "=" or "\0" in it. */ +function denoEnvGet(name: string) { + const perm = + Deno.permissions.querySync?.({ name: "env", variable: name }).state ?? + "granted"; // for Deno Deploy + // Returns undefined if the env permission is unavailable + if (perm !== "granted") { + return undefined; + } + try { + return Deno.env.get(name); + } catch (e) { + if (e instanceof TypeError) { + return undefined; + } + throw e; + } +} + +const OBJECT_PROTO_PROP_NAMES = Object.getOwnPropertyNames(Object.prototype); +/** + * https://nodejs.org/api/process.html#process_process_env + * Requires env permissions + */ +export const env: InstanceType & Record = + new Proxy(Object(), { + get: (target, prop) => { + if (typeof prop === "symbol") { + return target[prop]; + } + + const envValue = denoEnvGet(prop); + + if (envValue) { + return envValue; + } + + if (OBJECT_PROTO_PROP_NAMES.includes(prop)) { + return target[prop]; + } + + return envValue; + }, + ownKeys: () => Reflect.ownKeys(Deno.env.toObject()), + getOwnPropertyDescriptor: (_target, name) => { + const value = denoEnvGet(String(name)); + if (value) { + return { + enumerable: true, + configurable: true, + value, + }; + } + }, + set(_target, prop, value) { + Deno.env.set(String(prop), String(value)); + return true; // success + }, + has: (_target, prop) => typeof denoEnvGet(String(prop)) === "string", + }); + +/** + * https://nodejs.org/api/process.html#process_process_version + * + * This value is hard coded to latest stable release of Node, as + * some packages are checking it for compatibility. Previously + * it pointed to Deno version, but that led to incompability + * with some packages. + */ +export const version = "v18.12.1"; + +/** + * https://nodejs.org/api/process.html#process_process_versions + * + * This value is hard coded to latest stable release of Node, as + * some packages are checking it for compatibility. Previously + * it contained only output of `Deno.version`, but that led to incompability + * with some packages. Value of `v8` field is still taken from `Deno.version`. + */ +export const versions = { + node: "18.12.1", + uv: "1.43.0", + zlib: "1.2.11", + brotli: "1.0.9", + ares: "1.18.1", + modules: "108", + nghttp2: "1.47.0", + napi: "8", + llhttp: "6.0.10", + openssl: "3.0.7+quic", + cldr: "41.0", + icu: "71.1", + tz: "2022b", + unicode: "14.0", + ngtcp2: "0.8.1", + nghttp3: "0.7.0", + ...Deno.version, +}; diff --git a/ext/node/polyfills/_process/stdio.mjs b/ext/node/polyfills/_process/stdio.mjs new file mode 100644 index 0000000000..4e0173dfa7 --- /dev/null +++ b/ext/node/polyfills/_process/stdio.mjs @@ -0,0 +1,7 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +// Lazily initializes the actual stdio objects. +// This trick is necessary for avoiding circular dependencies between +// stream and process modules. +export const stdio = {}; diff --git a/ext/node/polyfills/_process/streams.mjs b/ext/node/polyfills/_process/streams.mjs new file mode 100644 index 0000000000..30811e6735 --- /dev/null +++ b/ext/node/polyfills/_process/streams.mjs @@ -0,0 +1,246 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + clearLine, + clearScreenDown, + cursorTo, + moveCursor, +} from "internal:deno_node/polyfills/internal/readline/callbacks.mjs"; +import { Duplex, Readable, Writable } from "internal:deno_node/polyfills/stream.ts"; +import { stdio } from "internal:deno_node/polyfills/_process/stdio.mjs"; +import { fs as fsConstants } from "internal:deno_node/polyfills/internal_binding/constants.ts"; + +// https://github.com/nodejs/node/blob/00738314828074243c9a52a228ab4c68b04259ef/lib/internal/bootstrap/switches/is_main_thread.js#L41 +function createWritableStdioStream(writer, name) { + const stream = new Writable({ + write(buf, enc, cb) { + if (!writer) { + this.destroy( + new Error(`Deno.${name} is not available in this environment`), + ); + return; + } + writer.writeSync(buf instanceof Uint8Array ? buf : Buffer.from(buf, enc)); + cb(); + }, + destroy(err, cb) { + cb(err); + this._undestroy(); + if (!this._writableState.emitClose) { + nextTick(() => this.emit("close")); + } + }, + }); + stream.fd = writer?.rid ?? -1; + stream.destroySoon = stream.destroy; + stream._isStdio = true; + stream.once("close", () => writer?.close()); + Object.defineProperties(stream, { + columns: { + enumerable: true, + configurable: true, + get: () => + Deno.isatty?.(writer?.rid) ? Deno.consoleSize?.().columns : undefined, + }, + rows: { + enumerable: true, + configurable: true, + get: () => + Deno.isatty?.(writer?.rid) ? Deno.consoleSize?.().rows : undefined, + }, + isTTY: { + enumerable: true, + configurable: true, + get: () => Deno.isatty?.(writer?.rid), + }, + getWindowSize: { + enumerable: true, + configurable: true, + value: () => + Deno.isatty?.(writer?.rid) + ? Object.values(Deno.consoleSize?.()) + : undefined, + }, + }); + + if (Deno.isatty?.(writer?.rid)) { + // These belong on tty.WriteStream(), but the TTY streams currently have + // following problems: + // 1. Using them here introduces a circular dependency. + // 2. Creating a net.Socket() from a fd is not currently supported. + stream.cursorTo = function (x, y, callback) { + return cursorTo(this, x, y, callback); + }; + + stream.moveCursor = function (dx, dy, callback) { + return moveCursor(this, dx, dy, callback); + }; + + stream.clearLine = function (dir, callback) { + return clearLine(this, dir, callback); + }; + + stream.clearScreenDown = function (callback) { + return clearScreenDown(this, callback); + }; + } + + return stream; +} + +/** https://nodejs.org/api/process.html#process_process_stderr */ +export const stderr = stdio.stderr = createWritableStdioStream( + Deno.stderr, + "stderr", +); + +/** https://nodejs.org/api/process.html#process_process_stdout */ +export const stdout = stdio.stdout = createWritableStdioStream( + Deno.stdout, + "stdout", +); + +// TODO(PolarETech): This function should be replaced by +// `guessHandleType()` in "../internal_binding/util.ts". +// https://github.com/nodejs/node/blob/v18.12.1/src/node_util.cc#L257 +function _guessStdinType(fd) { + if (typeof fd !== "number" || fd < 0) return "UNKNOWN"; + if (Deno.isatty?.(fd)) return "TTY"; + + try { + const fileInfo = Deno.fstatSync?.(fd); + + // https://github.com/nodejs/node/blob/v18.12.1/deps/uv/src/unix/tty.c#L333 + if (Deno.build.os !== "windows") { + switch (fileInfo.mode & fsConstants.S_IFMT) { + case fsConstants.S_IFREG: + case fsConstants.S_IFCHR: + return "FILE"; + case fsConstants.S_IFIFO: + return "PIPE"; + case fsConstants.S_IFSOCK: + // TODO(PolarETech): Need a better way to identify "TCP". + // Currently, unable to exclude UDP. + return "TCP"; + default: + return "UNKNOWN"; + } + } + + // https://github.com/nodejs/node/blob/v18.12.1/deps/uv/src/win/handle.c#L31 + if (fileInfo.isFile) { + // TODO(PolarETech): Need a better way to identify a piped stdin on Windows. + // On Windows, `Deno.fstatSync(rid).isFile` returns true even for a piped stdin. + // Therefore, a piped stdin cannot be distinguished from a file by this property. + // The mtime, atime, and birthtime of the file are "2339-01-01T00:00:00.000Z", + // so use the property as a workaround. + if (fileInfo.birthtime.valueOf() === 11644473600000) return "PIPE"; + return "FILE"; + } + } catch (e) { + // TODO(PolarETech): Need a better way to identify a character file on Windows. + // "EISDIR" error occurs when stdin is "null" on Windows, + // so use the error as a workaround. + if (Deno.build.os === "windows" && e.code === "EISDIR") return "FILE"; + } + + return "UNKNOWN"; +} + +const _read = function (size) { + const p = Buffer.alloc(size || 16 * 1024); + Deno.stdin?.read(p).then((length) => { + this.push(length === null ? null : p.slice(0, length)); + }, (error) => { + this.destroy(error); + }); +}; + +/** https://nodejs.org/api/process.html#process_process_stdin */ +// https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L189 +export const stdin = stdio.stdin = (() => { + const fd = Deno.stdin?.rid; + let _stdin; + const stdinType = _guessStdinType(fd); + + switch (stdinType) { + case "FILE": { + // Since `fs.ReadStream` cannot be imported before process initialization, + // use `Readable` instead. + // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L200 + // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/fs/streams.js#L148 + _stdin = new Readable({ + highWaterMark: 64 * 1024, + autoDestroy: false, + read: _read, + }); + break; + } + case "TTY": + case "PIPE": + case "TCP": { + // TODO(PolarETech): + // For TTY, `new Duplex()` should be replaced `new tty.ReadStream()` if possible. + // There are two problems that need to be resolved. + // 1. Using them here introduces a circular dependency. + // 2. Creating a tty.ReadStream() is not currently supported. + // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L194 + // https://github.com/nodejs/node/blob/v18.12.1/lib/tty.js#L47 + + // For PIPE and TCP, `new Duplex()` should be replaced `new net.Socket()` if possible. + // There are two problems that need to be resolved. + // 1. Using them here introduces a circular dependency. + // 2. Creating a net.Socket() from a fd is not currently supported. + // https://github.com/nodejs/node/blob/v18.12.1/lib/internal/bootstrap/switches/is_main_thread.js#L206 + // https://github.com/nodejs/node/blob/v18.12.1/lib/net.js#L329 + _stdin = new Duplex({ + readable: stdinType === "TTY" ? undefined : true, + writable: stdinType === "TTY" ? undefined : false, + readableHighWaterMark: stdinType === "TTY" ? 0 : undefined, + allowHalfOpen: false, + emitClose: false, + autoDestroy: true, + decodeStrings: false, + read: _read, + }); + + if (stdinType !== "TTY") { + // Make sure the stdin can't be `.end()`-ed + _stdin._writableState.ended = true; + } + break; + } + default: { + // Provide a dummy contentless input for e.g. non-console + // Windows applications. + _stdin = new Readable({ read() {} }); + _stdin.push(null); + } + } + + return _stdin; +})(); +stdin.on("close", () => Deno.stdin?.close()); +stdin.fd = Deno.stdin?.rid ?? -1; +Object.defineProperty(stdin, "isTTY", { + enumerable: true, + configurable: true, + get() { + return Deno.isatty?.(Deno.stdin.rid); + }, +}); +stdin._isRawMode = false; +stdin.setRawMode = (enable) => { + Deno.stdin?.setRaw?.(enable); + stdin._isRawMode = enable; + return stdin; +}; +Object.defineProperty(stdin, "isRaw", { + enumerable: true, + configurable: true, + get() { + return stdin._isRawMode; + }, +}); diff --git a/ext/node/polyfills/_readline.d.ts b/ext/node/polyfills/_readline.d.ts new file mode 100644 index 0000000000..e3caea92bf --- /dev/null +++ b/ext/node/polyfills/_readline.d.ts @@ -0,0 +1,655 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file no-explicit-any + +// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61f5b4d3d143108569ec3f88adc0eb34b961c4/types/node/readline.d.ts + +import { + Abortable, + EventEmitter, +} from "internal:deno_node/polyfills/_events.d.ts"; +import * as promises from "internal:deno_node/polyfills/readline/promises.ts"; +import { + ReadableStream, + WritableStream, +} from "internal:deno_node/polyfills/_global.d.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import type { + AsyncCompleter, + Completer, + CompleterResult, + ReadLineOptions, +} from "internal:deno_node/polyfills/_readline_shared_types.d.ts"; + +/** + * The `readline` module provides an interface for reading data from a `Readable` stream (such as `process.stdin`) one line at a time. + * + * To use the promise-based APIs: + * + * Once this code is invoked, the Node.js application will not terminate until the`readline.Interface` is closed because the interface waits for data to be + * received on the `input` stream. + * @see [source](https://github.com/nodejs/node/blob/v18.0.0/lib/readline.js) + */ +export { promises }; +export interface Key { + sequence?: string | undefined; + name?: string | undefined; + ctrl?: boolean | undefined; + meta?: boolean | undefined; + shift?: boolean | undefined; +} +/** + * Instances of the `readline.Interface` class are constructed using the`readline.createInterface()` method. Every instance is associated with a + * single `input` `Readable` stream and a single `output` `Writable` stream. + * The `output` stream is used to print prompts for user input that arrives on, + * and is read from, the `input` stream. + * @since v0.1.104 + */ +export class Interface extends EventEmitter { + readonly terminal: boolean; + /** + * The current input data being processed by node. + * + * This can be used when collecting input from a TTY stream to retrieve the + * current value that has been processed thus far, prior to the `line` event + * being emitted. Once the `line` event has been emitted, this property will + * be an empty string. + * + * Be aware that modifying the value during the instance runtime may have + * unintended consequences if `rl.cursor` is not also controlled. + * + * **If not using a TTY stream for input, use the `'line'` event.** + * + * One possible use case would be as follows: + * + * ```js + * const values = ['lorem ipsum', 'dolor sit amet']; + * const rl = readline.createInterface(process.stdin); + * const showResults = debounce(() => { + * console.log( + * '\n', + * values.filter((val) => val.startsWith(rl.line)).join(' ') + * ); + * }, 300); + * process.stdin.on('keypress', (c, k) => { + * showResults(); + * }); + * ``` + * @since v0.1.98 + */ + readonly line: string; + /** + * The cursor position relative to `rl.line`. + * + * This will track where the current cursor lands in the input string, when + * reading input from a TTY stream. The position of cursor determines the + * portion of the input string that will be modified as input is processed, + * as well as the column where the terminal caret will be rendered. + * @since v0.1.98 + */ + readonly cursor: number; + /** + * NOTE: According to the documentation: + * + * > Instances of the `readline.Interface` class are constructed using the + * > `readline.createInterface()` method. + * + * @see https://nodejs.org/dist/latest-v10.x/docs/api/readline.html#readline_class_interface + */ + protected constructor( + input: ReadableStream, + output?: WritableStream, + completer?: Completer | AsyncCompleter, + terminal?: boolean, + ); + /** + * NOTE: According to the documentation: + * + * > Instances of the `readline.Interface` class are constructed using the + * > `readline.createInterface()` method. + * + * @see https://nodejs.org/dist/latest-v10.x/docs/api/readline.html#readline_class_interface + */ + protected constructor(options: ReadLineOptions); + /** + * The `rl.getPrompt()` method returns the current prompt used by `rl.prompt()`. + * @since v15.3.0 + * @return the current prompt string + */ + getPrompt(): string; + /** + * The `rl.setPrompt()` method sets the prompt that will be written to `output`whenever `rl.prompt()` is called. + * @since v0.1.98 + */ + setPrompt(prompt: string): void; + /** + * The `rl.prompt()` method writes the `readline.Interface` instances configured`prompt` to a new line in `output` in order to provide a user with a new + * location at which to provide input. + * + * When called, `rl.prompt()` will resume the `input` stream if it has been + * paused. + * + * If the `readline.Interface` was created with `output` set to `null` or`undefined` the prompt is not written. + * @since v0.1.98 + * @param preserveCursor If `true`, prevents the cursor placement from being reset to `0`. + */ + prompt(preserveCursor?: boolean): void; + /** + * The `rl.question()` method displays the `query` by writing it to the `output`, + * waits for user input to be provided on `input`, then invokes the `callback`function passing the provided input as the first argument. + * + * When called, `rl.question()` will resume the `input` stream if it has been + * paused. + * + * If the `readline.Interface` was created with `output` set to `null` or`undefined` the `query` is not written. + * + * The `callback` function passed to `rl.question()` does not follow the typical + * pattern of accepting an `Error` object or `null` as the first argument. + * The `callback` is called with the provided answer as the only argument. + * + * Example usage: + * + * ```js + * rl.question('What is your favorite food? ', (answer) => { + * console.log(`Oh, so your favorite food is ${answer}`); + * }); + * ``` + * + * Using an `AbortController` to cancel a question. + * + * ```js + * const ac = new AbortController(); + * const signal = ac.signal; + * + * rl.question('What is your favorite food? ', { signal }, (answer) => { + * console.log(`Oh, so your favorite food is ${answer}`); + * }); + * + * signal.addEventListener('abort', () => { + * console.log('The food question timed out'); + * }, { once: true }); + * + * setTimeout(() => ac.abort(), 10000); + * ``` + * + * If this method is invoked as it's util.promisify()ed version, it returns a + * Promise that fulfills with the answer. If the question is canceled using + * an `AbortController` it will reject with an `AbortError`. + * + * ```js + * const util = require('util'); + * const question = util.promisify(rl.question).bind(rl); + * + * async function questionExample() { + * try { + * const answer = await question('What is you favorite food? '); + * console.log(`Oh, so your favorite food is ${answer}`); + * } catch (err) { + * console.error('Question rejected', err); + * } + * } + * questionExample(); + * ``` + * @since v0.3.3 + * @param query A statement or query to write to `output`, prepended to the prompt. + * @param callback A callback function that is invoked with the user's input in response to the `query`. + */ + question(query: string, callback: (answer: string) => void): void; + question( + query: string, + options: Abortable, + callback: (answer: string) => void, + ): void; + /** + * The `rl.pause()` method pauses the `input` stream, allowing it to be resumed + * later if necessary. + * + * Calling `rl.pause()` does not immediately pause other events (including`'line'`) from being emitted by the `readline.Interface` instance. + * @since v0.3.4 + */ + pause(): this; + /** + * The `rl.resume()` method resumes the `input` stream if it has been paused. + * @since v0.3.4 + */ + resume(): this; + /** + * The `rl.close()` method closes the `readline.Interface` instance and + * relinquishes control over the `input` and `output` streams. When called, + * the `'close'` event will be emitted. + * + * Calling `rl.close()` does not immediately stop other events (including `'line'`) + * from being emitted by the `readline.Interface` instance. + * @since v0.1.98 + */ + close(): void; + /** + * The `rl.write()` method will write either `data` or a key sequence identified + * by `key` to the `output`. The `key` argument is supported only if `output` is + * a `TTY` text terminal. See `TTY keybindings` for a list of key + * combinations. + * + * If `key` is specified, `data` is ignored. + * + * When called, `rl.write()` will resume the `input` stream if it has been + * paused. + * + * If the `readline.Interface` was created with `output` set to `null` or`undefined` the `data` and `key` are not written. + * + * ```js + * rl.write('Delete this!'); + * // Simulate Ctrl+U to delete the line written previously + * rl.write(null, { ctrl: true, name: 'u' }); + * ``` + * + * The `rl.write()` method will write the data to the `readline` `Interface`'s`input`_as if it were provided by the user_. + * @since v0.1.98 + */ + write(data: string | Buffer, key?: Key): void; + write(data: undefined | null | string | Buffer, key: Key): void; + /** + * Returns the real position of the cursor in relation to the input + * prompt + string. Long input (wrapping) strings, as well as multiple + * line prompts are included in the calculations. + * @since v13.5.0, v12.16.0 + */ + getCursorPos(): CursorPos; + /** + * events.EventEmitter + * 1. close + * 2. line + * 3. pause + * 4. resume + * 5. SIGCONT + * 6. SIGINT + * 7. SIGTSTP + * 8. history + */ + addListener(event: string, listener: (...args: any[]) => void): this; + addListener(event: "close", listener: () => void): this; + addListener(event: "line", listener: (input: string) => void): this; + addListener(event: "pause", listener: () => void): this; + addListener(event: "resume", listener: () => void): this; + addListener(event: "SIGCONT", listener: () => void): this; + addListener(event: "SIGINT", listener: () => void): this; + addListener(event: "SIGTSTP", listener: () => void): this; + addListener(event: "history", listener: (history: string[]) => void): this; + emit(event: string | symbol, ...args: any[]): boolean; + emit(event: "close"): boolean; + emit(event: "line", input: string): boolean; + emit(event: "pause"): boolean; + emit(event: "resume"): boolean; + emit(event: "SIGCONT"): boolean; + emit(event: "SIGINT"): boolean; + emit(event: "SIGTSTP"): boolean; + emit(event: "history", history: string[]): boolean; + on(event: string, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "line", listener: (input: string) => void): this; + on(event: "pause", listener: () => void): this; + on(event: "resume", listener: () => void): this; + on(event: "SIGCONT", listener: () => void): this; + on(event: "SIGINT", listener: () => void): this; + on(event: "SIGTSTP", listener: () => void): this; + on(event: "history", listener: (history: string[]) => void): this; + once(event: string, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "line", listener: (input: string) => void): this; + once(event: "pause", listener: () => void): this; + once(event: "resume", listener: () => void): this; + once(event: "SIGCONT", listener: () => void): this; + once(event: "SIGINT", listener: () => void): this; + once(event: "SIGTSTP", listener: () => void): this; + once(event: "history", listener: (history: string[]) => void): this; + prependListener(event: string, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "line", listener: (input: string) => void): this; + prependListener(event: "pause", listener: () => void): this; + prependListener(event: "resume", listener: () => void): this; + prependListener(event: "SIGCONT", listener: () => void): this; + prependListener(event: "SIGINT", listener: () => void): this; + prependListener(event: "SIGTSTP", listener: () => void): this; + prependListener( + event: "history", + listener: (history: string[]) => void, + ): this; + prependOnceListener(event: string, listener: (...args: any[]) => void): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "line", listener: (input: string) => void): this; + prependOnceListener(event: "pause", listener: () => void): this; + prependOnceListener(event: "resume", listener: () => void): this; + prependOnceListener(event: "SIGCONT", listener: () => void): this; + prependOnceListener(event: "SIGINT", listener: () => void): this; + prependOnceListener(event: "SIGTSTP", listener: () => void): this; + prependOnceListener( + event: "history", + listener: (history: string[]) => void, + ): this; + [Symbol.asyncIterator](): AsyncIterableIterator; +} +export type ReadLine = Interface; // type forwarded for backwards compatibility +export { AsyncCompleter, Completer, CompleterResult, ReadLineOptions }; +/** + * The `readline.createInterface()` method creates a new `readline.Interface`instance. + * + * ```js + * const readline = require('readline'); + * const rl = readline.createInterface({ + * input: process.stdin, + * output: process.stdout + * }); + * ``` + * + * Once the `readline.Interface` instance is created, the most common case is to + * listen for the `'line'` event: + * + * ```js + * rl.on('line', (line) => { + * console.log(`Received: ${line}`); + * }); + * ``` + * + * If `terminal` is `true` for this instance then the `output` stream will get + * the best compatibility if it defines an `output.columns` property and emits + * a `'resize'` event on the `output` if or when the columns ever change + * (`process.stdout` does this automatically when it is a TTY). + * + * When creating a `readline.Interface` using `stdin` as input, the program + * will not terminate until it receives `EOF` (Ctrl+D on + * Linux/macOS, Ctrl+Z followed by Return on + * Windows). + * If you want your application to exit without waiting for user input, you can `unref()` the standard input stream: + * + * ```js + * process.stdin.unref(); + * ``` + * @since v0.1.98 + */ +export function createInterface( + input: ReadableStream, + output?: WritableStream, + completer?: Completer | AsyncCompleter, + terminal?: boolean, +): Interface; +export function createInterface(options: ReadLineOptions): Interface; +/** + * The `readline.emitKeypressEvents()` method causes the given `Readable` stream to begin emitting `'keypress'` events corresponding to received input. + * + * Optionally, `interface` specifies a `readline.Interface` instance for which + * autocompletion is disabled when copy-pasted input is detected. + * + * If the `stream` is a `TTY`, then it must be in raw mode. + * + * This is automatically called by any readline instance on its `input` if the`input` is a terminal. Closing the `readline` instance does not stop + * the `input` from emitting `'keypress'` events. + * + * ```js + * readline.emitKeypressEvents(process.stdin); + * if (process.stdin.isTTY) + * process.stdin.setRawMode(true); + * ``` + * + * ## Example: Tiny CLI + * + * The following example illustrates the use of `readline.Interface` class to + * implement a small command-line interface: + * + * ```js + * const readline = require('readline'); + * const rl = readline.createInterface({ + * input: process.stdin, + * output: process.stdout, + * prompt: 'OHAI> ' + * }); + * + * rl.prompt(); + * + * rl.on('line', (line) => { + * switch (line.trim()) { + * case 'hello': + * console.log('world!'); + * break; + * default: + * console.log(`Say what? I might have heard '${line.trim()}'`); + * break; + * } + * rl.prompt(); + * }).on('close', () => { + * console.log('Have a great day!'); + * process.exit(0); + * }); + * ``` + * + * ## Example: Read file stream line-by-Line + * + * A common use case for `readline` is to consume an input file one line at a + * time. The easiest way to do so is leveraging the `fs.ReadStream` API as + * well as a `for await...of` loop: + * + * ```js + * const fs = require('fs'); + * const readline = require('readline'); + * + * async function processLineByLine() { + * const fileStream = fs.createReadStream('input.txt'); + * + * const rl = readline.createInterface({ + * input: fileStream, + * crlfDelay: Infinity + * }); + * // Note: we use the crlfDelay option to recognize all instances of CR LF + * // ('\r\n') in input.txt as a single line break. + * + * for await (const line of rl) { + * // Each line in input.txt will be successively available here as `line`. + * console.log(`Line from file: ${line}`); + * } + * } + * + * processLineByLine(); + * ``` + * + * Alternatively, one could use the `'line'` event: + * + * ```js + * const fs = require('fs'); + * const readline = require('readline'); + * + * const rl = readline.createInterface({ + * input: fs.createReadStream('sample.txt'), + * crlfDelay: Infinity + * }); + * + * rl.on('line', (line) => { + * console.log(`Line from file: ${line}`); + * }); + * ``` + * + * Currently, `for await...of` loop can be a bit slower. If `async` / `await`flow and speed are both essential, a mixed approach can be applied: + * + * ```js + * const { once } = require('events'); + * const { createReadStream } = require('fs'); + * const { createInterface } = require('readline'); + * + * (async function processLineByLine() { + * try { + * const rl = createInterface({ + * input: createReadStream('big-file.txt'), + * crlfDelay: Infinity + * }); + * + * rl.on('line', (line) => { + * // Process the line. + * }); + * + * await once(rl, 'close'); + * + * console.log('File processed.'); + * } catch (err) { + * console.error(err); + * } + * })(); + * ``` + * @since v0.7.7 + */ +export function emitKeypressEvents( + stream: ReadableStream, + readlineInterface?: Interface, +): void; +export type Direction = -1 | 0 | 1; +export interface CursorPos { + rows: number; + cols: number; +} +/** + * The `readline.clearLine()` method clears current line of given `TTY` stream + * in a specified direction identified by `dir`. + * @since v0.7.7 + * @param callback Invoked once the operation completes. + * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ +export function clearLine( + stream: WritableStream, + dir: Direction, + callback?: () => void, +): boolean; +/** + * The `readline.clearScreenDown()` method clears the given `TTY` stream from + * the current position of the cursor down. + * @since v0.7.7 + * @param callback Invoked once the operation completes. + * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ +export function clearScreenDown( + stream: WritableStream, + callback?: () => void, +): boolean; +/** + * The `readline.cursorTo()` method moves cursor to the specified position in a + * given `TTY` `stream`. + * @since v0.7.7 + * @param callback Invoked once the operation completes. + * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ +export function cursorTo( + stream: WritableStream, + x: number, + y?: number, + callback?: () => void, +): boolean; +/** + * The `readline.moveCursor()` method moves the cursor _relative_ to its current + * position in a given `TTY` `stream`. + * + * ## Example: Tiny CLI + * + * The following example illustrates the use of `readline.Interface` class to + * implement a small command-line interface: + * + * ```js + * const readline = require('readline'); + * const rl = readline.createInterface({ + * input: process.stdin, + * output: process.stdout, + * prompt: 'OHAI> ' + * }); + * + * rl.prompt(); + * + * rl.on('line', (line) => { + * switch (line.trim()) { + * case 'hello': + * console.log('world!'); + * break; + * default: + * console.log(`Say what? I might have heard '${line.trim()}'`); + * break; + * } + * rl.prompt(); + * }).on('close', () => { + * console.log('Have a great day!'); + * process.exit(0); + * }); + * ``` + * + * ## Example: Read file stream line-by-Line + * + * A common use case for `readline` is to consume an input file one line at a + * time. The easiest way to do so is leveraging the `fs.ReadStream` API as + * well as a `for await...of` loop: + * + * ```js + * const fs = require('fs'); + * const readline = require('readline'); + * + * async function processLineByLine() { + * const fileStream = fs.createReadStream('input.txt'); + * + * const rl = readline.createInterface({ + * input: fileStream, + * crlfDelay: Infinity + * }); + * // Note: we use the crlfDelay option to recognize all instances of CR LF + * // ('\r\n') in input.txt as a single line break. + * + * for await (const line of rl) { + * // Each line in input.txt will be successively available here as `line`. + * console.log(`Line from file: ${line}`); + * } + * } + * + * processLineByLine(); + * ``` + * + * Alternatively, one could use the `'line'` event: + * + * ```js + * const fs = require('fs'); + * const readline = require('readline'); + * + * const rl = readline.createInterface({ + * input: fs.createReadStream('sample.txt'), + * crlfDelay: Infinity + * }); + * + * rl.on('line', (line) => { + * console.log(`Line from file: ${line}`); + * }); + * ``` + * + * Currently, `for await...of` loop can be a bit slower. If `async` / `await`flow and speed are both essential, a mixed approach can be applied: + * + * ```js + * const { once } = require('events'); + * const { createReadStream } = require('fs'); + * const { createInterface } = require('readline'); + * + * (async function processLineByLine() { + * try { + * const rl = createInterface({ + * input: createReadStream('big-file.txt'), + * crlfDelay: Infinity + * }); + * + * rl.on('line', (line) => { + * // Process the line. + * }); + * + * await once(rl, 'close'); + * + * console.log('File processed.'); + * } catch (err) { + * console.error(err); + * } + * })(); + * ``` + * @since v0.7.7 + * @param callback Invoked once the operation completes. + * @return `false` if `stream` wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ +export function moveCursor( + stream: WritableStream, + dx: number, + dy: number, + callback?: () => void, +): boolean; diff --git a/ext/node/polyfills/_readline.mjs b/ext/node/polyfills/_readline.mjs new file mode 100644 index 0000000000..6e0968af0d --- /dev/null +++ b/ext/node/polyfills/_readline.mjs @@ -0,0 +1,486 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// deno-lint-ignore-file camelcase + +import { + clearLine, + clearScreenDown, + cursorTo, + moveCursor, +} from "internal:deno_node/polyfills/internal/readline/callbacks.mjs"; +import { emitKeypressEvents } from "internal:deno_node/polyfills/internal/readline/emitKeypressEvents.mjs"; +import promises from "internal:deno_node/polyfills/readline/promises.ts"; +import { validateAbortSignal } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; +import { AbortError } from "internal:deno_node/polyfills/internal/errors.ts"; +import { process } from "internal:deno_node/polyfills/process.ts"; + +import { + Interface as _Interface, + InterfaceConstructor, + kAddHistory, + kDecoder, + kDeleteLeft, + kDeleteLineLeft, + kDeleteLineRight, + kDeleteRight, + kDeleteWordLeft, + kDeleteWordRight, + kGetDisplayPos, + kHistoryNext, + kHistoryPrev, + kInsertString, + kLine, + kLine_buffer, + kMoveCursor, + kNormalWrite, + kOldPrompt, + kOnLine, + kPreviousKey, + kPrompt, + kQuestion, + kQuestionCallback, + kQuestionCancel, + kRefreshLine, + kSawKeyPress, + kSawReturnAt, + kSetRawMode, + kTabComplete, + kTabCompleter, + kTtyWrite, + kWordLeft, + kWordRight, + kWriteToOutput, +} from "internal:deno_node/polyfills/internal/readline/interface.mjs"; + +function Interface(input, output, completer, terminal) { + if (!(this instanceof Interface)) { + return new Interface(input, output, completer, terminal); + } + + if ( + input?.input && + typeof input.completer === "function" && input.completer.length !== 2 + ) { + const { completer } = input; + input.completer = (v, cb) => cb(null, completer(v)); + } else if (typeof completer === "function" && completer.length !== 2) { + const realCompleter = completer; + completer = (v, cb) => cb(null, realCompleter(v)); + } + + // NOTE(bartlomieju): in Node this is `FunctionPrototypeCall(...)`, + // but trying to do `Function.prototype.call()` somehow doesn't work here + // /shrug + InterfaceConstructor.bind( + this, + )( + input, + output, + completer, + terminal, + ); + if (process.env.TERM === "dumb") { + this._ttyWrite = _ttyWriteDumb.bind(this); + } +} + +Object.setPrototypeOf(Interface.prototype, _Interface.prototype); +Object.setPrototypeOf(Interface, _Interface); + +/** + * Displays `query` by writing it to the `output`. + * @param {string} query + * @param {{ signal?: AbortSignal; }} [options] + * @param {Function} cb + * @returns {void} + */ +Interface.prototype.question = function question(query, options, cb) { + cb = typeof options === "function" ? options : cb; + options = typeof options === "object" && options !== null ? options : {}; + + if (options.signal) { + validateAbortSignal(options.signal, "options.signal"); + if (options.signal.aborted) { + return; + } + + const onAbort = () => { + this[kQuestionCancel](); + }; + options.signal.addEventListener("abort", onAbort, { once: true }); + const cleanup = () => { + options.signal.removeEventListener(onAbort); + }; + cb = typeof cb === "function" + ? (answer) => { + cleanup(); + return cb(answer); + } + : cleanup; + } + + if (typeof cb === "function") { + this[kQuestion](query, cb); + } +}; +Interface.prototype.question[promisify.custom] = function question( + query, + options, +) { + options = typeof options === "object" && options !== null ? options : {}; + + if (options.signal && options.signal.aborted) { + return Promise.reject( + new AbortError(undefined, { cause: options.signal.reason }), + ); + } + + return new Promise((resolve, reject) => { + let cb = resolve; + + if (options.signal) { + const onAbort = () => { + reject(new AbortError(undefined, { cause: options.signal.reason })); + }; + options.signal.addEventListener("abort", onAbort, { once: true }); + cb = (answer) => { + options.signal.removeEventListener("abort", onAbort); + resolve(answer); + }; + } + + this.question(query, options, cb); + }); +}; + +/** + * Creates a new `readline.Interface` instance. + * @param {Readable | { + * input: Readable; + * output: Writable; + * completer?: Function; + * terminal?: boolean; + * history?: string[]; + * historySize?: number; + * removeHistoryDuplicates?: boolean; + * prompt?: string; + * crlfDelay?: number; + * escapeCodeTimeout?: number; + * tabSize?: number; + * signal?: AbortSignal; + * }} input + * @param {Writable} [output] + * @param {Function} [completer] + * @param {boolean} [terminal] + * @returns {Interface} + */ +function createInterface(input, output, completer, terminal) { + return new Interface(input, output, completer, terminal); +} + +Object.defineProperties(Interface.prototype, { + // Redirect internal prototype methods to the underscore notation for backward + // compatibility. + [kSetRawMode]: { + get() { + return this._setRawMode; + }, + }, + [kOnLine]: { + get() { + return this._onLine; + }, + }, + [kWriteToOutput]: { + get() { + return this._writeToOutput; + }, + }, + [kAddHistory]: { + get() { + return this._addHistory; + }, + }, + [kRefreshLine]: { + get() { + return this._refreshLine; + }, + }, + [kNormalWrite]: { + get() { + return this._normalWrite; + }, + }, + [kInsertString]: { + get() { + return this._insertString; + }, + }, + [kTabComplete]: { + get() { + return this._tabComplete; + }, + }, + [kWordLeft]: { + get() { + return this._wordLeft; + }, + }, + [kWordRight]: { + get() { + return this._wordRight; + }, + }, + [kDeleteLeft]: { + get() { + return this._deleteLeft; + }, + }, + [kDeleteRight]: { + get() { + return this._deleteRight; + }, + }, + [kDeleteWordLeft]: { + get() { + return this._deleteWordLeft; + }, + }, + [kDeleteWordRight]: { + get() { + return this._deleteWordRight; + }, + }, + [kDeleteLineLeft]: { + get() { + return this._deleteLineLeft; + }, + }, + [kDeleteLineRight]: { + get() { + return this._deleteLineRight; + }, + }, + [kLine]: { + get() { + return this._line; + }, + }, + [kHistoryNext]: { + get() { + return this._historyNext; + }, + }, + [kHistoryPrev]: { + get() { + return this._historyPrev; + }, + }, + [kGetDisplayPos]: { + get() { + return this._getDisplayPos; + }, + }, + [kMoveCursor]: { + get() { + return this._moveCursor; + }, + }, + [kTtyWrite]: { + get() { + return this._ttyWrite; + }, + }, + + // Defining proxies for the internal instance properties for backward + // compatibility. + _decoder: { + get() { + return this[kDecoder]; + }, + set(value) { + this[kDecoder] = value; + }, + }, + _line_buffer: { + get() { + return this[kLine_buffer]; + }, + set(value) { + this[kLine_buffer] = value; + }, + }, + _oldPrompt: { + get() { + return this[kOldPrompt]; + }, + set(value) { + this[kOldPrompt] = value; + }, + }, + _previousKey: { + get() { + return this[kPreviousKey]; + }, + set(value) { + this[kPreviousKey] = value; + }, + }, + _prompt: { + get() { + return this[kPrompt]; + }, + set(value) { + this[kPrompt] = value; + }, + }, + _questionCallback: { + get() { + return this[kQuestionCallback]; + }, + set(value) { + this[kQuestionCallback] = value; + }, + }, + _sawKeyPress: { + get() { + return this[kSawKeyPress]; + }, + set(value) { + this[kSawKeyPress] = value; + }, + }, + _sawReturnAt: { + get() { + return this[kSawReturnAt]; + }, + set(value) { + this[kSawReturnAt] = value; + }, + }, +}); + +// Make internal methods public for backward compatibility. +Interface.prototype._setRawMode = _Interface.prototype[kSetRawMode]; +Interface.prototype._onLine = _Interface.prototype[kOnLine]; +Interface.prototype._writeToOutput = _Interface.prototype[kWriteToOutput]; +Interface.prototype._addHistory = _Interface.prototype[kAddHistory]; +Interface.prototype._refreshLine = _Interface.prototype[kRefreshLine]; +Interface.prototype._normalWrite = _Interface.prototype[kNormalWrite]; +Interface.prototype._insertString = _Interface.prototype[kInsertString]; +Interface.prototype._tabComplete = function (lastKeypressWasTab) { + // Overriding parent method because `this.completer` in the legacy + // implementation takes a callback instead of being an async function. + this.pause(); + const string = this.line.slice(0, this.cursor); + this.completer(string, (err, value) => { + this.resume(); + + if (err) { + // TODO(bartlomieju): inspect is not ported yet + // this._writeToOutput(`Tab completion error: ${inspect(err)}`); + this._writeToOutput(`Tab completion error: ${err}`); + return; + } + + this[kTabCompleter](lastKeypressWasTab, value); + }); +}; +Interface.prototype._wordLeft = _Interface.prototype[kWordLeft]; +Interface.prototype._wordRight = _Interface.prototype[kWordRight]; +Interface.prototype._deleteLeft = _Interface.prototype[kDeleteLeft]; +Interface.prototype._deleteRight = _Interface.prototype[kDeleteRight]; +Interface.prototype._deleteWordLeft = _Interface.prototype[kDeleteWordLeft]; +Interface.prototype._deleteWordRight = _Interface.prototype[kDeleteWordRight]; +Interface.prototype._deleteLineLeft = _Interface.prototype[kDeleteLineLeft]; +Interface.prototype._deleteLineRight = _Interface.prototype[kDeleteLineRight]; +Interface.prototype._line = _Interface.prototype[kLine]; +Interface.prototype._historyNext = _Interface.prototype[kHistoryNext]; +Interface.prototype._historyPrev = _Interface.prototype[kHistoryPrev]; +Interface.prototype._getDisplayPos = _Interface.prototype[kGetDisplayPos]; +Interface.prototype._getCursorPos = _Interface.prototype.getCursorPos; +Interface.prototype._moveCursor = _Interface.prototype[kMoveCursor]; +Interface.prototype._ttyWrite = _Interface.prototype[kTtyWrite]; + +function _ttyWriteDumb(s, key) { + key = key || {}; + + if (key.name === "escape") return; + + if (this[kSawReturnAt] && key.name !== "enter") { + this[kSawReturnAt] = 0; + } + + if (key.ctrl) { + if (key.name === "c") { + if (this.listenerCount("SIGINT") > 0) { + this.emit("SIGINT"); + } else { + // This readline instance is finished + this.close(); + } + + return; + } else if (key.name === "d") { + this.close(); + return; + } + } + + switch (key.name) { + case "return": // Carriage return, i.e. \r + this[kSawReturnAt] = Date.now(); + this._line(); + break; + + case "enter": + // When key interval > crlfDelay + if ( + this[kSawReturnAt] === 0 || + Date.now() - this[kSawReturnAt] > this.crlfDelay + ) { + this._line(); + } + this[kSawReturnAt] = 0; + break; + + default: + if (typeof s === "string" && s) { + this.line += s; + this.cursor += s.length; + this._writeToOutput(s); + } + } +} + +export { + clearLine, + clearScreenDown, + createInterface, + cursorTo, + emitKeypressEvents, + Interface, + moveCursor, + promises, +}; diff --git a/ext/node/polyfills/_readline_shared_types.d.ts b/ext/node/polyfills/_readline_shared_types.d.ts new file mode 100644 index 0000000000..2744839164 --- /dev/null +++ b/ext/node/polyfills/_readline_shared_types.d.ts @@ -0,0 +1,42 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// Part of https://github.com/DefinitelyTyped/DefinitelyTyped/blob/cd61f5b4d3d143108569ec3f88adc0eb34b961c4/types/node/readline.d.ts + +// This .d.ts file is provided to avoid circular dependencies. + +import type { + ReadableStream, + WritableStream, +} from "internal:deno_node/polyfills/_global.d.ts"; + +export type Completer = (line: string) => CompleterResult; +export type AsyncCompleter = ( + line: string, + callback: (err?: null | Error, result?: CompleterResult) => void, +) => void; +export type CompleterResult = [string[], string]; +export interface ReadLineOptions { + input: ReadableStream; + output?: WritableStream | undefined; + completer?: Completer | AsyncCompleter | undefined; + terminal?: boolean | undefined; + /** + * Initial list of history lines. This option makes sense + * only if `terminal` is set to `true` by the user or by an internal `output` + * check, otherwise the history caching mechanism is not initialized at all. + * @default [] + */ + history?: string[] | undefined; + historySize?: number | undefined; + prompt?: string | undefined; + crlfDelay?: number | undefined; + /** + * If `true`, when a new input line added + * to the history list duplicates an older one, this removes the older line + * from the list. + * @default false + */ + removeHistoryDuplicates?: boolean | undefined; + escapeCodeTimeout?: number | undefined; + tabSize?: number | undefined; +} diff --git a/ext/node/polyfills/_stream.d.ts b/ext/node/polyfills/_stream.d.ts new file mode 100644 index 0000000000..33ebda23c2 --- /dev/null +++ b/ext/node/polyfills/_stream.d.ts @@ -0,0 +1,1491 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file no-explicit-any + +// Forked from https://github.com/DefinitelyTyped/DefinitelyTyped/blob/4f538975138678878fed5b2555c0672aa578ab7d/types/node/stream.d.ts + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + Abortable, + EventEmitter, +} from "internal:deno_node/polyfills/_events.d.ts"; +import { + Buffered, + BufferEncoding, + ErrnoException, + ReadableStream, + ReadWriteStream, + WritableStream, +} from "internal:deno_node/polyfills/_global.d.ts"; + +export class Stream extends EventEmitter { + pipe( + destination: T, + options?: { + end?: boolean | undefined; + }, + ): T; + constructor(opts?: ReadableOptions); +} + +interface StreamOptions extends Abortable { + emitClose?: boolean | undefined; + highWaterMark?: number | undefined; + objectMode?: boolean | undefined; + construct?(this: T, callback: (error?: Error | null) => void): void; + destroy?( + this: T, + error: Error | null, + callback: (error: Error | null) => void, + ): void; + autoDestroy?: boolean | undefined; +} +export interface ReadableOptions extends StreamOptions { + encoding?: BufferEncoding | undefined; + read?(this: Readable, size: number): void; +} +/** + * @since v0.9.4 + */ +export class Readable extends Stream implements ReadableStream { + /** + * A utility method for creating Readable Streams out of iterators. + */ + static from( + iterable: Iterable | AsyncIterable, + options?: ReadableOptions, + ): Readable; + + /** + * A utility method for creating `Readable` from a `ReadableStream`. + * @since v17.0.0 + * @experimental + */ + static fromWeb( + readableStream: globalThis.ReadableStream, + options?: Pick< + ReadableOptions, + "encoding" | "highWaterMark" | "objectMode" | "signal" + >, + ): Readable; + + /** + * Returns whether the stream has been read from or cancelled. + * @since v16.8.0 + */ + static isDisturbed(stream: Readable | ReadableStream): boolean; + /** + * Returns whether the stream was destroyed or errored before emitting `'end'`. + * @since v16.8.0 + * @experimental + */ + readonly readableAborted: boolean; + /** + * Is `true` if it is safe to call `readable.read()`, which means + * the stream has not been destroyed or emitted `'error'` or `'end'`. + * @since v11.4.0 + */ + readable: boolean; + /** + * Returns whether `'data'` has been emitted. + * @since v16.7.0 + * @experimental + */ + readonly readableDidRead: boolean; + /** + * Getter for the property `encoding` of a given `Readable` stream. The `encoding`property can be set using the `readable.setEncoding()` method. + * @since v12.7.0 + */ + readonly readableEncoding: BufferEncoding | null; + /** + * Becomes `true` when `'end'` event is emitted. + * @since v12.9.0 + */ + readonly readableEnded: boolean; + /** + * This property reflects the current state of a `Readable` stream as described + * in the `Three states` section. + * @since v9.4.0 + */ + readonly readableFlowing: boolean | null; + /** + * Returns the value of `highWaterMark` passed when creating this `Readable`. + * @since v9.3.0 + */ + readonly readableHighWaterMark: number; + /** + * This property contains the number of bytes (or objects) in the queue + * ready to be read. The value provides introspection data regarding + * the status of the `highWaterMark`. + * @since v9.4.0 + */ + readonly readableLength: number; + /** + * Getter for the property `objectMode` of a given `Readable` stream. + * @since v12.3.0 + */ + readonly readableObjectMode: boolean; + /** + * Is `true` after `readable.destroy()` has been called. + * @since v8.0.0 + */ + destroyed: boolean; + constructor(opts?: ReadableOptions); + _construct?(callback: (error?: Error | null) => void): void; + _read(size: number): void; + _undestroy(): void; + /** + * The `readable.read()` method pulls some data out of the internal buffer and + * returns it. If no data available to be read, `null` is returned. By default, + * the data will be returned as a `Buffer` object unless an encoding has been + * specified using the `readable.setEncoding()` method or the stream is operating + * in object mode. + * + * The optional `size` argument specifies a specific number of bytes to read. If`size` bytes are not available to be read, `null` will be returned _unless_the stream has ended, in which + * case all of the data remaining in the internal + * buffer will be returned. + * + * If the `size` argument is not specified, all of the data contained in the + * internal buffer will be returned. + * + * The `size` argument must be less than or equal to 1 GiB. + * + * The `readable.read()` method should only be called on `Readable` streams + * operating in paused mode. In flowing mode, `readable.read()` is called + * automatically until the internal buffer is fully drained. + * + * ```js + * const readable = getReadableStreamSomehow(); + * + * // 'readable' may be triggered multiple times as data is buffered in + * readable.on('readable', () => { + * let chunk; + * console.log('Stream is readable (new data received in buffer)'); + * // Use a loop to make sure we read all currently available data + * while (null !== (chunk = readable.read())) { + * console.log(`Read ${chunk.length} bytes of data...`); + * } + * }); + * + * // 'end' will be triggered once when there is no more data available + * readable.on('end', () => { + * console.log('Reached end of stream.'); + * }); + * ``` + * + * Each call to `readable.read()` returns a chunk of data, or `null`. The chunks + * are not concatenated. A `while` loop is necessary to consume all data + * currently in the buffer. When reading a large file `.read()` may return `null`, + * having consumed all buffered content so far, but there is still more data to + * come not yet buffered. In this case a new `'readable'` event will be emitted + * when there is more data in the buffer. Finally the `'end'` event will be + * emitted when there is no more data to come. + * + * Therefore to read a file's whole contents from a `readable`, it is necessary + * to collect chunks across multiple `'readable'` events: + * + * ```js + * const chunks = []; + * + * readable.on('readable', () => { + * let chunk; + * while (null !== (chunk = readable.read())) { + * chunks.push(chunk); + * } + * }); + * + * readable.on('end', () => { + * const content = chunks.join(''); + * }); + * ``` + * + * A `Readable` stream in object mode will always return a single item from + * a call to `readable.read(size)`, regardless of the value of the`size` argument. + * + * If the `readable.read()` method returns a chunk of data, a `'data'` event will + * also be emitted. + * + * Calling {@link read} after the `'end'` event has + * been emitted will return `null`. No runtime error will be raised. + * @since v0.9.4 + * @param size Optional argument to specify how much data to read. + */ + read(size?: number): any; + /** + * The `readable.setEncoding()` method sets the character encoding for + * data read from the `Readable` stream. + * + * By default, no encoding is assigned and stream data will be returned as`Buffer` objects. Setting an encoding causes the stream data + * to be returned as strings of the specified encoding rather than as `Buffer`objects. For instance, calling `readable.setEncoding('utf8')` will cause the + * output data to be interpreted as UTF-8 data, and passed as strings. Calling`readable.setEncoding('hex')` will cause the data to be encoded in hexadecimal + * string format. + * + * The `Readable` stream will properly handle multi-byte characters delivered + * through the stream that would otherwise become improperly decoded if simply + * pulled from the stream as `Buffer` objects. + * + * ```js + * const readable = getReadableStreamSomehow(); + * readable.setEncoding('utf8'); + * readable.on('data', (chunk) => { + * assert.equal(typeof chunk, 'string'); + * console.log('Got %d characters of string data:', chunk.length); + * }); + * ``` + * @since v0.9.4 + * @param encoding The encoding to use. + */ + setEncoding(encoding: BufferEncoding): this; + /** + * The `readable.pause()` method will cause a stream in flowing mode to stop + * emitting `'data'` events, switching out of flowing mode. Any data that + * becomes available will remain in the internal buffer. + * + * ```js + * const readable = getReadableStreamSomehow(); + * readable.on('data', (chunk) => { + * console.log(`Received ${chunk.length} bytes of data.`); + * readable.pause(); + * console.log('There will be no additional data for 1 second.'); + * setTimeout(() => { + * console.log('Now data will start flowing again.'); + * readable.resume(); + * }, 1000); + * }); + * ``` + * + * The `readable.pause()` method has no effect if there is a `'readable'`event listener. + * @since v0.9.4 + */ + pause(): this; + /** + * The `readable.resume()` method causes an explicitly paused `Readable` stream to + * resume emitting `'data'` events, switching the stream into flowing mode. + * + * The `readable.resume()` method can be used to fully consume the data from a + * stream without actually processing any of that data: + * + * ```js + * getReadableStreamSomehow() + * .resume() + * .on('end', () => { + * console.log('Reached the end, but did not read anything.'); + * }); + * ``` + * + * The `readable.resume()` method has no effect if there is a `'readable'`event listener. + * @since v0.9.4 + */ + resume(): this; + /** + * The `readable.isPaused()` method returns the current operating state of the`Readable`. This is used primarily by the mechanism that underlies the`readable.pipe()` method. In most + * typical cases, there will be no reason to + * use this method directly. + * + * ```js + * const readable = new stream.Readable(); + * + * readable.isPaused(); // === false + * readable.pause(); + * readable.isPaused(); // === true + * readable.resume(); + * readable.isPaused(); // === false + * ``` + * @since v0.11.14 + */ + isPaused(): boolean; + /** + * The `readable.unpipe()` method detaches a `Writable` stream previously attached + * using the {@link pipe} method. + * + * If the `destination` is not specified, then _all_ pipes are detached. + * + * If the `destination` is specified, but no pipe is set up for it, then + * the method does nothing. + * + * ```js + * const fs = require('fs'); + * const readable = getReadableStreamSomehow(); + * const writable = fs.createWriteStream('file.txt'); + * // All the data from readable goes into 'file.txt', + * // but only for the first second. + * readable.pipe(writable); + * setTimeout(() => { + * console.log('Stop writing to file.txt.'); + * readable.unpipe(writable); + * console.log('Manually close the file stream.'); + * writable.end(); + * }, 1000); + * ``` + * @since v0.9.4 + * @param destination Optional specific stream to unpipe + */ + unpipe(destination?: WritableStream): this; + /** + * Passing `chunk` as `null` signals the end of the stream (EOF) and behaves the + * same as `readable.push(null)`, after which no more data can be written. The EOF + * signal is put at the end of the buffer and any buffered data will still be + * flushed. + * + * The `readable.unshift()` method pushes a chunk of data back into the internal + * buffer. This is useful in certain situations where a stream is being consumed by + * code that needs to "un-consume" some amount of data that it has optimistically + * pulled out of the source, so that the data can be passed on to some other party. + * + * The `stream.unshift(chunk)` method cannot be called after the `'end'` event + * has been emitted or a runtime error will be thrown. + * + * Developers using `stream.unshift()` often should consider switching to + * use of a `Transform` stream instead. See the `API for stream implementers` section for more information. + * + * ```js + * // Pull off a header delimited by \n\n. + * // Use unshift() if we get too much. + * // Call the callback with (error, header, stream). + * const { StringDecoder } = require('string_decoder'); + * function parseHeader(stream, callback) { + * stream.on('error', callback); + * stream.on('readable', onReadable); + * const decoder = new StringDecoder('utf8'); + * let header = ''; + * function onReadable() { + * let chunk; + * while (null !== (chunk = stream.read())) { + * const str = decoder.write(chunk); + * if (str.match(/\n\n/)) { + * // Found the header boundary. + * const split = str.split(/\n\n/); + * header += split.shift(); + * const remaining = split.join('\n\n'); + * const buf = Buffer.from(remaining, 'utf8'); + * stream.removeListener('error', callback); + * // Remove the 'readable' listener before unshifting. + * stream.removeListener('readable', onReadable); + * if (buf.length) + * stream.unshift(buf); + * // Now the body of the message can be read from the stream. + * callback(null, header, stream); + * } else { + * // Still reading the header. + * header += str; + * } + * } + * } + * } + * ``` + * + * Unlike {@link push}, `stream.unshift(chunk)` will not + * end the reading process by resetting the internal reading state of the stream. + * This can cause unexpected results if `readable.unshift()` is called during a + * read (i.e. from within a {@link _read} implementation on a + * custom stream). Following the call to `readable.unshift()` with an immediate {@link push} will reset the reading state appropriately, + * however it is best to simply avoid calling `readable.unshift()` while in the + * process of performing a read. + * @since v0.9.11 + * @param chunk Chunk of data to unshift onto the read queue. For streams not operating in object mode, `chunk` must be a string, `Buffer`, `Uint8Array` or `null`. For object mode + * streams, `chunk` may be any JavaScript value. + * @param encoding Encoding of string chunks. Must be a valid `Buffer` encoding, such as `'utf8'` or `'ascii'`. + */ + unshift(chunk: any, encoding?: BufferEncoding): void; + /** + * Prior to Node.js 0.10, streams did not implement the entire `stream` module API + * as it is currently defined. (See `Compatibility` for more information.) + * + * When using an older Node.js library that emits `'data'` events and has a {@link pause} method that is advisory only, the`readable.wrap()` method can be used to create a `Readable` + * stream that uses + * the old stream as its data source. + * + * It will rarely be necessary to use `readable.wrap()` but the method has been + * provided as a convenience for interacting with older Node.js applications and + * libraries. + * + * ```js + * const { OldReader } = require('./old-api-module.js'); + * const { Readable } = require('stream'); + * const oreader = new OldReader(); + * const myReader = new Readable().wrap(oreader); + * + * myReader.on('readable', () => { + * myReader.read(); // etc. + * }); + * ``` + * @since v0.9.4 + * @param stream An "old style" readable stream + */ + wrap(stream: ReadableStream): this; + push(chunk: any, encoding?: BufferEncoding): boolean; + _destroy( + error: Error | null, + callback: (error?: Error | null) => void, + ): void; + /** + * Destroy the stream. Optionally emit an `'error'` event, and emit a `'close'`event (unless `emitClose` is set to `false`). After this call, the readable + * stream will release any internal resources and subsequent calls to `push()`will be ignored. + * + * Once `destroy()` has been called any further calls will be a no-op and no + * further errors except from `_destroy()` may be emitted as `'error'`. + * + * Implementors should not override this method, but instead implement `readable._destroy()`. + * @since v8.0.0 + * @param error Error which will be passed as payload in `'error'` event + */ + destroy(error?: Error): void; + /** + * Event emitter + * The defined events on documents including: + * 1. close + * 2. data + * 3. end + * 4. error + * 5. pause + * 6. readable + * 7. resume + */ + addListener(event: "close", listener: () => void): this; + addListener(event: "data", listener: (chunk: any) => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "pause", listener: () => void): this; + addListener(event: "readable", listener: () => void): this; + addListener(event: "resume", listener: () => void): this; + addListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + emit(event: "close"): boolean; + emit(event: "data", chunk: any): boolean; + emit(event: "end"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "pause"): boolean; + emit(event: "readable"): boolean; + emit(event: "resume"): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + on(event: "close", listener: () => void): this; + on(event: "data", listener: (chunk: any) => void): this; + on(event: "end", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "pause", listener: () => void): this; + on(event: "readable", listener: () => void): this; + on(event: "resume", listener: () => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "data", listener: (chunk: any) => void): this; + once(event: "end", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "pause", listener: () => void): this; + once(event: "readable", listener: () => void): this; + once(event: "resume", listener: () => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "data", listener: (chunk: any) => void): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "pause", listener: () => void): this; + prependListener(event: "readable", listener: () => void): this; + prependListener(event: "resume", listener: () => void): this; + prependListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "data", listener: (chunk: any) => void): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "pause", listener: () => void): this; + prependOnceListener(event: "readable", listener: () => void): this; + prependOnceListener(event: "resume", listener: () => void): this; + prependOnceListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + removeListener(event: "close", listener: () => void): this; + removeListener(event: "data", listener: (chunk: any) => void): this; + removeListener(event: "end", listener: () => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + removeListener(event: "pause", listener: () => void): this; + removeListener(event: "readable", listener: () => void): this; + removeListener(event: "resume", listener: () => void): this; + removeListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + [Symbol.asyncIterator](): AsyncIterableIterator; +} +export interface WritableOptions extends StreamOptions { + decodeStrings?: boolean | undefined; + defaultEncoding?: BufferEncoding | undefined; + write?( + this: Writable, + chunk: any, + encoding: BufferEncoding, + callback: (error?: Error | null) => void, + ): void; + writev?( + this: Writable, + chunks: Array<{ + chunk: any; + encoding: BufferEncoding; + }>, + callback: (error?: Error | null) => void, + ): void; + final?(this: Writable, callback: (error?: Error | null) => void): void; +} +/** + * @since v0.9.4 + */ +export class Writable extends Stream implements WritableStream { + /** + * A utility method for creating `Writable` from a `WritableStream`. + * @since v17.0.0 + * @experimental + */ + static fromWeb( + writableStream: globalThis.WritableStream, + options?: Pick< + WritableOptions, + "decodeStrings" | "highWaterMark" | "objectMode" | "signal" + >, + ): Writable; + + /** + * Is `true` if it is safe to call `writable.write()`, which means + * the stream has not been destroyed, errored or ended. + * @since v11.4.0 + */ + readonly writable: boolean; + readonly writableBuffer?: Buffered[]; + /** + * Is `true` after `writable.end()` has been called. This property + * does not indicate whether the data has been flushed, for this use `writable.writableFinished` instead. + * @since v12.9.0 + */ + readonly writableEnded: boolean; + /** + * Is set to `true` immediately before the `'finish'` event is emitted. + * @since v12.6.0 + */ + readonly writableFinished: boolean; + /** + * Return the value of `highWaterMark` passed when creating this `Writable`. + * @since v9.3.0 + */ + readonly writableHighWaterMark: number; + /** + * This property contains the number of bytes (or objects) in the queue + * ready to be written. The value provides introspection data regarding + * the status of the `highWaterMark`. + * @since v9.4.0 + */ + readonly writableLength: number; + /** + * Getter for the property `objectMode` of a given `Writable` stream. + * @since v12.3.0 + */ + readonly writableObjectMode: boolean; + /** + * Number of times `writable.uncork()` needs to be + * called in order to fully uncork the stream. + * @since v13.2.0, v12.16.0 + */ + readonly writableCorked: number; + /** + * Is `true` after `writable.destroy()` has been called. + * @since v8.0.0 + */ + destroyed: boolean; + /** + * Is true after 'close' has been emitted. + * @since v8.0.0 + */ + readonly closed: boolean; + constructor(opts?: WritableOptions); + _write( + chunk: any, + encoding: BufferEncoding, + callback: (error?: Error | null) => void, + ): void; + _writev?( + chunks: Array<{ + chunk: any; + encoding: BufferEncoding; + }>, + callback: (error?: Error | null) => void, + ): void; + _construct?(callback: (error?: Error | null) => void): void; + _destroy( + error: Error | null, + callback: (error?: Error | null) => void, + ): void; + _final(callback: (error?: Error | null) => void): void; + /** + * The `writable.write()` method writes some data to the stream, and calls the + * supplied `callback` once the data has been fully handled. If an error + * occurs, the `callback` will be called with the error as its + * first argument. The `callback` is called asynchronously and before `'error'` is + * emitted. + * + * The return value is `true` if the internal buffer is less than the`highWaterMark` configured when the stream was created after admitting `chunk`. + * If `false` is returned, further attempts to write data to the stream should + * stop until the `'drain'` event is emitted. + * + * While a stream is not draining, calls to `write()` will buffer `chunk`, and + * return false. Once all currently buffered chunks are drained (accepted for + * delivery by the operating system), the `'drain'` event will be emitted. + * It is recommended that once `write()` returns false, no more chunks be written + * until the `'drain'` event is emitted. While calling `write()` on a stream that + * is not draining is allowed, Node.js will buffer all written chunks until + * maximum memory usage occurs, at which point it will abort unconditionally. + * Even before it aborts, high memory usage will cause poor garbage collector + * performance and high RSS (which is not typically released back to the system, + * even after the memory is no longer required). Since TCP sockets may never + * drain if the remote peer does not read the data, writing a socket that is + * not draining may lead to a remotely exploitable vulnerability. + * + * Writing data while the stream is not draining is particularly + * problematic for a `Transform`, because the `Transform` streams are paused + * by default until they are piped or a `'data'` or `'readable'` event handler + * is added. + * + * If the data to be written can be generated or fetched on demand, it is + * recommended to encapsulate the logic into a `Readable` and use {@link pipe}. However, if calling `write()` is preferred, it is + * possible to respect backpressure and avoid memory issues using the `'drain'` event: + * + * ```js + * function write(data, cb) { + * if (!stream.write(data)) { + * stream.once('drain', cb); + * } else { + * process.nextTick(cb); + * } + * } + * + * // Wait for cb to be called before doing any other write. + * write('hello', () => { + * console.log('Write completed, do more writes now.'); + * }); + * ``` + * + * A `Writable` stream in object mode will always ignore the `encoding` argument. + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a string, `Buffer` or `Uint8Array`. For object mode streams, `chunk` may be any + * JavaScript value other than `null`. + * @param [encoding='utf8'] The encoding, if `chunk` is a string. + * @param callback Callback for when this chunk of data is flushed. + * @return `false` if the stream wishes for the calling code to wait for the `'drain'` event to be emitted before continuing to write additional data; otherwise `true`. + */ + write( + chunk: any, + callback?: (error: Error | null | undefined) => void, + ): boolean; + write( + chunk: any, + encoding: BufferEncoding, + callback?: (error: Error | null | undefined) => void, + ): boolean; + /** + * The `writable.setDefaultEncoding()` method sets the default `encoding` for a `Writable` stream. + * @since v0.11.15 + * @param encoding The new default encoding + */ + setDefaultEncoding(encoding: BufferEncoding): this; + /** + * Calling the `writable.end()` method signals that no more data will be written + * to the `Writable`. The optional `chunk` and `encoding` arguments allow one + * final additional chunk of data to be written immediately before closing the + * stream. + * + * Calling the {@link write} method after calling {@link end} will raise an error. + * + * ```js + * // Write 'hello, ' and then end with 'world!'. + * const fs = require('fs'); + * const file = fs.createWriteStream('example.txt'); + * file.write('hello, '); + * file.end('world!'); + * // Writing more now is not allowed! + * ``` + * @since v0.9.4 + * @param chunk Optional data to write. For streams not operating in object mode, `chunk` must be a string, `Buffer` or `Uint8Array`. For object mode streams, `chunk` may be any + * JavaScript value other than `null`. + * @param encoding The encoding if `chunk` is a string + * @param callback Callback for when the stream is finished. + */ + end(cb?: () => void): void; + end(chunk: any, cb?: () => void): void; + end(chunk: any, encoding: BufferEncoding, cb?: () => void): void; + /** + * The `writable.cork()` method forces all written data to be buffered in memory. + * The buffered data will be flushed when either the {@link uncork} or {@link end} methods are called. + * + * The primary intent of `writable.cork()` is to accommodate a situation in which + * several small chunks are written to the stream in rapid succession. Instead of + * immediately forwarding them to the underlying destination, `writable.cork()`buffers all the chunks until `writable.uncork()` is called, which will pass them + * all to `writable._writev()`, if present. This prevents a head-of-line blocking + * situation where data is being buffered while waiting for the first small chunk + * to be processed. However, use of `writable.cork()` without implementing`writable._writev()` may have an adverse effect on throughput. + * + * See also: `writable.uncork()`, `writable._writev()`. + * @since v0.11.2 + */ + cork(): void; + /** + * The `writable.uncork()` method flushes all data buffered since {@link cork} was called. + * + * When using `writable.cork()` and `writable.uncork()` to manage the buffering + * of writes to a stream, it is recommended that calls to `writable.uncork()` be + * deferred using `process.nextTick()`. Doing so allows batching of all`writable.write()` calls that occur within a given Node.js event loop phase. + * + * ```js + * stream.cork(); + * stream.write('some '); + * stream.write('data '); + * process.nextTick(() => stream.uncork()); + * ``` + * + * If the `writable.cork()` method is called multiple times on a stream, the + * same number of calls to `writable.uncork()` must be called to flush the buffered + * data. + * + * ```js + * stream.cork(); + * stream.write('some '); + * stream.cork(); + * stream.write('data '); + * process.nextTick(() => { + * stream.uncork(); + * // The data will not be flushed until uncork() is called a second time. + * stream.uncork(); + * }); + * ``` + * + * See also: `writable.cork()`. + * @since v0.11.2 + */ + uncork(): void; + /** + * Destroy the stream. Optionally emit an `'error'` event, and emit a `'close'`event (unless `emitClose` is set to `false`). After this call, the writable + * stream has ended and subsequent calls to `write()` or `end()` will result in + * an `ERR_STREAM_DESTROYED` error. + * This is a destructive and immediate way to destroy a stream. Previous calls to`write()` may not have drained, and may trigger an `ERR_STREAM_DESTROYED` error. + * Use `end()` instead of destroy if data should flush before close, or wait for + * the `'drain'` event before destroying the stream. + * + * Once `destroy()` has been called any further calls will be a no-op and no + * further errors except from `_destroy()` may be emitted as `'error'`. + * + * Implementors should not override this method, + * but instead implement `writable._destroy()`. + * @since v8.0.0 + * @param error Optional, an error to emit with `'error'` event. + */ + destroy(error?: Error): void; + /** + * Event emitter + * The defined events on documents including: + * 1. close + * 2. drain + * 3. error + * 4. finish + * 5. pipe + * 6. unpipe + */ + addListener(event: "close", listener: () => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "finish", listener: () => void): this; + addListener(event: "pipe", listener: (src: Readable) => void): this; + addListener(event: "unpipe", listener: (src: Readable) => void): this; + addListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + emit(event: "close"): boolean; + emit(event: "drain"): boolean; + emit(event: "error", err: Error): boolean; + emit(event: "finish"): boolean; + emit(event: "pipe", src: Readable): boolean; + emit(event: "unpipe", src: Readable): boolean; + emit(event: string | symbol, ...args: any[]): boolean; + on(event: "close", listener: () => void): this; + on(event: "drain", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "finish", listener: () => void): this; + on(event: "pipe", listener: (src: Readable) => void): this; + on(event: "unpipe", listener: (src: Readable) => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "drain", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "finish", listener: () => void): this; + once(event: "pipe", listener: (src: Readable) => void): this; + once(event: "unpipe", listener: (src: Readable) => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "finish", listener: () => void): this; + prependListener(event: "pipe", listener: (src: Readable) => void): this; + prependListener(event: "unpipe", listener: (src: Readable) => void): this; + prependListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "finish", listener: () => void): this; + prependOnceListener(event: "pipe", listener: (src: Readable) => void): this; + prependOnceListener( + event: "unpipe", + listener: (src: Readable) => void, + ): this; + prependOnceListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + removeListener(event: "close", listener: () => void): this; + removeListener(event: "drain", listener: () => void): this; + removeListener(event: "error", listener: (err: Error) => void): this; + removeListener(event: "finish", listener: () => void): this; + removeListener(event: "pipe", listener: (src: Readable) => void): this; + removeListener(event: "unpipe", listener: (src: Readable) => void): this; + removeListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; +} +export interface DuplexOptions extends ReadableOptions, WritableOptions { + allowHalfOpen?: boolean | undefined; + readableObjectMode?: boolean | undefined; + writableObjectMode?: boolean | undefined; + readableHighWaterMark?: number | undefined; + writableHighWaterMark?: number | undefined; + writableCorked?: number | undefined; + construct?(this: Duplex, callback: (error?: Error | null) => void): void; + read?(this: Duplex, size: number): void; + write?( + this: Duplex, + chunk: any, + encoding: BufferEncoding, + callback: (error?: Error | null) => void, + ): void; + writev?( + this: Duplex, + chunks: Array<{ + chunk: any; + encoding: BufferEncoding; + }>, + callback: (error?: Error | null) => void, + ): void; + final?(this: Duplex, callback: (error?: Error | null) => void): void; + destroy?( + this: Duplex, + error: Error | null, + callback: (error: Error | null) => void, + ): void; +} +/** + * Duplex streams are streams that implement both the `Readable` and `Writable` interfaces. + * + * Examples of `Duplex` streams include: + * + * * `TCP sockets` + * * `zlib streams` + * * `crypto streams` + * @since v0.9.4 + */ +export class Duplex extends Readable implements Writable { + readonly writable: boolean; + readonly writableBuffer?: Buffered[]; + readonly writableEnded: boolean; + readonly writableFinished: boolean; + readonly writableHighWaterMark: number; + readonly writableLength: number; + readonly writableObjectMode: boolean; + readonly writableCorked: number; + readonly closed: boolean; + /** + * If `false` then the stream will automatically end the writable side when the + * readable side ends. Set initially by the `allowHalfOpen` constructor option, + * which defaults to `false`. + * + * This can be changed manually to change the half-open behavior of an existing`Duplex` stream instance, but must be changed before the `'end'` event is + * emitted. + * @since v0.9.4 + */ + allowHalfOpen: boolean; + constructor(opts?: DuplexOptions); + /** + * A utility method for creating duplex streams. + * + * - `Stream` converts writable stream into writable `Duplex` and readable stream + * to `Duplex`. + * - `Blob` converts into readable `Duplex`. + * - `string` converts into readable `Duplex`. + * - `ArrayBuffer` converts into readable `Duplex`. + * - `AsyncIterable` converts into a readable `Duplex`. Cannot yield `null`. + * - `AsyncGeneratorFunction` converts into a readable/writable transform + * `Duplex`. Must take a source `AsyncIterable` as first parameter. Cannot yield + * `null`. + * - `AsyncFunction` converts into a writable `Duplex`. Must return + * either `null` or `undefined` + * - `Object ({ writable, readable })` converts `readable` and + * `writable` into `Stream` and then combines them into `Duplex` where the + * `Duplex` will write to the `writable` and read from the `readable`. + * - `Promise` converts into readable `Duplex`. Value `null` is ignored. + * + * @since v16.8.0 + */ + static from( + src: + | Stream + | Blob + | ArrayBuffer + | string + | Iterable + | AsyncIterable + | AsyncGeneratorFunction + | Promise + // deno-lint-ignore ban-types + | Object, + ): Duplex; + _write( + chunk: any, + encoding: BufferEncoding, + callback: (error?: Error | null) => void, + ): void; + _writev?( + chunks: Array<{ + chunk: any; + encoding: BufferEncoding; + }>, + callback: (error?: Error | null) => void, + ): void; + _destroy( + error: Error | null, + callback: (error: Error | null) => void, + ): void; + _final(callback: (error?: Error | null) => void): void; + write( + chunk: any, + encoding?: BufferEncoding, + cb?: (error: Error | null | undefined) => void, + ): boolean; + write(chunk: any, cb?: (error: Error | null | undefined) => void): boolean; + setDefaultEncoding(encoding: BufferEncoding): this; + end(cb?: () => void): void; + end(chunk: any, cb?: () => void): void; + end(chunk: any, encoding?: BufferEncoding, cb?: () => void): void; + cork(): void; + uncork(): void; +} +type TransformCallback = (error?: Error | null, data?: any) => void; +export interface TransformOptions extends DuplexOptions { + construct?(this: Transform, callback: (error?: Error | null) => void): void; + read?(this: Transform, size: number): void; + write?( + this: Transform, + chunk: any, + encoding: BufferEncoding, + callback: (error?: Error | null) => void, + ): void; + writev?( + this: Transform, + chunks: Array<{ + chunk: any; + encoding: BufferEncoding; + }>, + callback: (error?: Error | null) => void, + ): void; + final?(this: Transform, callback: (error?: Error | null) => void): void; + destroy?( + this: Transform, + error: Error | null, + callback: (error: Error | null) => void, + ): void; + transform?( + this: Transform, + chunk: any, + encoding: BufferEncoding, + callback: TransformCallback, + ): void; + flush?(this: Transform, callback: TransformCallback): void; +} +/** + * Transform streams are `Duplex` streams where the output is in some way + * related to the input. Like all `Duplex` streams, `Transform` streams + * implement both the `Readable` and `Writable` interfaces. + * + * Examples of `Transform` streams include: + * + * * `zlib streams` + * * `crypto streams` + * @since v0.9.4 + */ +export class Transform extends Duplex { + constructor(opts?: TransformOptions); + _transform( + chunk: any, + encoding: BufferEncoding, + callback: TransformCallback, + ): void; + _flush(callback: TransformCallback): void; +} +/** + * The `stream.PassThrough` class is a trivial implementation of a `Transform` stream that simply passes the input bytes across to the output. Its purpose is + * primarily for examples and testing, but there are some use cases where `stream.PassThrough` is useful as a building block for novel sorts of streams. + */ +export class PassThrough extends Transform {} +/** + * Attaches an AbortSignal to a readable or writeable stream. This lets code + * control stream destruction using an `AbortController`. + * + * Calling `abort` on the `AbortController` corresponding to the passed`AbortSignal` will behave the same way as calling `.destroy(new AbortError())`on the stream. + * + * ```js + * const fs = require('fs'); + * + * const controller = new AbortController(); + * const read = addAbortSignal( + * controller.signal, + * fs.createReadStream(('object.json')) + * ); + * // Later, abort the operation closing the stream + * controller.abort(); + * ``` + * + * Or using an `AbortSignal` with a readable stream as an async iterable: + * + * ```js + * const controller = new AbortController(); + * setTimeout(() => controller.abort(), 10_000); // set a timeout + * const stream = addAbortSignal( + * controller.signal, + * fs.createReadStream(('object.json')) + * ); + * (async () => { + * try { + * for await (const chunk of stream) { + * await process(chunk); + * } + * } catch (e) { + * if (e.name === 'AbortError') { + * // The operation was cancelled + * } else { + * throw e; + * } + * } + * })(); + * ``` + * @since v15.4.0 + * @param signal A signal representing possible cancellation + * @param stream a stream to attach a signal to + */ +export function addAbortSignal( + signal: AbortSignal, + stream: T, +): T; +interface FinishedOptions extends Abortable { + error?: boolean | undefined; + readable?: boolean | undefined; + writable?: boolean | undefined; +} +/** + * A function to get notified when a stream is no longer readable, writable + * or has experienced an error or a premature close event. + * + * ```js + * const { finished } = require('stream'); + * + * const rs = fs.createReadStream('archive.tar'); + * + * finished(rs, (err) => { + * if (err) { + * console.error('Stream failed.', err); + * } else { + * console.log('Stream is done reading.'); + * } + * }); + * + * rs.resume(); // Drain the stream. + * ``` + * + * Especially useful in error handling scenarios where a stream is destroyed + * prematurely (like an aborted HTTP request), and will not emit `'end'`or `'finish'`. + * + * The `finished` API provides promise version: + * + * ```js + * const { finished } = require('stream/promises'); + * + * const rs = fs.createReadStream('archive.tar'); + * + * async function run() { + * await finished(rs); + * console.log('Stream is done reading.'); + * } + * + * run().catch(console.error); + * rs.resume(); // Drain the stream. + * ``` + * + * `stream.finished()` leaves dangling event listeners (in particular`'error'`, `'end'`, `'finish'` and `'close'`) after `callback` has been + * invoked. The reason for this is so that unexpected `'error'` events (due to + * incorrect stream implementations) do not cause unexpected crashes. + * If this is unwanted behavior then the returned cleanup function needs to be + * invoked in the callback: + * + * ```js + * const cleanup = finished(rs, (err) => { + * cleanup(); + * // ... + * }); + * ``` + * @since v10.0.0 + * @param stream A readable and/or writable stream. + * @param callback A callback function that takes an optional error argument. + * @return A cleanup function which removes all registered listeners. + */ +export function finished( + stream: + | ReadableStream + | WritableStream + | ReadWriteStream, + options: FinishedOptions, + callback: (err?: ErrnoException | null) => void, +): () => void; +export function finished( + stream: + | ReadableStream + | WritableStream + | ReadWriteStream, + callback: (err?: ErrnoException | null) => void, +): () => void; +export namespace finished { + function __promisify__( + stream: + | ReadableStream + | WritableStream + | ReadWriteStream, + options?: FinishedOptions, + ): Promise; +} +type PipelineSourceFunction = () => Iterable | AsyncIterable; +type PipelineSource = + | Iterable + | AsyncIterable + | ReadableStream + | PipelineSourceFunction; +type PipelineTransform, U> = + | ReadWriteStream + | (( + source: S extends + (...args: any[]) => Iterable | AsyncIterable + ? AsyncIterable + : S, + ) => AsyncIterable); +type PipelineTransformSource = + | PipelineSource + | PipelineTransform; +type PipelineDestinationIterableFunction = ( + source: AsyncIterable, +) => AsyncIterable; +type PipelineDestinationPromiseFunction = ( + source: AsyncIterable, +) => Promise

; +type PipelineDestination, P> = S extends + PipelineTransformSource ? + | WritableStream + | PipelineDestinationIterableFunction + | PipelineDestinationPromiseFunction + : never; +type PipelineCallback> = S extends + PipelineDestinationPromiseFunction + ? (err: ErrnoException | null, value: P) => void + : (err: ErrnoException | null) => void; +type PipelinePromise> = S extends + PipelineDestinationPromiseFunction ? Promise

+ : Promise; +interface PipelineOptions { + signal: AbortSignal; +} +/** + * A module method to pipe between streams and generators forwarding errors and + * properly cleaning up and provide a callback when the pipeline is complete. + * + * ```js + * const { pipeline } = require('stream'); + * const fs = require('fs'); + * const zlib = require('zlib'); + * + * // Use the pipeline API to easily pipe a series of streams + * // together and get notified when the pipeline is fully done. + * + * // A pipeline to gzip a potentially huge tar file efficiently: + * + * pipeline( + * fs.createReadStream('archive.tar'), + * zlib.createGzip(), + * fs.createWriteStream('archive.tar.gz'), + * (err) => { + * if (err) { + * console.error('Pipeline failed.', err); + * } else { + * console.log('Pipeline succeeded.'); + * } + * } + * ); + * ``` + * + * The `pipeline` API provides a promise version, which can also + * receive an options argument as the last parameter with a`signal` `AbortSignal` property. When the signal is aborted,`destroy` will be called on the underlying pipeline, with + * an`AbortError`. + * + * ```js + * const { pipeline } = require('stream/promises'); + * + * async function run() { + * await pipeline( + * fs.createReadStream('archive.tar'), + * zlib.createGzip(), + * fs.createWriteStream('archive.tar.gz') + * ); + * console.log('Pipeline succeeded.'); + * } + * + * run().catch(console.error); + * ``` + * + * To use an `AbortSignal`, pass it inside an options object, + * as the last argument: + * + * ```js + * const { pipeline } = require('stream/promises'); + * + * async function run() { + * const ac = new AbortController(); + * const signal = ac.signal; + * + * setTimeout(() => ac.abort(), 1); + * await pipeline( + * fs.createReadStream('archive.tar'), + * zlib.createGzip(), + * fs.createWriteStream('archive.tar.gz'), + * { signal }, + * ); + * } + * + * run().catch(console.error); // AbortError + * ``` + * + * The `pipeline` API also supports async generators: + * + * ```js + * const { pipeline } = require('stream/promises'); + * const fs = require('fs'); + * + * async function run() { + * await pipeline( + * fs.createReadStream('lowercase.txt'), + * async function* (source, signal) { + * source.setEncoding('utf8'); // Work with strings rather than `Buffer`s. + * for await (const chunk of source) { + * yield await processChunk(chunk, { signal }); + * } + * }, + * fs.createWriteStream('uppercase.txt') + * ); + * console.log('Pipeline succeeded.'); + * } + * + * run().catch(console.error); + * ``` + * + * Remember to handle the `signal` argument passed into the async generator. + * Especially in the case where the async generator is the source for the + * pipeline (i.e. first argument) or the pipeline will never complete. + * + * ```js + * const { pipeline } = require('stream/promises'); + * const fs = require('fs'); + * + * async function run() { + * await pipeline( + * async function * (signal) { + * await someLongRunningfn({ signal }); + * yield 'asd'; + * }, + * fs.createWriteStream('uppercase.txt') + * ); + * console.log('Pipeline succeeded.'); + * } + * + * run().catch(console.error); + * ``` + * + * `stream.pipeline()` will call `stream.destroy(err)` on all streams except: + * + * * `Readable` streams which have emitted `'end'` or `'close'`. + * * `Writable` streams which have emitted `'finish'` or `'close'`. + * + * `stream.pipeline()` leaves dangling event listeners on the streams + * after the `callback` has been invoked. In the case of reuse of streams after + * failure, this can cause event listener leaks and swallowed errors. + * @since v10.0.0 + * @param callback Called when the pipeline is fully done. + */ +export function pipeline< + A extends PipelineSource, + B extends PipelineDestination, +>( + source: A, + destination: B, + callback?: PipelineCallback, +): B extends WritableStream ? B : WritableStream; +export function pipeline< + A extends PipelineSource, + T1 extends PipelineTransform, + B extends PipelineDestination, +>( + source: A, + transform1: T1, + destination: B, + callback?: PipelineCallback, +): B extends WritableStream ? B : WritableStream; +export function pipeline< + A extends PipelineSource, + T1 extends PipelineTransform, + T2 extends PipelineTransform, + B extends PipelineDestination, +>( + source: A, + transform1: T1, + transform2: T2, + destination: B, + callback?: PipelineCallback, +): B extends WritableStream ? B : WritableStream; +export function pipeline< + A extends PipelineSource, + T1 extends PipelineTransform, + T2 extends PipelineTransform, + T3 extends PipelineTransform, + B extends PipelineDestination, +>( + source: A, + transform1: T1, + transform2: T2, + transform3: T3, + destination: B, + callback?: PipelineCallback, +): B extends WritableStream ? B : WritableStream; +export function pipeline< + A extends PipelineSource, + T1 extends PipelineTransform, + T2 extends PipelineTransform, + T3 extends PipelineTransform, + T4 extends PipelineTransform, + B extends PipelineDestination, +>( + source: A, + transform1: T1, + transform2: T2, + transform3: T3, + transform4: T4, + destination: B, + callback?: PipelineCallback, +): B extends WritableStream ? B : WritableStream; +export function pipeline( + streams: ReadonlyArray< + ReadableStream | WritableStream | ReadWriteStream + >, + callback?: (err: ErrnoException | null) => void, +): WritableStream; +export function pipeline( + stream1: ReadableStream, + stream2: ReadWriteStream | WritableStream, + ...streams: Array< + | ReadWriteStream + | WritableStream + | ((err: ErrnoException | null) => void) + > +): WritableStream; +export namespace pipeline { + function __promisify__< + A extends PipelineSource, + B extends PipelineDestination, + >(source: A, destination: B, options?: PipelineOptions): PipelinePromise; + function __promisify__< + A extends PipelineSource, + T1 extends PipelineTransform, + B extends PipelineDestination, + >( + source: A, + transform1: T1, + destination: B, + options?: PipelineOptions, + ): PipelinePromise; + function __promisify__< + A extends PipelineSource, + T1 extends PipelineTransform, + T2 extends PipelineTransform, + B extends PipelineDestination, + >( + source: A, + transform1: T1, + transform2: T2, + destination: B, + options?: PipelineOptions, + ): PipelinePromise; + function __promisify__< + A extends PipelineSource, + T1 extends PipelineTransform, + T2 extends PipelineTransform, + T3 extends PipelineTransform, + B extends PipelineDestination, + >( + source: A, + transform1: T1, + transform2: T2, + transform3: T3, + destination: B, + options?: PipelineOptions, + ): PipelinePromise; + function __promisify__< + A extends PipelineSource, + T1 extends PipelineTransform, + T2 extends PipelineTransform, + T3 extends PipelineTransform, + T4 extends PipelineTransform, + B extends PipelineDestination, + >( + source: A, + transform1: T1, + transform2: T2, + transform3: T3, + transform4: T4, + destination: B, + options?: PipelineOptions, + ): PipelinePromise; + function __promisify__( + streams: ReadonlyArray< + ReadableStream | WritableStream | ReadWriteStream + >, + options?: PipelineOptions, + ): Promise; + function __promisify__( + stream1: ReadableStream, + stream2: ReadWriteStream | WritableStream, + ...streams: Array< + ReadWriteStream | WritableStream | PipelineOptions + > + ): Promise; +} +interface Pipe { + close(): void; + hasRef(): boolean; + ref(): void; + unref(): void; +} + +// These have to be at the bottom of the file to work correctly, for some reason +export function _uint8ArrayToBuffer(chunk: Uint8Array): Buffer; +export function _isUint8Array(value: unknown): value is Uint8Array; diff --git a/ext/node/polyfills/_stream.mjs b/ext/node/polyfills/_stream.mjs new file mode 100644 index 0000000000..b933917cb9 --- /dev/null +++ b/ext/node/polyfills/_stream.mjs @@ -0,0 +1,746 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-fmt-ignore-file +// deno-lint-ignore-file +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +import { stdio } from "internal:deno_node/polyfills/_process/stdio.mjs"; + +/* esm.sh - esbuild bundle(readable-stream@4.2.0) es2022 production */ +const __process$ = { nextTick, stdio };import __buffer$ from "internal:deno_node/polyfills/buffer.ts";import __string_decoder$ from "internal:deno_node/polyfills/string_decoder.ts";import __events$ from "internal:deno_node/polyfills/events.ts";var pi=Object.create;var Bt=Object.defineProperty;var wi=Object.getOwnPropertyDescriptor;var yi=Object.getOwnPropertyNames;var gi=Object.getPrototypeOf,Si=Object.prototype.hasOwnProperty;var E=(e=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(e,{get:(t,n)=>(typeof require<"u"?require:t)[n]}):e)(function(e){if(typeof require<"u")return require.apply(this,arguments);throw new Error('Dynamic require of "'+e+'" is not supported')});var g=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);var Ei=(e,t,n,r)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of yi(t))!Si.call(e,i)&&i!==n&&Bt(e,i,{get:()=>t[i],enumerable:!(r=wi(t,i))||r.enumerable});return e};var Ri=(e,t,n)=>(n=e!=null?pi(gi(e)):{},Ei(t||!e||!e.__esModule?Bt(n,"default",{value:e,enumerable:!0}):n,e));var m=g((Yf,Gt)=>{"use strict";Gt.exports={ArrayIsArray(e){return Array.isArray(e)},ArrayPrototypeIncludes(e,t){return e.includes(t)},ArrayPrototypeIndexOf(e,t){return e.indexOf(t)},ArrayPrototypeJoin(e,t){return e.join(t)},ArrayPrototypeMap(e,t){return e.map(t)},ArrayPrototypePop(e,t){return e.pop(t)},ArrayPrototypePush(e,t){return e.push(t)},ArrayPrototypeSlice(e,t,n){return e.slice(t,n)},Error,FunctionPrototypeCall(e,t,...n){return e.call(t,...n)},FunctionPrototypeSymbolHasInstance(e,t){return Function.prototype[Symbol.hasInstance].call(e,t)},MathFloor:Math.floor,Number,NumberIsInteger:Number.isInteger,NumberIsNaN:Number.isNaN,NumberMAX_SAFE_INTEGER:Number.MAX_SAFE_INTEGER,NumberMIN_SAFE_INTEGER:Number.MIN_SAFE_INTEGER,NumberParseInt:Number.parseInt,ObjectDefineProperties(e,t){return Object.defineProperties(e,t)},ObjectDefineProperty(e,t,n){return Object.defineProperty(e,t,n)},ObjectGetOwnPropertyDescriptor(e,t){return Object.getOwnPropertyDescriptor(e,t)},ObjectKeys(e){return Object.keys(e)},ObjectSetPrototypeOf(e,t){return Object.setPrototypeOf(e,t)},Promise,PromisePrototypeCatch(e,t){return e.catch(t)},PromisePrototypeThen(e,t,n){return e.then(t,n)},PromiseReject(e){return Promise.reject(e)},ReflectApply:Reflect.apply,RegExpPrototypeTest(e,t){return e.test(t)},SafeSet:Set,String,StringPrototypeSlice(e,t,n){return e.slice(t,n)},StringPrototypeToLowerCase(e){return e.toLowerCase()},StringPrototypeToUpperCase(e){return e.toUpperCase()},StringPrototypeTrim(e){return e.trim()},Symbol,SymbolAsyncIterator:Symbol.asyncIterator,SymbolHasInstance:Symbol.hasInstance,SymbolIterator:Symbol.iterator,TypedArrayPrototypeSet(e,t,n){return e.set(t,n)},Uint8Array}});var j=g((Kf,Je)=>{"use strict";var Ai=__buffer$,mi=Object.getPrototypeOf(async function(){}).constructor,Ht=globalThis.Blob||Ai.Blob,Ti=typeof Ht<"u"?function(t){return t instanceof Ht}:function(t){return!1},Xe=class extends Error{constructor(t){if(!Array.isArray(t))throw new TypeError(`Expected input to be an Array, got ${typeof t}`);let n="";for(let r=0;r{e=r,t=i}),resolve:e,reject:t}},promisify(e){return new Promise((t,n)=>{e((r,...i)=>r?n(r):t(...i))})},debuglog(){return function(){}},format(e,...t){return e.replace(/%([sdifj])/g,function(...[n,r]){let i=t.shift();return r==="f"?i.toFixed(6):r==="j"?JSON.stringify(i):r==="s"&&typeof i=="object"?`${i.constructor!==Object?i.constructor.name:""} {}`.trim():i.toString()})},inspect(e){switch(typeof e){case"string":if(e.includes("'"))if(e.includes('"')){if(!e.includes("`")&&!e.includes("${"))return`\`${e}\``}else return`"${e}"`;return`'${e}'`;case"number":return isNaN(e)?"NaN":Object.is(e,-0)?String(e):e;case"bigint":return`${String(e)}n`;case"boolean":case"undefined":return String(e);case"object":return"{}"}},types:{isAsyncFunction(e){return e instanceof mi},isArrayBufferView(e){return ArrayBuffer.isView(e)}},isBlob:Ti};Je.exports.promisify.custom=Symbol.for("nodejs.util.promisify.custom")});var O=g((zf,Kt)=>{"use strict";var{format:Ii,inspect:Re,AggregateError:Mi}=j(),Ni=globalThis.AggregateError||Mi,Di=Symbol("kIsNodeError"),Oi=["string","function","number","object","Function","Object","boolean","bigint","symbol"],qi=/^([A-Z][a-z0-9]*)+$/,xi="__node_internal_",Ae={};function X(e,t){if(!e)throw new Ae.ERR_INTERNAL_ASSERTION(t)}function Vt(e){let t="",n=e.length,r=e[0]==="-"?1:0;for(;n>=r+4;n-=3)t=`_${e.slice(n-3,n)}${t}`;return`${e.slice(0,n)}${t}`}function Li(e,t,n){if(typeof t=="function")return X(t.length<=n.length,`Code: ${e}; The provided arguments length (${n.length}) does not match the required ones (${t.length}).`),t(...n);let r=(t.match(/%[dfijoOs]/g)||[]).length;return X(r===n.length,`Code: ${e}; The provided arguments length (${n.length}) does not match the required ones (${r}).`),n.length===0?t:Ii(t,...n)}function N(e,t,n){n||(n=Error);class r extends n{constructor(...o){super(Li(e,t,o))}toString(){return`${this.name} [${e}]: ${this.message}`}}Object.defineProperties(r.prototype,{name:{value:n.name,writable:!0,enumerable:!1,configurable:!0},toString:{value(){return`${this.name} [${e}]: ${this.message}`},writable:!0,enumerable:!1,configurable:!0}}),r.prototype.code=e,r.prototype[Di]=!0,Ae[e]=r}function Yt(e){let t=xi+e.name;return Object.defineProperty(e,"name",{value:t}),e}function Pi(e,t){if(e&&t&&e!==t){if(Array.isArray(t.errors))return t.errors.push(e),t;let n=new Ni([t,e],t.message);return n.code=t.code,n}return e||t}var Qe=class extends Error{constructor(t="The operation was aborted",n=void 0){if(n!==void 0&&typeof n!="object")throw new Ae.ERR_INVALID_ARG_TYPE("options","Object",n);super(t,n),this.code="ABORT_ERR",this.name="AbortError"}};N("ERR_ASSERTION","%s",Error);N("ERR_INVALID_ARG_TYPE",(e,t,n)=>{X(typeof e=="string","'name' must be a string"),Array.isArray(t)||(t=[t]);let r="The ";e.endsWith(" argument")?r+=`${e} `:r+=`"${e}" ${e.includes(".")?"property":"argument"} `,r+="must be ";let i=[],o=[],l=[];for(let f of t)X(typeof f=="string","All expected entries have to be of type string"),Oi.includes(f)?i.push(f.toLowerCase()):qi.test(f)?o.push(f):(X(f!=="object",'The value "object" should be written as "Object"'),l.push(f));if(o.length>0){let f=i.indexOf("object");f!==-1&&(i.splice(i,f,1),o.push("Object"))}if(i.length>0){switch(i.length){case 1:r+=`of type ${i[0]}`;break;case 2:r+=`one of type ${i[0]} or ${i[1]}`;break;default:{let f=i.pop();r+=`one of type ${i.join(", ")}, or ${f}`}}(o.length>0||l.length>0)&&(r+=" or ")}if(o.length>0){switch(o.length){case 1:r+=`an instance of ${o[0]}`;break;case 2:r+=`an instance of ${o[0]} or ${o[1]}`;break;default:{let f=o.pop();r+=`an instance of ${o.join(", ")}, or ${f}`}}l.length>0&&(r+=" or ")}switch(l.length){case 0:break;case 1:l[0].toLowerCase()!==l[0]&&(r+="an "),r+=`${l[0]}`;break;case 2:r+=`one of ${l[0]} or ${l[1]}`;break;default:{let f=l.pop();r+=`one of ${l.join(", ")}, or ${f}`}}if(n==null)r+=`. Received ${n}`;else if(typeof n=="function"&&n.name)r+=`. Received function ${n.name}`;else if(typeof n=="object"){var u;(u=n.constructor)!==null&&u!==void 0&&u.name?r+=`. Received an instance of ${n.constructor.name}`:r+=`. Received ${Re(n,{depth:-1})}`}else{let f=Re(n,{colors:!1});f.length>25&&(f=`${f.slice(0,25)}...`),r+=`. Received type ${typeof n} (${f})`}return r},TypeError);N("ERR_INVALID_ARG_VALUE",(e,t,n="is invalid")=>{let r=Re(t);return r.length>128&&(r=r.slice(0,128)+"..."),`The ${e.includes(".")?"property":"argument"} '${e}' ${n}. Received ${r}`},TypeError);N("ERR_INVALID_RETURN_VALUE",(e,t,n)=>{var r;let i=n!=null&&(r=n.constructor)!==null&&r!==void 0&&r.name?`instance of ${n.constructor.name}`:`type ${typeof n}`;return`Expected ${e} to be returned from the "${t}" function but got ${i}.`},TypeError);N("ERR_MISSING_ARGS",(...e)=>{X(e.length>0,"At least one arg needs to be specified");let t,n=e.length;switch(e=(Array.isArray(e)?e:[e]).map(r=>`"${r}"`).join(" or "),n){case 1:t+=`The ${e[0]} argument`;break;case 2:t+=`The ${e[0]} and ${e[1]} arguments`;break;default:{let r=e.pop();t+=`The ${e.join(", ")}, and ${r} arguments`}break}return`${t} must be specified`},TypeError);N("ERR_OUT_OF_RANGE",(e,t,n)=>{X(t,'Missing "range" argument');let r;return Number.isInteger(n)&&Math.abs(n)>2**32?r=Vt(String(n)):typeof n=="bigint"?(r=String(n),(n>2n**32n||n<-(2n**32n))&&(r=Vt(r)),r+="n"):r=Re(n),`The value of "${e}" is out of range. It must be ${t}. Received ${r}`},RangeError);N("ERR_MULTIPLE_CALLBACK","Callback called multiple times",Error);N("ERR_METHOD_NOT_IMPLEMENTED","The %s method is not implemented",Error);N("ERR_STREAM_ALREADY_FINISHED","Cannot call %s after a stream was finished",Error);N("ERR_STREAM_CANNOT_PIPE","Cannot pipe, not readable",Error);N("ERR_STREAM_DESTROYED","Cannot call %s after a stream was destroyed",Error);N("ERR_STREAM_NULL_VALUES","May not write null values to stream",TypeError);N("ERR_STREAM_PREMATURE_CLOSE","Premature close",Error);N("ERR_STREAM_PUSH_AFTER_EOF","stream.push() after EOF",Error);N("ERR_STREAM_UNSHIFT_AFTER_END_EVENT","stream.unshift() after end event",Error);N("ERR_STREAM_WRITE_AFTER_END","write after end",Error);N("ERR_UNKNOWN_ENCODING","Unknown encoding: %s",TypeError);Kt.exports={AbortError:Qe,aggregateTwoErrors:Yt(Pi),hideStackFrames:Yt,codes:Ae}});var _e=g((Xf,nn)=>{"use strict";var{ArrayIsArray:Jt,ArrayPrototypeIncludes:Qt,ArrayPrototypeJoin:Zt,ArrayPrototypeMap:ki,NumberIsInteger:et,NumberIsNaN:Wi,NumberMAX_SAFE_INTEGER:Ci,NumberMIN_SAFE_INTEGER:ji,NumberParseInt:$i,ObjectPrototypeHasOwnProperty:vi,RegExpPrototypeExec:Fi,String:Ui,StringPrototypeToUpperCase:Bi,StringPrototypeTrim:Gi}=m(),{hideStackFrames:k,codes:{ERR_SOCKET_BAD_PORT:Hi,ERR_INVALID_ARG_TYPE:q,ERR_INVALID_ARG_VALUE:me,ERR_OUT_OF_RANGE:J,ERR_UNKNOWN_SIGNAL:zt}}=O(),{normalizeEncoding:Vi}=j(),{isAsyncFunction:Yi,isArrayBufferView:Ki}=j().types,Xt={};function zi(e){return e===(e|0)}function Xi(e){return e===e>>>0}var Ji=/^[0-7]+$/,Qi="must be a 32-bit unsigned integer or an octal string";function Zi(e,t,n){if(typeof e>"u"&&(e=n),typeof e=="string"){if(Fi(Ji,e)===null)throw new me(t,e,Qi);e=$i(e,8)}return en(e,t),e}var eo=k((e,t,n=ji,r=Ci)=>{if(typeof e!="number")throw new q(t,"number",e);if(!et(e))throw new J(t,"an integer",e);if(er)throw new J(t,`>= ${n} && <= ${r}`,e)}),to=k((e,t,n=-2147483648,r=2147483647)=>{if(typeof e!="number")throw new q(t,"number",e);if(!et(e))throw new J(t,"an integer",e);if(er)throw new J(t,`>= ${n} && <= ${r}`,e)}),en=k((e,t,n=!1)=>{if(typeof e!="number")throw new q(t,"number",e);if(!et(e))throw new J(t,"an integer",e);let r=n?1:0,i=4294967295;if(ei)throw new J(t,`>= ${r} && <= ${i}`,e)});function tn(e,t){if(typeof e!="string")throw new q(t,"string",e)}function no(e,t,n=void 0,r){if(typeof e!="number")throw new q(t,"number",e);if(n!=null&&er||(n!=null||r!=null)&&Wi(e))throw new J(t,`${n!=null?`>= ${n}`:""}${n!=null&&r!=null?" && ":""}${r!=null?`<= ${r}`:""}`,e)}var ro=k((e,t,n)=>{if(!Qt(n,e)){let r=Zt(ki(n,o=>typeof o=="string"?`'${o}'`:Ui(o)),", "),i="must be one of: "+r;throw new me(t,e,i)}});function io(e,t){if(typeof e!="boolean")throw new q(t,"boolean",e)}function Ze(e,t,n){return e==null||!vi(e,t)?n:e[t]}var oo=k((e,t,n=null)=>{let r=Ze(n,"allowArray",!1),i=Ze(n,"allowFunction",!1);if(!Ze(n,"nullable",!1)&&e===null||!r&&Jt(e)||typeof e!="object"&&(!i||typeof e!="function"))throw new q(t,"Object",e)}),lo=k((e,t,n=0)=>{if(!Jt(e))throw new q(t,"Array",e);if(e.length{if(!Ki(e))throw new q(t,["Buffer","TypedArray","DataView"],e)});function uo(e,t){let n=Vi(t),r=e.length;if(n==="hex"&&r%2!==0)throw new me("encoding",t,`is invalid for data of length ${r}`)}function so(e,t="Port",n=!0){if(typeof e!="number"&&typeof e!="string"||typeof e=="string"&&Gi(e).length===0||+e!==+e>>>0||e>65535||e===0&&!n)throw new Hi(t,e,n);return e|0}var co=k((e,t)=>{if(e!==void 0&&(e===null||typeof e!="object"||!("aborted"in e)))throw new q(t,"AbortSignal",e)}),ho=k((e,t)=>{if(typeof e!="function")throw new q(t,"Function",e)}),bo=k((e,t)=>{if(typeof e!="function"||Yi(e))throw new q(t,"Function",e)}),_o=k((e,t)=>{if(e!==void 0)throw new q(t,"undefined",e)});function po(e,t,n){if(!Qt(n,e))throw new q(t,`('${Zt(n,"|")}')`,e)}nn.exports={isInt32:zi,isUint32:Xi,parseFileMode:Zi,validateArray:lo,validateBoolean:io,validateBuffer:fo,validateEncoding:uo,validateFunction:ho,validateInt32:to,validateInteger:eo,validateNumber:no,validateObject:oo,validateOneOf:ro,validatePlainFunction:bo,validatePort:so,validateSignalName:ao,validateString:tn,validateUint32:en,validateUndefined:_o,validateUnion:po,validateAbortSignal:co}});var V=g((Jf,_n)=>{"use strict";var{Symbol:Te,SymbolAsyncIterator:rn,SymbolIterator:on}=m(),ln=Te("kDestroyed"),an=Te("kIsErrored"),tt=Te("kIsReadable"),fn=Te("kIsDisturbed");function Ie(e,t=!1){var n;return!!(e&&typeof e.pipe=="function"&&typeof e.on=="function"&&(!t||typeof e.pause=="function"&&typeof e.resume=="function")&&(!e._writableState||((n=e._readableState)===null||n===void 0?void 0:n.readable)!==!1)&&(!e._writableState||e._readableState))}function Me(e){var t;return!!(e&&typeof e.write=="function"&&typeof e.on=="function"&&(!e._readableState||((t=e._writableState)===null||t===void 0?void 0:t.writable)!==!1))}function wo(e){return!!(e&&typeof e.pipe=="function"&&e._readableState&&typeof e.on=="function"&&typeof e.write=="function")}function Q(e){return e&&(e._readableState||e._writableState||typeof e.write=="function"&&typeof e.on=="function"||typeof e.pipe=="function"&&typeof e.on=="function")}function yo(e,t){return e==null?!1:t===!0?typeof e[rn]=="function":t===!1?typeof e[on]=="function":typeof e[rn]=="function"||typeof e[on]=="function"}function Ne(e){if(!Q(e))return null;let t=e._writableState,n=e._readableState,r=t||n;return!!(e.destroyed||e[ln]||r!=null&&r.destroyed)}function un(e){if(!Me(e))return null;if(e.writableEnded===!0)return!0;let t=e._writableState;return t!=null&&t.errored?!1:typeof t?.ended!="boolean"?null:t.ended}function go(e,t){if(!Me(e))return null;if(e.writableFinished===!0)return!0;let n=e._writableState;return n!=null&&n.errored?!1:typeof n?.finished!="boolean"?null:!!(n.finished||t===!1&&n.ended===!0&&n.length===0)}function So(e){if(!Ie(e))return null;if(e.readableEnded===!0)return!0;let t=e._readableState;return!t||t.errored?!1:typeof t?.ended!="boolean"?null:t.ended}function sn(e,t){if(!Ie(e))return null;let n=e._readableState;return n!=null&&n.errored?!1:typeof n?.endEmitted!="boolean"?null:!!(n.endEmitted||t===!1&&n.ended===!0&&n.length===0)}function dn(e){return e&&e[tt]!=null?e[tt]:typeof e?.readable!="boolean"?null:Ne(e)?!1:Ie(e)&&e.readable&&!sn(e)}function cn(e){return typeof e?.writable!="boolean"?null:Ne(e)?!1:Me(e)&&e.writable&&!un(e)}function Eo(e,t){return Q(e)?Ne(e)?!0:!(t?.readable!==!1&&dn(e)||t?.writable!==!1&&cn(e)):null}function Ro(e){var t,n;return Q(e)?e.writableErrored?e.writableErrored:(t=(n=e._writableState)===null||n===void 0?void 0:n.errored)!==null&&t!==void 0?t:null:null}function Ao(e){var t,n;return Q(e)?e.readableErrored?e.readableErrored:(t=(n=e._readableState)===null||n===void 0?void 0:n.errored)!==null&&t!==void 0?t:null:null}function mo(e){if(!Q(e))return null;if(typeof e.closed=="boolean")return e.closed;let t=e._writableState,n=e._readableState;return typeof t?.closed=="boolean"||typeof n?.closed=="boolean"?t?.closed||n?.closed:typeof e._closed=="boolean"&&hn(e)?e._closed:null}function hn(e){return typeof e._closed=="boolean"&&typeof e._defaultKeepAlive=="boolean"&&typeof e._removedConnection=="boolean"&&typeof e._removedContLen=="boolean"}function bn(e){return typeof e._sent100=="boolean"&&hn(e)}function To(e){var t;return typeof e._consuming=="boolean"&&typeof e._dumped=="boolean"&&((t=e.req)===null||t===void 0?void 0:t.upgradeOrConnect)===void 0}function Io(e){if(!Q(e))return null;let t=e._writableState,n=e._readableState,r=t||n;return!r&&bn(e)||!!(r&&r.autoDestroy&&r.emitClose&&r.closed===!1)}function Mo(e){var t;return!!(e&&((t=e[fn])!==null&&t!==void 0?t:e.readableDidRead||e.readableAborted))}function No(e){var t,n,r,i,o,l,u,f,a,c;return!!(e&&((t=(n=(r=(i=(o=(l=e[an])!==null&&l!==void 0?l:e.readableErrored)!==null&&o!==void 0?o:e.writableErrored)!==null&&i!==void 0?i:(u=e._readableState)===null||u===void 0?void 0:u.errorEmitted)!==null&&r!==void 0?r:(f=e._writableState)===null||f===void 0?void 0:f.errorEmitted)!==null&&n!==void 0?n:(a=e._readableState)===null||a===void 0?void 0:a.errored)!==null&&t!==void 0?t:(c=e._writableState)===null||c===void 0?void 0:c.errored))}_n.exports={kDestroyed:ln,isDisturbed:Mo,kIsDisturbed:fn,isErrored:No,kIsErrored:an,isReadable:dn,kIsReadable:tt,isClosed:mo,isDestroyed:Ne,isDuplexNodeStream:wo,isFinished:Eo,isIterable:yo,isReadableNodeStream:Ie,isReadableEnded:So,isReadableFinished:sn,isReadableErrored:Ao,isNodeStream:Q,isWritable:cn,isWritableNodeStream:Me,isWritableEnded:un,isWritableFinished:go,isWritableErrored:Ro,isServerRequest:To,isServerResponse:bn,willEmitClose:Io}});var Y=g((Qf,rt)=>{var oe=__process$,{AbortError:Do,codes:Oo}=O(),{ERR_INVALID_ARG_TYPE:qo,ERR_STREAM_PREMATURE_CLOSE:pn}=Oo,{kEmptyObject:wn,once:yn}=j(),{validateAbortSignal:xo,validateFunction:Lo,validateObject:Po}=_e(),{Promise:ko}=m(),{isClosed:Wo,isReadable:gn,isReadableNodeStream:nt,isReadableFinished:Sn,isReadableErrored:Co,isWritable:En,isWritableNodeStream:Rn,isWritableFinished:An,isWritableErrored:jo,isNodeStream:$o,willEmitClose:vo}=V();function Fo(e){return e.setHeader&&typeof e.abort=="function"}var Uo=()=>{};function mn(e,t,n){var r,i;arguments.length===2?(n=t,t=wn):t==null?t=wn:Po(t,"options"),Lo(n,"callback"),xo(t.signal,"options.signal"),n=yn(n);let o=(r=t.readable)!==null&&r!==void 0?r:nt(e),l=(i=t.writable)!==null&&i!==void 0?i:Rn(e);if(!$o(e))throw new qo("stream","Stream",e);let u=e._writableState,f=e._readableState,a=()=>{e.writable||b()},c=vo(e)&&nt(e)===o&&Rn(e)===l,s=An(e,!1),b=()=>{s=!0,e.destroyed&&(c=!1),!(c&&(!e.readable||o))&&(!o||d)&&n.call(e)},d=Sn(e,!1),h=()=>{d=!0,e.destroyed&&(c=!1),!(c&&(!e.writable||l))&&(!l||s)&&n.call(e)},D=M=>{n.call(e,M)},L=Wo(e),_=()=>{L=!0;let M=jo(e)||Co(e);if(M&&typeof M!="boolean")return n.call(e,M);if(o&&!d&&nt(e,!0)&&!Sn(e,!1))return n.call(e,new pn);if(l&&!s&&!An(e,!1))return n.call(e,new pn);n.call(e)},p=()=>{e.req.on("finish",b)};Fo(e)?(e.on("complete",b),c||e.on("abort",_),e.req?p():e.on("request",p)):l&&!u&&(e.on("end",a),e.on("close",a)),!c&&typeof e.aborted=="boolean"&&e.on("aborted",_),e.on("end",h),e.on("finish",b),t.error!==!1&&e.on("error",D),e.on("close",_),L?oe.nextTick(_):u!=null&&u.errorEmitted||f!=null&&f.errorEmitted?c||oe.nextTick(_):(!o&&(!c||gn(e))&&(s||En(e)===!1)||!l&&(!c||En(e))&&(d||gn(e)===!1)||f&&e.req&&e.aborted)&&oe.nextTick(_);let I=()=>{n=Uo,e.removeListener("aborted",_),e.removeListener("complete",b),e.removeListener("abort",_),e.removeListener("request",p),e.req&&e.req.removeListener("finish",b),e.removeListener("end",a),e.removeListener("close",a),e.removeListener("finish",b),e.removeListener("end",h),e.removeListener("error",D),e.removeListener("close",_)};if(t.signal&&!L){let M=()=>{let F=n;I(),F.call(e,new Do(void 0,{cause:t.signal.reason}))};if(t.signal.aborted)oe.nextTick(M);else{let F=n;n=yn((...re)=>{t.signal.removeEventListener("abort",M),F.apply(e,re)}),t.signal.addEventListener("abort",M)}}return I}function Bo(e,t){return new ko((n,r)=>{mn(e,t,i=>{i?r(i):n()})})}rt.exports=mn;rt.exports.finished=Bo});var xn=g((Zf,lt)=>{"use strict";var Nn=globalThis.AbortController,{codes:{ERR_INVALID_ARG_TYPE:pe,ERR_MISSING_ARGS:Go,ERR_OUT_OF_RANGE:Ho},AbortError:$}=O(),{validateAbortSignal:le,validateInteger:Vo,validateObject:ae}=_e(),Yo=m().Symbol("kWeak"),{finished:Ko}=Y(),{ArrayPrototypePush:zo,MathFloor:Xo,Number:Jo,NumberIsNaN:Qo,Promise:Tn,PromiseReject:In,PromisePrototypeThen:Zo,Symbol:Dn}=m(),De=Dn("kEmpty"),Mn=Dn("kEof");function Oe(e,t){if(typeof e!="function")throw new pe("fn",["Function","AsyncFunction"],e);t!=null&&ae(t,"options"),t?.signal!=null&&le(t.signal,"options.signal");let n=1;return t?.concurrency!=null&&(n=Xo(t.concurrency)),Vo(n,"concurrency",1),async function*(){var i,o;let l=new Nn,u=this,f=[],a=l.signal,c={signal:a},s=()=>l.abort();t!=null&&(i=t.signal)!==null&&i!==void 0&&i.aborted&&s(),t==null||(o=t.signal)===null||o===void 0||o.addEventListener("abort",s);let b,d,h=!1;function D(){h=!0}async function L(){try{for await(let I of u){var _;if(h)return;if(a.aborted)throw new $;try{I=e(I,c)}catch(M){I=In(M)}I!==De&&(typeof((_=I)===null||_===void 0?void 0:_.catch)=="function"&&I.catch(D),f.push(I),b&&(b(),b=null),!h&&f.length&&f.length>=n&&await new Tn(M=>{d=M}))}f.push(Mn)}catch(I){let M=In(I);Zo(M,void 0,D),f.push(M)}finally{var p;h=!0,b&&(b(),b=null),t==null||(p=t.signal)===null||p===void 0||p.removeEventListener("abort",s)}}L();try{for(;;){for(;f.length>0;){let _=await f[0];if(_===Mn)return;if(a.aborted)throw new $;_!==De&&(yield _),f.shift(),d&&(d(),d=null)}await new Tn(_=>{b=_})}}finally{l.abort(),h=!0,d&&(d(),d=null)}}.call(this)}function el(e=void 0){return e!=null&&ae(e,"options"),e?.signal!=null&&le(e.signal,"options.signal"),async function*(){let n=0;for await(let i of this){var r;if(e!=null&&(r=e.signal)!==null&&r!==void 0&&r.aborted)throw new $({cause:e.signal.reason});yield[n++,i]}}.call(this)}async function On(e,t=void 0){for await(let n of ot.call(this,e,t))return!0;return!1}async function tl(e,t=void 0){if(typeof e!="function")throw new pe("fn",["Function","AsyncFunction"],e);return!await On.call(this,async(...n)=>!await e(...n),t)}async function nl(e,t){for await(let n of ot.call(this,e,t))return n}async function rl(e,t){if(typeof e!="function")throw new pe("fn",["Function","AsyncFunction"],e);async function n(r,i){return await e(r,i),De}for await(let r of Oe.call(this,n,t));}function ot(e,t){if(typeof e!="function")throw new pe("fn",["Function","AsyncFunction"],e);async function n(r,i){return await e(r,i)?r:De}return Oe.call(this,n,t)}var it=class extends Go{constructor(){super("reduce"),this.message="Reduce of an empty stream requires an initial value"}};async function il(e,t,n){var r;if(typeof e!="function")throw new pe("reducer",["Function","AsyncFunction"],e);n!=null&&ae(n,"options"),n?.signal!=null&&le(n.signal,"options.signal");let i=arguments.length>1;if(n!=null&&(r=n.signal)!==null&&r!==void 0&&r.aborted){let a=new $(void 0,{cause:n.signal.reason});throw this.once("error",()=>{}),await Ko(this.destroy(a)),a}let o=new Nn,l=o.signal;if(n!=null&&n.signal){let a={once:!0,[Yo]:this};n.signal.addEventListener("abort",()=>o.abort(),a)}let u=!1;try{for await(let a of this){var f;if(u=!0,n!=null&&(f=n.signal)!==null&&f!==void 0&&f.aborted)throw new $;i?t=await e(t,a,{signal:l}):(t=a,i=!0)}if(!u&&!i)throw new it}finally{o.abort()}return t}async function ol(e){e!=null&&ae(e,"options"),e?.signal!=null&&le(e.signal,"options.signal");let t=[];for await(let r of this){var n;if(e!=null&&(n=e.signal)!==null&&n!==void 0&&n.aborted)throw new $(void 0,{cause:e.signal.reason});zo(t,r)}return t}function ll(e,t){let n=Oe.call(this,e,t);return async function*(){for await(let i of n)yield*i}.call(this)}function qn(e){if(e=Jo(e),Qo(e))return 0;if(e<0)throw new Ho("number",">= 0",e);return e}function al(e,t=void 0){return t!=null&&ae(t,"options"),t?.signal!=null&&le(t.signal,"options.signal"),e=qn(e),async function*(){var r;if(t!=null&&(r=t.signal)!==null&&r!==void 0&&r.aborted)throw new $;for await(let o of this){var i;if(t!=null&&(i=t.signal)!==null&&i!==void 0&&i.aborted)throw new $;e--<=0&&(yield o)}}.call(this)}function fl(e,t=void 0){return t!=null&&ae(t,"options"),t?.signal!=null&&le(t.signal,"options.signal"),e=qn(e),async function*(){var r;if(t!=null&&(r=t.signal)!==null&&r!==void 0&&r.aborted)throw new $;for await(let o of this){var i;if(t!=null&&(i=t.signal)!==null&&i!==void 0&&i.aborted)throw new $;if(e-- >0)yield o;else return}}.call(this)}lt.exports.streamReturningOperators={asIndexedPairs:el,drop:al,filter:ot,flatMap:ll,map:Oe,take:fl};lt.exports.promiseReturningOperators={every:tl,forEach:rl,reduce:il,toArray:ol,some:On,find:nl}});var Z=g((eu,vn)=>{"use strict";var K=__process$,{aggregateTwoErrors:ul,codes:{ERR_MULTIPLE_CALLBACK:sl},AbortError:dl}=O(),{Symbol:kn}=m(),{kDestroyed:cl,isDestroyed:hl,isFinished:bl,isServerRequest:_l}=V(),Wn=kn("kDestroy"),at=kn("kConstruct");function Cn(e,t,n){e&&(e.stack,t&&!t.errored&&(t.errored=e),n&&!n.errored&&(n.errored=e))}function pl(e,t){let n=this._readableState,r=this._writableState,i=r||n;return r&&r.destroyed||n&&n.destroyed?(typeof t=="function"&&t(),this):(Cn(e,r,n),r&&(r.destroyed=!0),n&&(n.destroyed=!0),i.constructed?Ln(this,e,t):this.once(Wn,function(o){Ln(this,ul(o,e),t)}),this)}function Ln(e,t,n){let r=!1;function i(o){if(r)return;r=!0;let l=e._readableState,u=e._writableState;Cn(o,u,l),u&&(u.closed=!0),l&&(l.closed=!0),typeof n=="function"&&n(o),o?K.nextTick(wl,e,o):K.nextTick(jn,e)}try{e._destroy(t||null,i)}catch(o){i(o)}}function wl(e,t){ft(e,t),jn(e)}function jn(e){let t=e._readableState,n=e._writableState;n&&(n.closeEmitted=!0),t&&(t.closeEmitted=!0),(n&&n.emitClose||t&&t.emitClose)&&e.emit("close")}function ft(e,t){let n=e._readableState,r=e._writableState;r&&r.errorEmitted||n&&n.errorEmitted||(r&&(r.errorEmitted=!0),n&&(n.errorEmitted=!0),e.emit("error",t))}function yl(){let e=this._readableState,t=this._writableState;e&&(e.constructed=!0,e.closed=!1,e.closeEmitted=!1,e.destroyed=!1,e.errored=null,e.errorEmitted=!1,e.reading=!1,e.ended=e.readable===!1,e.endEmitted=e.readable===!1),t&&(t.constructed=!0,t.destroyed=!1,t.closed=!1,t.closeEmitted=!1,t.errored=null,t.errorEmitted=!1,t.finalCalled=!1,t.prefinished=!1,t.ended=t.writable===!1,t.ending=t.writable===!1,t.finished=t.writable===!1)}function ut(e,t,n){let r=e._readableState,i=e._writableState;if(i&&i.destroyed||r&&r.destroyed)return this;r&&r.autoDestroy||i&&i.autoDestroy?e.destroy(t):t&&(t.stack,i&&!i.errored&&(i.errored=t),r&&!r.errored&&(r.errored=t),n?K.nextTick(ft,e,t):ft(e,t))}function gl(e,t){if(typeof e._construct!="function")return;let n=e._readableState,r=e._writableState;n&&(n.constructed=!1),r&&(r.constructed=!1),e.once(at,t),!(e.listenerCount(at)>1)&&K.nextTick(Sl,e)}function Sl(e){let t=!1;function n(r){if(t){ut(e,r??new sl);return}t=!0;let i=e._readableState,o=e._writableState,l=o||i;i&&(i.constructed=!0),o&&(o.constructed=!0),l.destroyed?e.emit(Wn,r):r?ut(e,r,!0):K.nextTick(El,e)}try{e._construct(n)}catch(r){n(r)}}function El(e){e.emit(at)}function Pn(e){return e&&e.setHeader&&typeof e.abort=="function"}function $n(e){e.emit("close")}function Rl(e,t){e.emit("error",t),K.nextTick($n,e)}function Al(e,t){!e||hl(e)||(!t&&!bl(e)&&(t=new dl),_l(e)?(e.socket=null,e.destroy(t)):Pn(e)?e.abort():Pn(e.req)?e.req.abort():typeof e.destroy=="function"?e.destroy(t):typeof e.close=="function"?e.close():t?K.nextTick(Rl,e,t):K.nextTick($n,e),e.destroyed||(e[cl]=!0))}vn.exports={construct:gl,destroyer:Al,destroy:pl,undestroy:yl,errorOrDestroy:ut}});var Le=g((tu,Un)=>{"use strict";var{ArrayIsArray:ml,ObjectSetPrototypeOf:Fn}=m(),{EventEmitter:qe}=__events$;function xe(e){qe.call(this,e)}Fn(xe.prototype,qe.prototype);Fn(xe,qe);xe.prototype.pipe=function(e,t){let n=this;function r(c){e.writable&&e.write(c)===!1&&n.pause&&n.pause()}n.on("data",r);function i(){n.readable&&n.resume&&n.resume()}e.on("drain",i),!e._isStdio&&(!t||t.end!==!1)&&(n.on("end",l),n.on("close",u));let o=!1;function l(){o||(o=!0,e.end())}function u(){o||(o=!0,typeof e.destroy=="function"&&e.destroy())}function f(c){a(),qe.listenerCount(this,"error")===0&&this.emit("error",c)}st(n,"error",f),st(e,"error",f);function a(){n.removeListener("data",r),e.removeListener("drain",i),n.removeListener("end",l),n.removeListener("close",u),n.removeListener("error",f),e.removeListener("error",f),n.removeListener("end",a),n.removeListener("close",a),e.removeListener("close",a)}return n.on("end",a),n.on("close",a),e.on("close",a),e.emit("pipe",n),e};function st(e,t,n){if(typeof e.prependListener=="function")return e.prependListener(t,n);!e._events||!e._events[t]?e.on(t,n):ml(e._events[t])?e._events[t].unshift(n):e._events[t]=[n,e._events[t]]}Un.exports={Stream:xe,prependListener:st}});var ke=g((nu,Pe)=>{"use strict";var{AbortError:Tl,codes:Il}=O(),Ml=Y(),{ERR_INVALID_ARG_TYPE:Bn}=Il,Nl=(e,t)=>{if(typeof e!="object"||!("aborted"in e))throw new Bn(t,"AbortSignal",e)};function Dl(e){return!!(e&&typeof e.pipe=="function")}Pe.exports.addAbortSignal=function(t,n){if(Nl(t,"signal"),!Dl(n))throw new Bn("stream","stream.Stream",n);return Pe.exports.addAbortSignalNoValidate(t,n)};Pe.exports.addAbortSignalNoValidate=function(e,t){if(typeof e!="object"||!("aborted"in e))return t;let n=()=>{t.destroy(new Tl(void 0,{cause:e.reason}))};return e.aborted?n():(e.addEventListener("abort",n),Ml(t,()=>e.removeEventListener("abort",n))),t}});var Vn=g((iu,Hn)=>{"use strict";var{StringPrototypeSlice:Gn,SymbolIterator:Ol,TypedArrayPrototypeSet:We,Uint8Array:ql}=m(),{Buffer:dt}=__buffer$,{inspect:xl}=j();Hn.exports=class{constructor(){this.head=null,this.tail=null,this.length=0}push(t){let n={data:t,next:null};this.length>0?this.tail.next=n:this.head=n,this.tail=n,++this.length}unshift(t){let n={data:t,next:this.head};this.length===0&&(this.tail=n),this.head=n,++this.length}shift(){if(this.length===0)return;let t=this.head.data;return this.length===1?this.head=this.tail=null:this.head=this.head.next,--this.length,t}clear(){this.head=this.tail=null,this.length=0}join(t){if(this.length===0)return"";let n=this.head,r=""+n.data;for(;(n=n.next)!==null;)r+=t+n.data;return r}concat(t){if(this.length===0)return dt.alloc(0);let n=dt.allocUnsafe(t>>>0),r=this.head,i=0;for(;r;)We(n,r.data,i),i+=r.data.length,r=r.next;return n}consume(t,n){let r=this.head.data;if(to.length)n+=o,t-=o.length;else{t===o.length?(n+=o,++i,r.next?this.head=r.next:this.head=this.tail=null):(n+=Gn(o,0,t),this.head=r,r.data=Gn(o,t));break}++i}while((r=r.next)!==null);return this.length-=i,n}_getBuffer(t){let n=dt.allocUnsafe(t),r=t,i=this.head,o=0;do{let l=i.data;if(t>l.length)We(n,l,r-t),t-=l.length;else{t===l.length?(We(n,l,r-t),++o,i.next?this.head=i.next:this.head=this.tail=null):(We(n,new ql(l.buffer,l.byteOffset,t),r-t),this.head=i,i.data=l.slice(t));break}++o}while((i=i.next)!==null);return this.length-=o,n}[Symbol.for("nodejs.util.inspect.custom")](t,n){return xl(this,{...n,depth:0,customInspect:!1})}}});var Ce=g((ou,Kn)=>{"use strict";var{MathFloor:Ll,NumberIsInteger:Pl}=m(),{ERR_INVALID_ARG_VALUE:kl}=O().codes;function Wl(e,t,n){return e.highWaterMark!=null?e.highWaterMark:t?e[n]:null}function Yn(e){return e?16:16*1024}function Cl(e,t,n,r){let i=Wl(t,r,n);if(i!=null){if(!Pl(i)||i<0){let o=r?`options.${n}`:"options.highWaterMark";throw new kl(o,i)}return Ll(i)}return Yn(e.objectMode)}Kn.exports={getHighWaterMark:Cl,getDefaultHighWaterMark:Yn}});var ct=g((lu,Qn)=>{"use strict";var zn=__process$,{PromisePrototypeThen:jl,SymbolAsyncIterator:Xn,SymbolIterator:Jn}=m(),{Buffer:$l}=__buffer$,{ERR_INVALID_ARG_TYPE:vl,ERR_STREAM_NULL_VALUES:Fl}=O().codes;function Ul(e,t,n){let r;if(typeof t=="string"||t instanceof $l)return new e({objectMode:!0,...n,read(){this.push(t),this.push(null)}});let i;if(t&&t[Xn])i=!0,r=t[Xn]();else if(t&&t[Jn])i=!1,r=t[Jn]();else throw new vl("iterable",["Iterable"],t);let o=new e({objectMode:!0,highWaterMark:1,...n}),l=!1;o._read=function(){l||(l=!0,f())},o._destroy=function(a,c){jl(u(a),()=>zn.nextTick(c,a),s=>zn.nextTick(c,s||a))};async function u(a){let c=a!=null,s=typeof r.throw=="function";if(c&&s){let{value:b,done:d}=await r.throw(a);if(await b,d)return}if(typeof r.return=="function"){let{value:b}=await r.return();await b}}async function f(){for(;;){try{let{value:a,done:c}=i?await r.next():r.next();if(c)o.push(null);else{let s=a&&typeof a.then=="function"?await a:a;if(s===null)throw l=!1,new Fl;if(o.push(s))continue;l=!1}}catch(a){o.destroy(a)}break}}return o}Qn.exports=Ul});var we=g((au,dr)=>{var W=__process$,{ArrayPrototypeIndexOf:Bl,NumberIsInteger:Gl,NumberIsNaN:Hl,NumberParseInt:Vl,ObjectDefineProperties:tr,ObjectKeys:Yl,ObjectSetPrototypeOf:nr,Promise:Kl,SafeSet:zl,SymbolAsyncIterator:Xl,Symbol:Jl}=m();dr.exports=w;w.ReadableState=yt;var{EventEmitter:Ql}=__events$,{Stream:z,prependListener:Zl}=Le(),{Buffer:ht}=__buffer$,{addAbortSignal:ea}=ke(),ta=Y(),y=j().debuglog("stream",e=>{y=e}),na=Vn(),ue=Z(),{getHighWaterMark:ra,getDefaultHighWaterMark:ia}=Ce(),{aggregateTwoErrors:Zn,codes:{ERR_INVALID_ARG_TYPE:oa,ERR_METHOD_NOT_IMPLEMENTED:la,ERR_OUT_OF_RANGE:aa,ERR_STREAM_PUSH_AFTER_EOF:fa,ERR_STREAM_UNSHIFT_AFTER_END_EVENT:ua}}=O(),{validateObject:sa}=_e(),ee=Jl("kPaused"),{StringDecoder:rr}=__string_decoder$,da=ct();nr(w.prototype,z.prototype);nr(w,z);var bt=()=>{},{errorOrDestroy:fe}=ue;function yt(e,t,n){typeof n!="boolean"&&(n=t instanceof v()),this.objectMode=!!(e&&e.objectMode),n&&(this.objectMode=this.objectMode||!!(e&&e.readableObjectMode)),this.highWaterMark=e?ra(this,e,"readableHighWaterMark",n):ia(!1),this.buffer=new na,this.length=0,this.pipes=[],this.flowing=null,this.ended=!1,this.endEmitted=!1,this.reading=!1,this.constructed=!0,this.sync=!0,this.needReadable=!1,this.emittedReadable=!1,this.readableListening=!1,this.resumeScheduled=!1,this[ee]=null,this.errorEmitted=!1,this.emitClose=!e||e.emitClose!==!1,this.autoDestroy=!e||e.autoDestroy!==!1,this.destroyed=!1,this.errored=null,this.closed=!1,this.closeEmitted=!1,this.defaultEncoding=e&&e.defaultEncoding||"utf8",this.awaitDrainWriters=null,this.multiAwaitDrain=!1,this.readingMore=!1,this.dataEmitted=!1,this.decoder=null,this.encoding=null,e&&e.encoding&&(this.decoder=new rr(e.encoding),this.encoding=e.encoding)}function w(e){if(!(this instanceof w))return new w(e);let t=this instanceof v();this._readableState=new yt(e,this,t),e&&(typeof e.read=="function"&&(this._read=e.read),typeof e.destroy=="function"&&(this._destroy=e.destroy),typeof e.construct=="function"&&(this._construct=e.construct),e.signal&&!t&&ea(e.signal,this)),z.call(this,e),ue.construct(this,()=>{this._readableState.needReadable&&je(this,this._readableState)})}w.prototype.destroy=ue.destroy;w.prototype._undestroy=ue.undestroy;w.prototype._destroy=function(e,t){t(e)};w.prototype[Ql.captureRejectionSymbol]=function(e){this.destroy(e)};w.prototype.push=function(e,t){return ir(this,e,t,!1)};w.prototype.unshift=function(e,t){return ir(this,e,t,!0)};function ir(e,t,n,r){y("readableAddChunk",t);let i=e._readableState,o;if(i.objectMode||(typeof t=="string"?(n=n||i.defaultEncoding,i.encoding!==n&&(r&&i.encoding?t=ht.from(t,n).toString(i.encoding):(t=ht.from(t,n),n=""))):t instanceof ht?n="":z._isUint8Array(t)?(t=z._uint8ArrayToBuffer(t),n=""):t!=null&&(o=new oa("chunk",["string","Buffer","Uint8Array"],t))),o)fe(e,o);else if(t===null)i.reading=!1,ba(e,i);else if(i.objectMode||t&&t.length>0)if(r)if(i.endEmitted)fe(e,new ua);else{if(i.destroyed||i.errored)return!1;_t(e,i,t,!0)}else if(i.ended)fe(e,new fa);else{if(i.destroyed||i.errored)return!1;i.reading=!1,i.decoder&&!n?(t=i.decoder.write(t),i.objectMode||t.length!==0?_t(e,i,t,!1):je(e,i)):_t(e,i,t,!1)}else r||(i.reading=!1,je(e,i));return!i.ended&&(i.length0?(t.multiAwaitDrain?t.awaitDrainWriters.clear():t.awaitDrainWriters=null,t.dataEmitted=!0,e.emit("data",n)):(t.length+=t.objectMode?1:n.length,r?t.buffer.unshift(n):t.buffer.push(n),t.needReadable&&$e(e)),je(e,t)}w.prototype.isPaused=function(){let e=this._readableState;return e[ee]===!0||e.flowing===!1};w.prototype.setEncoding=function(e){let t=new rr(e);this._readableState.decoder=t,this._readableState.encoding=this._readableState.decoder.encoding;let n=this._readableState.buffer,r="";for(let i of n)r+=t.write(i);return n.clear(),r!==""&&n.push(r),this._readableState.length=r.length,this};var ca=1073741824;function ha(e){if(e>ca)throw new aa("size","<= 1GiB",e);return e--,e|=e>>>1,e|=e>>>2,e|=e>>>4,e|=e>>>8,e|=e>>>16,e++,e}function er(e,t){return e<=0||t.length===0&&t.ended?0:t.objectMode?1:Hl(e)?t.flowing&&t.length?t.buffer.first().length:t.length:e<=t.length?e:t.ended?t.length:0}w.prototype.read=function(e){y("read",e),e===void 0?e=NaN:Gl(e)||(e=Vl(e,10));let t=this._readableState,n=e;if(e>t.highWaterMark&&(t.highWaterMark=ha(e)),e!==0&&(t.emittedReadable=!1),e===0&&t.needReadable&&((t.highWaterMark!==0?t.length>=t.highWaterMark:t.length>0)||t.ended))return y("read: emitReadable",t.length,t.ended),t.length===0&&t.ended?pt(this):$e(this),null;if(e=er(e,t),e===0&&t.ended)return t.length===0&&pt(this),null;let r=t.needReadable;if(y("need readable",r),(t.length===0||t.length-e0?i=ur(e,t):i=null,i===null?(t.needReadable=t.length<=t.highWaterMark,e=0):(t.length-=e,t.multiAwaitDrain?t.awaitDrainWriters.clear():t.awaitDrainWriters=null),t.length===0&&(t.ended||(t.needReadable=!0),n!==e&&t.ended&&pt(this)),i!==null&&!t.errorEmitted&&!t.closeEmitted&&(t.dataEmitted=!0,this.emit("data",i)),i};function ba(e,t){if(y("onEofChunk"),!t.ended){if(t.decoder){let n=t.decoder.end();n&&n.length&&(t.buffer.push(n),t.length+=t.objectMode?1:n.length)}t.ended=!0,t.sync?$e(e):(t.needReadable=!1,t.emittedReadable=!0,or(e))}}function $e(e){let t=e._readableState;y("emitReadable",t.needReadable,t.emittedReadable),t.needReadable=!1,t.emittedReadable||(y("emitReadable",t.flowing),t.emittedReadable=!0,W.nextTick(or,e))}function or(e){let t=e._readableState;y("emitReadable_",t.destroyed,t.length,t.ended),!t.destroyed&&!t.errored&&(t.length||t.ended)&&(e.emit("readable"),t.emittedReadable=!1),t.needReadable=!t.flowing&&!t.ended&&t.length<=t.highWaterMark,ar(e)}function je(e,t){!t.readingMore&&t.constructed&&(t.readingMore=!0,W.nextTick(_a,e,t))}function _a(e,t){for(;!t.reading&&!t.ended&&(t.length1&&r.pipes.includes(e)&&(y("false write response, pause",r.awaitDrainWriters.size),r.awaitDrainWriters.add(e)),n.pause()),f||(f=pa(n,e),e.on("drain",f))}n.on("data",b);function b(_){y("ondata");let p=e.write(_);y("dest.write",p),p===!1&&s()}function d(_){if(y("onerror",_),L(),e.removeListener("error",d),e.listenerCount("error")===0){let p=e._writableState||e._readableState;p&&!p.errorEmitted?fe(e,_):e.emit("error",_)}}Zl(e,"error",d);function h(){e.removeListener("finish",D),L()}e.once("close",h);function D(){y("onfinish"),e.removeListener("close",h),L()}e.once("finish",D);function L(){y("unpipe"),n.unpipe(e)}return e.emit("pipe",n),e.writableNeedDrain===!0?r.flowing&&s():r.flowing||(y("pipe resume"),n.resume()),e};function pa(e,t){return function(){let r=e._readableState;r.awaitDrainWriters===t?(y("pipeOnDrain",1),r.awaitDrainWriters=null):r.multiAwaitDrain&&(y("pipeOnDrain",r.awaitDrainWriters.size),r.awaitDrainWriters.delete(t)),(!r.awaitDrainWriters||r.awaitDrainWriters.size===0)&&e.listenerCount("data")&&e.resume()}}w.prototype.unpipe=function(e){let t=this._readableState,n={hasUnpiped:!1};if(t.pipes.length===0)return this;if(!e){let i=t.pipes;t.pipes=[],this.pause();for(let o=0;o0,r.flowing!==!1&&this.resume()):e==="readable"&&!r.endEmitted&&!r.readableListening&&(r.readableListening=r.needReadable=!0,r.flowing=!1,r.emittedReadable=!1,y("on readable",r.length,r.reading),r.length?$e(this):r.reading||W.nextTick(wa,this)),n};w.prototype.addListener=w.prototype.on;w.prototype.removeListener=function(e,t){let n=z.prototype.removeListener.call(this,e,t);return e==="readable"&&W.nextTick(lr,this),n};w.prototype.off=w.prototype.removeListener;w.prototype.removeAllListeners=function(e){let t=z.prototype.removeAllListeners.apply(this,arguments);return(e==="readable"||e===void 0)&&W.nextTick(lr,this),t};function lr(e){let t=e._readableState;t.readableListening=e.listenerCount("readable")>0,t.resumeScheduled&&t[ee]===!1?t.flowing=!0:e.listenerCount("data")>0?e.resume():t.readableListening||(t.flowing=null)}function wa(e){y("readable nexttick read 0"),e.read(0)}w.prototype.resume=function(){let e=this._readableState;return e.flowing||(y("resume"),e.flowing=!e.readableListening,ya(this,e)),e[ee]=!1,this};function ya(e,t){t.resumeScheduled||(t.resumeScheduled=!0,W.nextTick(ga,e,t))}function ga(e,t){y("resume",t.reading),t.reading||e.read(0),t.resumeScheduled=!1,e.emit("resume"),ar(e),t.flowing&&!t.reading&&e.read(0)}w.prototype.pause=function(){return y("call pause flowing=%j",this._readableState.flowing),this._readableState.flowing!==!1&&(y("pause"),this._readableState.flowing=!1,this.emit("pause")),this._readableState[ee]=!0,this};function ar(e){let t=e._readableState;for(y("flow",t.flowing);t.flowing&&e.read()!==null;);}w.prototype.wrap=function(e){let t=!1;e.on("data",r=>{!this.push(r)&&e.pause&&(t=!0,e.pause())}),e.on("end",()=>{this.push(null)}),e.on("error",r=>{fe(this,r)}),e.on("close",()=>{this.destroy()}),e.on("destroy",()=>{this.destroy()}),this._read=()=>{t&&e.resume&&(t=!1,e.resume())};let n=Yl(e);for(let r=1;r{i=l?Zn(i,l):null,n(),n=bt});try{for(;;){let l=e.destroyed?null:e.read();if(l!==null)yield l;else{if(i)throw i;if(i===null)return;await new Kl(r)}}}catch(l){throw i=Zn(i,l),i}finally{(i||t?.destroyOnReturn!==!1)&&(i===void 0||e._readableState.autoDestroy)?ue.destroyer(e,null):(e.off("readable",r),o())}}tr(w.prototype,{readable:{__proto__:null,get(){let e=this._readableState;return!!e&&e.readable!==!1&&!e.destroyed&&!e.errorEmitted&&!e.endEmitted},set(e){this._readableState&&(this._readableState.readable=!!e)}},readableDidRead:{__proto__:null,enumerable:!1,get:function(){return this._readableState.dataEmitted}},readableAborted:{__proto__:null,enumerable:!1,get:function(){return!!(this._readableState.readable!==!1&&(this._readableState.destroyed||this._readableState.errored)&&!this._readableState.endEmitted)}},readableHighWaterMark:{__proto__:null,enumerable:!1,get:function(){return this._readableState.highWaterMark}},readableBuffer:{__proto__:null,enumerable:!1,get:function(){return this._readableState&&this._readableState.buffer}},readableFlowing:{__proto__:null,enumerable:!1,get:function(){return this._readableState.flowing},set:function(e){this._readableState&&(this._readableState.flowing=e)}},readableLength:{__proto__:null,enumerable:!1,get(){return this._readableState.length}},readableObjectMode:{__proto__:null,enumerable:!1,get(){return this._readableState?this._readableState.objectMode:!1}},readableEncoding:{__proto__:null,enumerable:!1,get(){return this._readableState?this._readableState.encoding:null}},errored:{__proto__:null,enumerable:!1,get(){return this._readableState?this._readableState.errored:null}},closed:{__proto__:null,get(){return this._readableState?this._readableState.closed:!1}},destroyed:{__proto__:null,enumerable:!1,get(){return this._readableState?this._readableState.destroyed:!1},set(e){!this._readableState||(this._readableState.destroyed=e)}},readableEnded:{__proto__:null,enumerable:!1,get(){return this._readableState?this._readableState.endEmitted:!1}}});tr(yt.prototype,{pipesCount:{__proto__:null,get(){return this.pipes.length}},paused:{__proto__:null,get(){return this[ee]!==!1},set(e){this[ee]=!!e}}});w._fromList=ur;function ur(e,t){if(t.length===0)return null;let n;return t.objectMode?n=t.buffer.shift():!e||e>=t.length?(t.decoder?n=t.buffer.join(""):t.buffer.length===1?n=t.buffer.first():n=t.buffer.concat(t.length),t.buffer.clear()):n=t.buffer.consume(e,t.decoder),n}function pt(e){let t=e._readableState;y("endReadable",t.endEmitted),t.endEmitted||(t.ended=!0,W.nextTick(Ea,t,e))}function Ea(e,t){if(y("endReadableNT",e.endEmitted,e.length),!e.errored&&!e.closeEmitted&&!e.endEmitted&&e.length===0){if(e.endEmitted=!0,t.emit("end"),t.writable&&t.allowHalfOpen===!1)W.nextTick(Ra,t);else if(e.autoDestroy){let n=t._writableState;(!n||n.autoDestroy&&(n.finished||n.writable===!1))&&t.destroy()}}}function Ra(e){e.writable&&!e.writableEnded&&!e.destroyed&&e.end()}w.from=function(e,t){return da(w,e,t)};var wt;function sr(){return wt===void 0&&(wt={}),wt}w.fromWeb=function(e,t){return sr().newStreamReadableFromReadableStream(e,t)};w.toWeb=function(e,t){return sr().newReadableStreamFromStreamReadable(e,t)};w.wrap=function(e,t){var n,r;return new w({objectMode:(n=(r=e.readableObjectMode)!==null&&r!==void 0?r:e.objectMode)!==null&&n!==void 0?n:!0,...t,destroy(i,o){ue.destroyer(e,i),o(i)}}).wrap(e)}});var Tt=g((fu,Ar)=>{var te=__process$,{ArrayPrototypeSlice:br,Error:Aa,FunctionPrototypeSymbolHasInstance:_r,ObjectDefineProperty:pr,ObjectDefineProperties:ma,ObjectSetPrototypeOf:wr,StringPrototypeToLowerCase:Ta,Symbol:Ia,SymbolHasInstance:Ma}=m();Ar.exports=S;S.WritableState=Se;var{EventEmitter:Na}=__events$,ye=Le().Stream,{Buffer:ve}=__buffer$,Be=Z(),{addAbortSignal:Da}=ke(),{getHighWaterMark:Oa,getDefaultHighWaterMark:qa}=Ce(),{ERR_INVALID_ARG_TYPE:xa,ERR_METHOD_NOT_IMPLEMENTED:La,ERR_MULTIPLE_CALLBACK:yr,ERR_STREAM_CANNOT_PIPE:Pa,ERR_STREAM_DESTROYED:ge,ERR_STREAM_ALREADY_FINISHED:ka,ERR_STREAM_NULL_VALUES:Wa,ERR_STREAM_WRITE_AFTER_END:Ca,ERR_UNKNOWN_ENCODING:gr}=O().codes,{errorOrDestroy:se}=Be;wr(S.prototype,ye.prototype);wr(S,ye);function Et(){}var de=Ia("kOnFinished");function Se(e,t,n){typeof n!="boolean"&&(n=t instanceof v()),this.objectMode=!!(e&&e.objectMode),n&&(this.objectMode=this.objectMode||!!(e&&e.writableObjectMode)),this.highWaterMark=e?Oa(this,e,"writableHighWaterMark",n):qa(!1),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;let r=!!(e&&e.decodeStrings===!1);this.decodeStrings=!r,this.defaultEncoding=e&&e.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=$a.bind(void 0,t),this.writecb=null,this.writelen=0,this.afterWriteTickInfo=null,Ue(this),this.pendingcb=0,this.constructed=!0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!e||e.emitClose!==!1,this.autoDestroy=!e||e.autoDestroy!==!1,this.errored=null,this.closed=!1,this.closeEmitted=!1,this[de]=[]}function Ue(e){e.buffered=[],e.bufferedIndex=0,e.allBuffers=!0,e.allNoop=!0}Se.prototype.getBuffer=function(){return br(this.buffered,this.bufferedIndex)};pr(Se.prototype,"bufferedRequestCount",{__proto__:null,get(){return this.buffered.length-this.bufferedIndex}});function S(e){let t=this instanceof v();if(!t&&!_r(S,this))return new S(e);this._writableState=new Se(e,this,t),e&&(typeof e.write=="function"&&(this._write=e.write),typeof e.writev=="function"&&(this._writev=e.writev),typeof e.destroy=="function"&&(this._destroy=e.destroy),typeof e.final=="function"&&(this._final=e.final),typeof e.construct=="function"&&(this._construct=e.construct),e.signal&&Da(e.signal,this)),ye.call(this,e),Be.construct(this,()=>{let n=this._writableState;n.writing||At(this,n),mt(this,n)})}pr(S,Ma,{__proto__:null,value:function(e){return _r(this,e)?!0:this!==S?!1:e&&e._writableState instanceof Se}});S.prototype.pipe=function(){se(this,new Pa)};function Sr(e,t,n,r){let i=e._writableState;if(typeof n=="function")r=n,n=i.defaultEncoding;else{if(!n)n=i.defaultEncoding;else if(n!=="buffer"&&!ve.isEncoding(n))throw new gr(n);typeof r!="function"&&(r=Et)}if(t===null)throw new Wa;if(!i.objectMode)if(typeof t=="string")i.decodeStrings!==!1&&(t=ve.from(t,n),n="buffer");else if(t instanceof ve)n="buffer";else if(ye._isUint8Array(t))t=ye._uint8ArrayToBuffer(t),n="buffer";else throw new xa("chunk",["string","Buffer","Uint8Array"],t);let o;return i.ending?o=new Ca:i.destroyed&&(o=new ge("write")),o?(te.nextTick(r,o),se(e,o,!0),o):(i.pendingcb++,ja(e,i,t,n,r))}S.prototype.write=function(e,t,n){return Sr(this,e,t,n)===!0};S.prototype.cork=function(){this._writableState.corked++};S.prototype.uncork=function(){let e=this._writableState;e.corked&&(e.corked--,e.writing||At(this,e))};S.prototype.setDefaultEncoding=function(t){if(typeof t=="string"&&(t=Ta(t)),!ve.isEncoding(t))throw new gr(t);return this._writableState.defaultEncoding=t,this};function ja(e,t,n,r,i){let o=t.objectMode?1:n.length;t.length+=o;let l=t.lengthn.bufferedIndex&&At(e,n),r?n.afterWriteTickInfo!==null&&n.afterWriteTickInfo.cb===i?n.afterWriteTickInfo.count++:(n.afterWriteTickInfo={count:1,cb:i,stream:e,state:n},te.nextTick(va,n.afterWriteTickInfo)):Er(e,n,1,i))}function va({stream:e,state:t,count:n,cb:r}){return t.afterWriteTickInfo=null,Er(e,t,n,r)}function Er(e,t,n,r){for(!t.ending&&!e.destroyed&&t.length===0&&t.needDrain&&(t.needDrain=!1,e.emit("drain"));n-- >0;)t.pendingcb--,r();t.destroyed&&Rt(t),mt(e,t)}function Rt(e){if(e.writing)return;for(let i=e.bufferedIndex;i1&&e._writev){t.pendingcb-=o-1;let u=t.allNoop?Et:a=>{for(let c=l;c256?(n.splice(0,l),t.bufferedIndex=0):t.bufferedIndex=l}t.bufferProcessing=!1}S.prototype._write=function(e,t,n){if(this._writev)this._writev([{chunk:e,encoding:t}],n);else throw new La("_write()")};S.prototype._writev=null;S.prototype.end=function(e,t,n){let r=this._writableState;typeof e=="function"?(n=e,e=null,t=null):typeof t=="function"&&(n=t,t=null);let i;if(e!=null){let o=Sr(this,e,t);o instanceof Aa&&(i=o)}return r.corked&&(r.corked=1,this.uncork()),i||(!r.errored&&!r.ending?(r.ending=!0,mt(this,r,!0),r.ended=!0):r.finished?i=new ka("end"):r.destroyed&&(i=new ge("end"))),typeof n=="function"&&(i||r.finished?te.nextTick(n,i):r[de].push(n)),this};function Fe(e){return e.ending&&!e.destroyed&&e.constructed&&e.length===0&&!e.errored&&e.buffered.length===0&&!e.finished&&!e.writing&&!e.errorEmitted&&!e.closeEmitted}function Fa(e,t){let n=!1;function r(i){if(n){se(e,i??yr());return}if(n=!0,t.pendingcb--,i){let o=t[de].splice(0);for(let l=0;l{Fe(i)?St(r,i):i.pendingcb--},e,t)):Fe(t)&&(t.pendingcb++,St(e,t))))}function St(e,t){t.pendingcb--,t.finished=!0;let n=t[de].splice(0);for(let r=0;r{var It=__process$,Ga=__buffer$,{isReadable:Ha,isWritable:Va,isIterable:mr,isNodeStream:Ya,isReadableNodeStream:Tr,isWritableNodeStream:Ir,isDuplexNodeStream:Ka}=V(),Mr=Y(),{AbortError:Lr,codes:{ERR_INVALID_ARG_TYPE:za,ERR_INVALID_RETURN_VALUE:Nr}}=O(),{destroyer:ce}=Z(),Xa=v(),Ja=we(),{createDeferredPromise:Dr}=j(),Or=ct(),qr=globalThis.Blob||Ga.Blob,Qa=typeof qr<"u"?function(t){return t instanceof qr}:function(t){return!1},Za=globalThis.AbortController,{FunctionPrototypeCall:xr}=m(),ne=class extends Xa{constructor(t){super(t),t?.readable===!1&&(this._readableState.readable=!1,this._readableState.ended=!0,this._readableState.endEmitted=!0),t?.writable===!1&&(this._writableState.writable=!1,this._writableState.ending=!0,this._writableState.ended=!0,this._writableState.finished=!0)}};Pr.exports=function e(t,n){if(Ka(t))return t;if(Tr(t))return Ge({readable:t});if(Ir(t))return Ge({writable:t});if(Ya(t))return Ge({writable:!1,readable:!1});if(typeof t=="function"){let{value:i,write:o,final:l,destroy:u}=ef(t);if(mr(i))return Or(ne,i,{objectMode:!0,write:o,final:l,destroy:u});let f=i?.then;if(typeof f=="function"){let a,c=xr(f,i,s=>{if(s!=null)throw new Nr("nully","body",s)},s=>{ce(a,s)});return a=new ne({objectMode:!0,readable:!1,write:o,final(s){l(async()=>{try{await c,It.nextTick(s,null)}catch(b){It.nextTick(s,b)}})},destroy:u})}throw new Nr("Iterable, AsyncIterable or AsyncFunction",n,i)}if(Qa(t))return e(t.arrayBuffer());if(mr(t))return Or(ne,t,{objectMode:!0,writable:!1});if(typeof t?.writable=="object"||typeof t?.readable=="object"){let i=t!=null&&t.readable?Tr(t?.readable)?t?.readable:e(t.readable):void 0,o=t!=null&&t.writable?Ir(t?.writable)?t?.writable:e(t.writable):void 0;return Ge({readable:i,writable:o})}let r=t?.then;if(typeof r=="function"){let i;return xr(r,t,o=>{o!=null&&i.push(o),i.push(null)},o=>{ce(i,o)}),i=new ne({objectMode:!0,writable:!1,read(){}})}throw new za(n,["Blob","ReadableStream","WritableStream","Stream","Iterable","AsyncIterable","Function","{ readable, writable } pair","Promise"],t)};function ef(e){let{promise:t,resolve:n}=Dr(),r=new Za,i=r.signal;return{value:e(async function*(){for(;;){let l=t;t=null;let{chunk:u,done:f,cb:a}=await l;if(It.nextTick(a),f)return;if(i.aborted)throw new Lr(void 0,{cause:i.reason});({promise:t,resolve:n}=Dr()),yield u}}(),{signal:i}),write(l,u,f){let a=n;n=null,a({chunk:l,done:!1,cb:f})},final(l){let u=n;n=null,u({done:!0,cb:l})},destroy(l,u){r.abort(),u(l)}}}function Ge(e){let t=e.readable&&typeof e.readable.read!="function"?Ja.wrap(e.readable):e.readable,n=e.writable,r=!!Ha(t),i=!!Va(n),o,l,u,f,a;function c(s){let b=f;f=null,b?b(s):s?a.destroy(s):!r&&!i&&a.destroy()}return a=new ne({readableObjectMode:!!(t!=null&&t.readableObjectMode),writableObjectMode:!!(n!=null&&n.writableObjectMode),readable:r,writable:i}),i&&(Mr(n,s=>{i=!1,s&&ce(t,s),c(s)}),a._write=function(s,b,d){n.write(s,b)?d():o=d},a._final=function(s){n.end(),l=s},n.on("drain",function(){if(o){let s=o;o=null,s()}}),n.on("finish",function(){if(l){let s=l;l=null,s()}})),r&&(Mr(t,s=>{r=!1,s&&ce(t,s),c(s)}),t.on("readable",function(){if(u){let s=u;u=null,s()}}),t.on("end",function(){a.push(null)}),a._read=function(){for(;;){let s=t.read();if(s===null){u=a._read;return}if(!a.push(s))return}}),a._destroy=function(s,b){!s&&f!==null&&(s=new Lr),u=null,o=null,l=null,f===null?b(s):(f=b,ce(n,s),ce(t,s))},a}});var v=g((su,jr)=>{"use strict";var{ObjectDefineProperties:tf,ObjectGetOwnPropertyDescriptor:B,ObjectKeys:nf,ObjectSetPrototypeOf:Wr}=m();jr.exports=C;var Dt=we(),x=Tt();Wr(C.prototype,Dt.prototype);Wr(C,Dt);{let e=nf(x.prototype);for(let t=0;t{"use strict";var{ObjectSetPrototypeOf:$r,Symbol:rf}=m();vr.exports=G;var{ERR_METHOD_NOT_IMPLEMENTED:of}=O().codes,qt=v(),{getHighWaterMark:lf}=Ce();$r(G.prototype,qt.prototype);$r(G,qt);var Ee=rf("kCallback");function G(e){if(!(this instanceof G))return new G(e);let t=e?lf(this,e,"readableHighWaterMark",!0):null;t===0&&(e={...e,highWaterMark:null,readableHighWaterMark:t,writableHighWaterMark:e.writableHighWaterMark||0}),qt.call(this,e),this._readableState.sync=!1,this[Ee]=null,e&&(typeof e.transform=="function"&&(this._transform=e.transform),typeof e.flush=="function"&&(this._flush=e.flush)),this.on("prefinish",af)}function Ot(e){typeof this._flush=="function"&&!this.destroyed?this._flush((t,n)=>{if(t){e?e(t):this.destroy(t);return}n!=null&&this.push(n),this.push(null),e&&e()}):(this.push(null),e&&e())}function af(){this._final!==Ot&&Ot.call(this)}G.prototype._final=Ot;G.prototype._transform=function(e,t,n){throw new of("_transform()")};G.prototype._write=function(e,t,n){let r=this._readableState,i=this._writableState,o=r.length;this._transform(e,t,(l,u)=>{if(l){n(l);return}u!=null&&this.push(u),i.ended||o===r.length||r.length{"use strict";var{ObjectSetPrototypeOf:Fr}=m();Ur.exports=he;var Lt=xt();Fr(he.prototype,Lt.prototype);Fr(he,Lt);function he(e){if(!(this instanceof he))return new he(e);Lt.call(this,e)}he.prototype._transform=function(e,t,n){n(null,e)}});var Ye=g((hu,zr)=>{var He=__process$,{ArrayIsArray:ff,Promise:uf,SymbolAsyncIterator:sf}=m(),Ve=Y(),{once:df}=j(),cf=Z(),Br=v(),{aggregateTwoErrors:hf,codes:{ERR_INVALID_ARG_TYPE:Yr,ERR_INVALID_RETURN_VALUE:kt,ERR_MISSING_ARGS:bf,ERR_STREAM_DESTROYED:_f,ERR_STREAM_PREMATURE_CLOSE:pf},AbortError:wf}=O(),{validateFunction:yf,validateAbortSignal:gf}=_e(),{isIterable:be,isReadable:Wt,isReadableNodeStream:$t,isNodeStream:Gr}=V(),Sf=globalThis.AbortController,Ct,jt;function Hr(e,t,n){let r=!1;e.on("close",()=>{r=!0});let i=Ve(e,{readable:t,writable:n},o=>{r=!o});return{destroy:o=>{r||(r=!0,cf.destroyer(e,o||new _f("pipe")))},cleanup:i}}function Ef(e){return yf(e[e.length-1],"streams[stream.length - 1]"),e.pop()}function Rf(e){if(be(e))return e;if($t(e))return Af(e);throw new Yr("val",["Readable","Iterable","AsyncIterable"],e)}async function*Af(e){jt||(jt=we()),yield*jt.prototype[sf].call(e)}async function Vr(e,t,n,{end:r}){let i,o=null,l=a=>{if(a&&(i=a),o){let c=o;o=null,c()}},u=()=>new uf((a,c)=>{i?c(i):o=()=>{i?c(i):a()}});t.on("drain",l);let f=Ve(t,{readable:!1},l);try{t.writableNeedDrain&&await u();for await(let a of e)t.write(a)||await u();r&&t.end(),await u(),n()}catch(a){n(i!==a?hf(i,a):a)}finally{f(),t.off("drain",l)}}function mf(...e){return Kr(e,df(Ef(e)))}function Kr(e,t,n){if(e.length===1&&ff(e[0])&&(e=e[0]),e.length<2)throw new bf("streams");let r=new Sf,i=r.signal,o=n?.signal,l=[];gf(o,"options.signal");function u(){d(new wf)}o?.addEventListener("abort",u);let f,a,c=[],s=0;function b(_){d(_,--s===0)}function d(_,p){if(_&&(!f||f.code==="ERR_STREAM_PREMATURE_CLOSE")&&(f=_),!(!f&&!p)){for(;c.length;)c.shift()(f);o?.removeEventListener("abort",u),r.abort(),p&&(f||l.forEach(I=>I()),He.nextTick(t,f,a))}}let h;for(let _=0;_0,F=I||n?.end!==!1,re=_===e.length-1;if(Gr(p)){let P=function(U){U&&U.name!=="AbortError"&&U.code!=="ERR_STREAM_PREMATURE_CLOSE"&&b(U)};var L=P;if(F){let{destroy:U,cleanup:ze}=Hr(p,I,M);c.push(U),Wt(p)&&re&&l.push(ze)}p.on("error",P),Wt(p)&&re&&l.push(()=>{p.removeListener("error",P)})}if(_===0)if(typeof p=="function"){if(h=p({signal:i}),!be(h))throw new kt("Iterable, AsyncIterable or Stream","source",h)}else be(p)||$t(p)?h=p:h=Br.from(p);else if(typeof p=="function")if(h=Rf(h),h=p(h,{signal:i}),I){if(!be(h,!0))throw new kt("AsyncIterable",`transform[${_-1}]`,h)}else{var D;Ct||(Ct=Pt());let P=new Ct({objectMode:!0}),U=(D=h)===null||D===void 0?void 0:D.then;if(typeof U=="function")s++,U.call(h,ie=>{a=ie,ie!=null&&P.write(ie),F&&P.end(),He.nextTick(b)},ie=>{P.destroy(ie),He.nextTick(b,ie)});else if(be(h,!0))s++,Vr(h,P,b,{end:F});else throw new kt("AsyncIterable or Promise","destination",h);h=P;let{destroy:ze,cleanup:_i}=Hr(h,!1,!0);c.push(ze),re&&l.push(_i)}else if(Gr(p)){if($t(h)){s+=2;let P=Tf(h,p,b,{end:F});Wt(p)&&re&&l.push(P)}else if(be(h))s++,Vr(h,p,b,{end:F});else throw new Yr("val",["Readable","Iterable","AsyncIterable"],h);h=p}else h=Br.from(p)}return(i!=null&&i.aborted||o!=null&&o.aborted)&&He.nextTick(u),h}function Tf(e,t,n,{end:r}){let i=!1;return t.on("close",()=>{i||n(new pf)}),e.pipe(t,{end:r}),r?e.once("end",()=>{i=!0,t.end()}):n(),Ve(e,{readable:!0,writable:!1},o=>{let l=e._readableState;o&&o.code==="ERR_STREAM_PREMATURE_CLOSE"&&l&&l.ended&&!l.errored&&!l.errorEmitted?e.once("end",n).once("error",n):n(o)}),Ve(t,{readable:!1,writable:!0},n)}zr.exports={pipelineImpl:Kr,pipeline:mf}});var ei=g((bu,Zr)=>{"use strict";var{pipeline:If}=Ye(),Ke=v(),{destroyer:Mf}=Z(),{isNodeStream:Nf,isReadable:Xr,isWritable:Jr}=V(),{AbortError:Df,codes:{ERR_INVALID_ARG_VALUE:Qr,ERR_MISSING_ARGS:Of}}=O();Zr.exports=function(...t){if(t.length===0)throw new Of("streams");if(t.length===1)return Ke.from(t[0]);let n=[...t];if(typeof t[0]=="function"&&(t[0]=Ke.from(t[0])),typeof t[t.length-1]=="function"){let d=t.length-1;t[d]=Ke.from(t[d])}for(let d=0;d0&&!Jr(t[d]))throw new Qr(`streams[${d}]`,n[d],"must be writable")}let r,i,o,l,u;function f(d){let h=l;l=null,h?h(d):d?u.destroy(d):!b&&!s&&u.destroy()}let a=t[0],c=If(t,f),s=!!Jr(a),b=!!Xr(c);return u=new Ke({writableObjectMode:!!(a!=null&&a.writableObjectMode),readableObjectMode:!!(c!=null&&c.writableObjectMode),writable:s,readable:b}),s&&(u._write=function(d,h,D){a.write(d,h)?D():r=D},u._final=function(d){a.end(),i=d},a.on("drain",function(){if(r){let d=r;r=null,d()}}),c.on("finish",function(){if(i){let d=i;i=null,d()}})),b&&(c.on("readable",function(){if(o){let d=o;o=null,d()}}),c.on("end",function(){u.push(null)}),u._read=function(){for(;;){let d=c.read();if(d===null){o=u._read;return}if(!u.push(d))return}}),u._destroy=function(d,h){!d&&l!==null&&(d=new Df),o=null,r=null,i=null,l===null?h(d):(l=h,Mf(c,d))},u}});var vt=g((_u,ti)=>{"use strict";var{ArrayPrototypePop:qf,Promise:xf}=m(),{isIterable:Lf,isNodeStream:Pf}=V(),{pipelineImpl:kf}=Ye(),{finished:Wf}=Y();function Cf(...e){return new xf((t,n)=>{let r,i,o=e[e.length-1];if(o&&typeof o=="object"&&!Pf(o)&&!Lf(o)){let l=qf(e);r=l.signal,i=l.end}kf(e,(l,u)=>{l?n(l):t(u)},{signal:r,end:i})})}ti.exports={finished:Wf,pipeline:Cf}});var di=g((pu,si)=>{var{Buffer:jf}=__buffer$,{ObjectDefineProperty:H,ObjectKeys:ii,ReflectApply:oi}=m(),{promisify:{custom:li}}=j(),{streamReturningOperators:ni,promiseReturningOperators:ri}=xn(),{codes:{ERR_ILLEGAL_CONSTRUCTOR:ai}}=O(),$f=ei(),{pipeline:fi}=Ye(),{destroyer:vf}=Z(),ui=Y(),Ft=vt(),Ut=V(),R=si.exports=Le().Stream;R.isDisturbed=Ut.isDisturbed;R.isErrored=Ut.isErrored;R.isReadable=Ut.isReadable;R.Readable=we();for(let e of ii(ni)){let n=function(...r){if(new.target)throw ai();return R.Readable.from(oi(t,this,r))};Uf=n;let t=ni[e];H(n,"name",{__proto__:null,value:t.name}),H(n,"length",{__proto__:null,value:t.length}),H(R.Readable.prototype,e,{__proto__:null,value:n,enumerable:!1,configurable:!0,writable:!0})}var Uf;for(let e of ii(ri)){let n=function(...i){if(new.target)throw ai();return oi(t,this,i)};Uf=n;let t=ri[e];H(n,"name",{__proto__:null,value:t.name}),H(n,"length",{__proto__:null,value:t.length}),H(R.Readable.prototype,e,{__proto__:null,value:n,enumerable:!1,configurable:!0,writable:!0})}var Uf;R.Writable=Tt();R.Duplex=v();R.Transform=xt();R.PassThrough=Pt();R.pipeline=fi;var{addAbortSignal:Ff}=ke();R.addAbortSignal=Ff;R.finished=ui;R.destroy=vf;R.compose=$f;H(R,"promises",{__proto__:null,configurable:!0,enumerable:!0,get(){return Ft}});H(fi,li,{__proto__:null,enumerable:!0,get(){return Ft.pipeline}});H(ui,li,{__proto__:null,enumerable:!0,get(){return Ft.finished}});R.Stream=R;R._isUint8Array=function(t){return t instanceof Uint8Array};R._uint8ArrayToBuffer=function(t){return jf.from(t.buffer,t.byteOffset,t.byteLength)}});var ci=g((wu,A)=>{"use strict";var T=di(),Bf=vt(),Gf=T.Readable.destroy;A.exports=T.Readable;A.exports._uint8ArrayToBuffer=T._uint8ArrayToBuffer;A.exports._isUint8Array=T._isUint8Array;A.exports.isDisturbed=T.isDisturbed;A.exports.isErrored=T.isErrored;A.exports.isReadable=T.isReadable;A.exports.Readable=T.Readable;A.exports.Writable=T.Writable;A.exports.Duplex=T.Duplex;A.exports.Transform=T.Transform;A.exports.PassThrough=T.PassThrough;A.exports.addAbortSignal=T.addAbortSignal;A.exports.finished=T.finished;A.exports.destroy=T.destroy;A.exports.destroy=Gf;A.exports.pipeline=T.pipeline;A.exports.compose=T.compose;Object.defineProperty(T,"promises",{configurable:!0,enumerable:!0,get(){return Bf}});A.exports.Stream=T.Stream;A.exports.default=A.exports});var bi=Ri(ci()),{_uint8ArrayToBuffer:yu,_isUint8Array:gu,isDisturbed:Su,isErrored:Eu,isReadable:Ru,Readable:Au,Writable:mu,Duplex:Tu,Transform:Iu,PassThrough:Mu,addAbortSignal:Nu,finished:Du,destroy:Ou,pipeline:qu,compose:xu,Stream:Lu}=bi,{default:hi,...Hf}=bi,Pu=hi!==void 0?hi:Hf;export{Tu as Duplex,Mu as PassThrough,Au as Readable,Lu as Stream,Iu as Transform,mu as Writable,gu as _isUint8Array,yu as _uint8ArrayToBuffer,Nu as addAbortSignal,xu as compose,Pu as default,Ou as destroy,Du as finished,Su as isDisturbed,Eu as isErrored,Ru as isReadable,qu as pipeline}; +/* End esm.sh bundle */ + +// The following code implements Readable.fromWeb(), Writable.fromWeb(), and +// Duplex.fromWeb(). These functions are not properly implemented in the +// readable-stream module yet. This can be removed once the following upstream +// issue is resolved: https://github.com/nodejs/readable-stream/issues/482 + +import { + AbortError, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_STREAM_PREMATURE_CLOSE, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { destroy } from "internal:deno_node/polyfills/internal/streams/destroy.mjs"; +import finished from "internal:deno_node/polyfills/internal/streams/end-of-stream.mjs"; +import { + isDestroyed, + isReadable, + isReadableEnded, + isWritable, + isWritableEnded, +} from "internal:deno_node/polyfills/internal/streams/utils.mjs"; +import { createDeferredPromise, kEmptyObject } from "internal:deno_node/polyfills/internal/util.mjs"; +import { validateBoolean, validateObject } from "internal:deno_node/polyfills/internal/validators.mjs"; + +const process = __process$; +const { Buffer } = __buffer$; +const Readable = Au; +const Writable = mu; +const Duplex = Tu; + +function isReadableStream(object) { + return object instanceof ReadableStream; +} + +function isWritableStream(object) { + return object instanceof WritableStream; +} + +Readable.fromWeb = function ( + readableStream, + options = kEmptyObject, +) { + if (!isReadableStream(readableStream)) { + throw new ERR_INVALID_ARG_TYPE( + "readableStream", + "ReadableStream", + readableStream, + ); + } + + validateObject(options, "options"); + const { + highWaterMark, + encoding, + objectMode = false, + signal, + } = options; + + if (encoding !== undefined && !Buffer.isEncoding(encoding)) { + throw new ERR_INVALID_ARG_VALUE(encoding, "options.encoding"); + } + validateBoolean(objectMode, "options.objectMode"); + + const reader = readableStream.getReader(); + let closed = false; + + const readable = new Readable({ + objectMode, + highWaterMark, + encoding, + signal, + + read() { + reader.read().then( + (chunk) => { + if (chunk.done) { + readable.push(null); + } else { + readable.push(chunk.value); + } + }, + (error) => destroy.call(readable, error), + ); + }, + + destroy(error, callback) { + function done() { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => { + throw error; + }); + } + } + + if (!closed) { + reader.cancel(error).then(done, done); + return; + } + + done(); + }, + }); + + reader.closed.then( + () => { + closed = true; + if (!isReadableEnded(readable)) { + readable.push(null); + } + }, + (error) => { + closed = true; + destroy.call(readable, error); + }, + ); + + return readable; +}; + +Writable.fromWeb = function ( + writableStream, + options = kEmptyObject, +) { + if (!isWritableStream(writableStream)) { + throw new ERR_INVALID_ARG_TYPE( + "writableStream", + "WritableStream", + writableStream, + ); + } + + validateObject(options, "options"); + const { + highWaterMark, + decodeStrings = true, + objectMode = false, + signal, + } = options; + + validateBoolean(objectMode, "options.objectMode"); + validateBoolean(decodeStrings, "options.decodeStrings"); + + const writer = writableStream.getWriter(); + let closed = false; + + const writable = new Writable({ + highWaterMark, + objectMode, + decodeStrings, + signal, + + writev(chunks, callback) { + function done(error) { + error = error.filter((e) => e); + try { + callback(error.length === 0 ? undefined : error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy.call(writable, error)); + } + } + + writer.ready.then( + () => + Promise.all( + chunks.map((data) => writer.write(data.chunk)), + ).then(done, done), + done, + ); + }, + + write(chunk, encoding, callback) { + if (typeof chunk === "string" && decodeStrings && !objectMode) { + chunk = Buffer.from(chunk, encoding); + chunk = new Uint8Array( + chunk.buffer, + chunk.byteOffset, + chunk.byteLength, + ); + } + + function done(error) { + try { + callback(error); + } catch (error) { + destroy(this, duplex, error); + } + } + + writer.ready.then( + () => writer.write(chunk).then(done, done), + done, + ); + }, + + destroy(error, callback) { + function done() { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => { + throw error; + }); + } + } + + if (!closed) { + if (error != null) { + writer.abort(error).then(done, done); + } else { + writer.close().then(done, done); + } + return; + } + + done(); + }, + + final(callback) { + function done(error) { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy.call(writable, error)); + } + } + + if (!closed) { + writer.close().then(done, done); + } + }, + }); + + writer.closed.then( + () => { + closed = true; + if (!isWritableEnded(writable)) { + destroy.call(writable, new ERR_STREAM_PREMATURE_CLOSE()); + } + }, + (error) => { + closed = true; + destroy.call(writable, error); + }, + ); + + return writable; +}; + +Duplex.fromWeb = function (pair, options = kEmptyObject) { + validateObject(pair, "pair"); + const { + readable: readableStream, + writable: writableStream, + } = pair; + + if (!isReadableStream(readableStream)) { + throw new ERR_INVALID_ARG_TYPE( + "pair.readable", + "ReadableStream", + readableStream, + ); + } + if (!isWritableStream(writableStream)) { + throw new ERR_INVALID_ARG_TYPE( + "pair.writable", + "WritableStream", + writableStream, + ); + } + + validateObject(options, "options"); + const { + allowHalfOpen = false, + objectMode = false, + encoding, + decodeStrings = true, + highWaterMark, + signal, + } = options; + + validateBoolean(objectMode, "options.objectMode"); + if (encoding !== undefined && !Buffer.isEncoding(encoding)) { + throw new ERR_INVALID_ARG_VALUE(encoding, "options.encoding"); + } + + const writer = writableStream.getWriter(); + const reader = readableStream.getReader(); + let writableClosed = false; + let readableClosed = false; + + const duplex = new Duplex({ + allowHalfOpen, + highWaterMark, + objectMode, + encoding, + decodeStrings, + signal, + + writev(chunks, callback) { + function done(error) { + error = error.filter((e) => e); + try { + callback(error.length === 0 ? undefined : error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy(duplex, error)); + } + } + + writer.ready.then( + () => + Promise.all( + chunks.map((data) => writer.write(data.chunk)), + ).then(done, done), + done, + ); + }, + + write(chunk, encoding, callback) { + if (typeof chunk === "string" && decodeStrings && !objectMode) { + chunk = Buffer.from(chunk, encoding); + chunk = new Uint8Array( + chunk.buffer, + chunk.byteOffset, + chunk.byteLength, + ); + } + + function done(error) { + try { + callback(error); + } catch (error) { + destroy(duplex, error); + } + } + + writer.ready.then( + () => writer.write(chunk).then(done, done), + done, + ); + }, + + final(callback) { + function done(error) { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => destroy(duplex, error)); + } + } + + if (!writableClosed) { + writer.close().then(done, done); + } + }, + + read() { + reader.read().then( + (chunk) => { + if (chunk.done) { + duplex.push(null); + } else { + duplex.push(chunk.value); + } + }, + (error) => destroy(duplex, error), + ); + }, + + destroy(error, callback) { + function done() { + try { + callback(error); + } catch (error) { + // In a next tick because this is happening within + // a promise context, and if there are any errors + // thrown we don't want those to cause an unhandled + // rejection. Let's just escape the promise and + // handle it separately. + process.nextTick(() => { + throw error; + }); + } + } + + async function closeWriter() { + if (!writableClosed) { + await writer.abort(error); + } + } + + async function closeReader() { + if (!readableClosed) { + await reader.cancel(error); + } + } + + if (!writableClosed || !readableClosed) { + Promise.all([ + closeWriter(), + closeReader(), + ]).then(done, done); + return; + } + + done(); + }, + }); + + writer.closed.then( + () => { + writableClosed = true; + if (!isWritableEnded(duplex)) { + destroy(duplex, new ERR_STREAM_PREMATURE_CLOSE()); + } + }, + (error) => { + writableClosed = true; + readableClosed = true; + destroy(duplex, error); + }, + ); + + reader.closed.then( + () => { + readableClosed = true; + if (!isReadableEnded(duplex)) { + duplex.push(null); + } + }, + (error) => { + writableClosed = true; + readableClosed = true; + destroy(duplex, error); + }, + ); + + return duplex; +}; + +// readable-stream attaches these to Readable, but Node.js core does not. +// Delete them here to better match Node.js core. These can be removed once +// https://github.com/nodejs/readable-stream/issues/485 is resolved. +delete Readable.Duplex; +delete Readable.PassThrough; +delete Readable.Readable; +delete Readable.Stream; +delete Readable.Transform; +delete Readable.Writable; +delete Readable._isUint8Array; +delete Readable._uint8ArrayToBuffer; +delete Readable.addAbortSignal; +delete Readable.compose; +delete Readable.destroy; +delete Readable.finished; +delete Readable.isDisturbed; +delete Readable.isErrored; +delete Readable.isReadable; +delete Readable.pipeline; + +// The following code implements Readable.toWeb(), Writable.toWeb(), and +// Duplex.toWeb(). These functions are not properly implemented in the +// readable-stream module yet. This can be removed once the following upstream +// issue is resolved: https://github.com/nodejs/readable-stream/issues/482 +function newReadableStreamFromStreamReadable( + streamReadable, + options = kEmptyObject, +) { + // Not using the internal/streams/utils isReadableNodeStream utility + // here because it will return false if streamReadable is a Duplex + // whose readable option is false. For a Duplex that is not readable, + // we want it to pass this check but return a closed ReadableStream. + if (typeof streamReadable?._readableState !== "object") { + throw new ERR_INVALID_ARG_TYPE( + "streamReadable", + "stream.Readable", + streamReadable, + ); + } + + if (isDestroyed(streamReadable) || !isReadable(streamReadable)) { + const readable = new ReadableStream(); + readable.cancel(); + return readable; + } + + const objectMode = streamReadable.readableObjectMode; + const highWaterMark = streamReadable.readableHighWaterMark; + + const evaluateStrategyOrFallback = (strategy) => { + // If there is a strategy available, use it + if (strategy) { + return strategy; + } + + if (objectMode) { + // When running in objectMode explicitly but no strategy, we just fall + // back to CountQueuingStrategy + return new CountQueuingStrategy({ highWaterMark }); + } + + // When not running in objectMode explicitly, we just fall + // back to a minimal strategy that just specifies the highWaterMark + // and no size algorithm. Using a ByteLengthQueuingStrategy here + // is unnecessary. + return { highWaterMark }; + }; + + const strategy = evaluateStrategyOrFallback(options?.strategy); + + let controller; + + function onData(chunk) { + // Copy the Buffer to detach it from the pool. + if (Buffer.isBuffer(chunk) && !objectMode) { + chunk = new Uint8Array(chunk); + } + controller.enqueue(chunk); + if (controller.desiredSize <= 0) { + streamReadable.pause(); + } + } + + streamReadable.pause(); + + const cleanup = finished(streamReadable, (error) => { + if (error?.code === "ERR_STREAM_PREMATURE_CLOSE") { + const err = new AbortError(undefined, { cause: error }); + error = err; + } + + cleanup(); + // This is a protection against non-standard, legacy streams + // that happen to emit an error event again after finished is called. + streamReadable.on("error", () => {}); + if (error) { + return controller.error(error); + } + controller.close(); + }); + + streamReadable.on("data", onData); + + return new ReadableStream({ + start(c) { + controller = c; + }, + + pull() { + streamReadable.resume(); + }, + + cancel(reason) { + destroy(streamReadable, reason); + }, + }, strategy); +} + +function newWritableStreamFromStreamWritable(streamWritable) { + // Not using the internal/streams/utils isWritableNodeStream utility + // here because it will return false if streamWritable is a Duplex + // whose writable option is false. For a Duplex that is not writable, + // we want it to pass this check but return a closed WritableStream. + if (typeof streamWritable?._writableState !== "object") { + throw new ERR_INVALID_ARG_TYPE( + "streamWritable", + "stream.Writable", + streamWritable, + ); + } + + if (isDestroyed(streamWritable) || !isWritable(streamWritable)) { + const writable = new WritableStream(); + writable.close(); + return writable; + } + + const highWaterMark = streamWritable.writableHighWaterMark; + const strategy = streamWritable.writableObjectMode + ? new CountQueuingStrategy({ highWaterMark }) + : { highWaterMark }; + + let controller; + let backpressurePromise; + let closed; + + function onDrain() { + if (backpressurePromise !== undefined) { + backpressurePromise.resolve(); + } + } + + const cleanup = finished(streamWritable, (error) => { + if (error?.code === "ERR_STREAM_PREMATURE_CLOSE") { + const err = new AbortError(undefined, { cause: error }); + error = err; + } + + cleanup(); + // This is a protection against non-standard, legacy streams + // that happen to emit an error event again after finished is called. + streamWritable.on("error", () => {}); + if (error != null) { + if (backpressurePromise !== undefined) { + backpressurePromise.reject(error); + } + // If closed is not undefined, the error is happening + // after the WritableStream close has already started. + // We need to reject it here. + if (closed !== undefined) { + closed.reject(error); + closed = undefined; + } + controller.error(error); + controller = undefined; + return; + } + + if (closed !== undefined) { + closed.resolve(); + closed = undefined; + return; + } + controller.error(new AbortError()); + controller = undefined; + }); + + streamWritable.on("drain", onDrain); + + return new WritableStream({ + start(c) { + controller = c; + }, + + async write(chunk) { + if (streamWritable.writableNeedDrain || !streamWritable.write(chunk)) { + backpressurePromise = createDeferredPromise(); + return backpressurePromise.promise.finally(() => { + backpressurePromise = undefined; + }); + } + }, + + abort(reason) { + destroy(streamWritable, reason); + }, + + close() { + if (closed === undefined && !isWritableEnded(streamWritable)) { + closed = createDeferredPromise(); + streamWritable.end(); + return closed.promise; + } + + controller = undefined; + return Promise.resolve(); + }, + }, strategy); +} + +function newReadableWritablePairFromDuplex(duplex) { + // Not using the internal/streams/utils isWritableNodeStream and + // isReadableNodestream utilities here because they will return false + // if the duplex was created with writable or readable options set to + // false. Instead, we'll check the readable and writable state after + // and return closed WritableStream or closed ReadableStream as + // necessary. + if ( + typeof duplex?._writableState !== "object" || + typeof duplex?._readableState !== "object" + ) { + throw new ERR_INVALID_ARG_TYPE("duplex", "stream.Duplex", duplex); + } + + if (isDestroyed(duplex)) { + const writable = new WritableStream(); + const readable = new ReadableStream(); + writable.close(); + readable.cancel(); + return { readable, writable }; + } + + const writable = isWritable(duplex) + ? newWritableStreamFromStreamWritable(duplex) + : new WritableStream(); + + if (!isWritable(duplex)) { + writable.close(); + } + + const readable = isReadable(duplex) + ? newReadableStreamFromStreamReadable(duplex) + : new ReadableStream(); + + if (!isReadable(duplex)) { + readable.cancel(); + } + + return { writable, readable }; +} + +Readable.toWeb = newReadableStreamFromStreamReadable; +Writable.toWeb = newWritableStreamFromStreamWritable; +Duplex.toWeb = newReadableWritablePairFromDuplex; diff --git a/ext/node/polyfills/_tls_common.ts b/ext/node/polyfills/_tls_common.ts new file mode 100644 index 0000000000..c032f5680f --- /dev/null +++ b/ext/node/polyfills/_tls_common.ts @@ -0,0 +1,15 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file no-explicit-any + +export function createSecureContext(options: any) { + return { + ca: options?.ca, + cert: options?.cert, + key: options?.key, + }; +} + +export default { + createSecureContext, +}; diff --git a/ext/node/polyfills/_tls_wrap.ts b/ext/node/polyfills/_tls_wrap.ts new file mode 100644 index 0000000000..2cec7b129a --- /dev/null +++ b/ext/node/polyfills/_tls_wrap.ts @@ -0,0 +1,443 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file no-explicit-any + +import { + ObjectAssign, + StringPrototypeReplace, +} from "internal:deno_node/polyfills/internal/primordials.mjs"; +import assert from "internal:deno_node/polyfills/internal/assert.mjs"; +import * as net from "internal:deno_node/polyfills/net.ts"; +import { createSecureContext } from "internal:deno_node/polyfills/_tls_common.ts"; +import { kStreamBaseField } from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { connResetException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { emitWarning } from "internal:deno_node/polyfills/process.ts"; +import { debuglog } from "internal:deno_node/polyfills/internal/util/debuglog.ts"; +import { + constants as TCPConstants, + TCP, +} from "internal:deno_node/polyfills/internal_binding/tcp_wrap.ts"; +import { + constants as PipeConstants, + Pipe, +} from "internal:deno_node/polyfills/internal_binding/pipe_wrap.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { kEmptyObject } from "internal:deno_node/polyfills/internal/util.mjs"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; + +const kConnectOptions = Symbol("connect-options"); +const kIsVerified = Symbol("verified"); +const kPendingSession = Symbol("pendingSession"); +const kRes = Symbol("res"); + +let debug = debuglog("tls", (fn) => { + debug = fn; +}); + +function onConnectEnd(this: any) { + // NOTE: This logic is shared with _http_client.js + if (!this._hadError) { + const options = this[kConnectOptions]; + this._hadError = true; + const error: any = connResetException( + "Client network socket disconnected " + + "before secure TLS connection was " + + "established", + ); + error.path = options.path; + error.host = options.host; + error.port = options.port; + error.localAddress = options.localAddress; + this.destroy(error); + } +} + +export class TLSSocket extends net.Socket { + _tlsOptions: any; + _secureEstablished: boolean; + _securePending: boolean; + _newSessionPending: boolean; + _controlReleased: boolean; + secureConnecting: boolean; + _SNICallback: any; + servername: string | null; + alpnProtocol: any; + authorized: boolean; + authorizationError: any; + [kRes]: any; + [kIsVerified]: boolean; + [kPendingSession]: any; + [kConnectOptions]: any; + ssl: any; + _start: any; + constructor(socket: any, opts: any = kEmptyObject) { + const tlsOptions = { ...opts }; + + let hostname = tlsOptions?.secureContext?.servername; + hostname = opts.host; + tlsOptions.hostname = hostname; + + const _cert = tlsOptions?.secureContext?.cert; + const _key = tlsOptions?.secureContext?.key; + + let caCerts = tlsOptions?.secureContext?.ca; + if (typeof caCerts === "string") caCerts = [caCerts]; + tlsOptions.caCerts = caCerts; + + super({ + handle: _wrapHandle(tlsOptions, socket), + ...opts, + manualStart: true, // This prevents premature reading from TLS handle + }); + if (socket) { + this._parent = socket; + } + this._tlsOptions = tlsOptions; + this._secureEstablished = false; + this._securePending = false; + this._newSessionPending = false; + this._controlReleased = false; + this.secureConnecting = true; + this._SNICallback = null; + this.servername = null; + this.alpnProtocol = null; + this.authorized = false; + this.authorizationError = null; + this[kRes] = null; + this[kIsVerified] = false; + this[kPendingSession] = null; + + this.ssl = new class { + verifyError() { + return null; // Never fails, rejectUnauthorized is always true in Deno. + } + }(); + + // deno-lint-ignore no-this-alias + const tlssock = this; + + /** Wraps the given socket and adds the tls capability to the underlying + * handle */ + function _wrapHandle(tlsOptions: any, wrap: net.Socket | undefined) { + let handle: any; + + if (wrap) { + handle = wrap._handle; + } + + const options = tlsOptions; + if (!handle) { + handle = options.pipe + ? new Pipe(PipeConstants.SOCKET) + : new TCP(TCPConstants.SOCKET); + } + + // Patches `afterConnect` hook to replace TCP conn with TLS conn + const afterConnect = handle.afterConnect; + handle.afterConnect = async (req: any, status: number) => { + try { + const conn = await Deno.startTls(handle[kStreamBaseField], options); + tlssock.emit("secure"); + tlssock.removeListener("end", onConnectEnd); + handle[kStreamBaseField] = conn; + } catch { + // TODO(kt3k): Handle this + } + return afterConnect.call(handle, req, status); + }; + + (handle as any).verifyError = function () { + return null; // Never fails, rejectUnauthorized is always true in Deno. + }; + // Pretends `handle` is `tls_wrap.wrap(handle, ...)` to make some npm modules happy + // An example usage of `_parentWrap` in npm module: + // https://github.com/szmarczak/http2-wrapper/blob/51eeaf59ff9344fb192b092241bfda8506983620/source/utils/js-stream-socket.js#L6 + handle._parent = handle; + handle._parentWrap = wrap; + + return handle; + } + } + + _tlsError(err: Error) { + this.emit("_tlsError", err); + if (this._controlReleased) { + return err; + } + return null; + } + + _releaseControl() { + if (this._controlReleased) { + return false; + } + this._controlReleased = true; + this.removeListener("error", this._tlsError); + return true; + } + + getEphemeralKeyInfo() { + return {}; + } + + isSessionReused() { + return false; + } + + setSession(_session: any) { + // TODO(kt3k): implement this + } + + setServername(_servername: any) { + // TODO(kt3k): implement this + } + + getPeerCertificate(_detailed: boolean) { + // TODO(kt3k): implement this + return { + subject: "localhost", + subjectaltname: "IP Address:127.0.0.1, IP Address:::1", + }; + } +} + +function normalizeConnectArgs(listArgs: any) { + const args = net._normalizeArgs(listArgs); + const options = args[0]; + const cb = args[1]; + + // If args[0] was options, then normalize dealt with it. + // If args[0] is port, or args[0], args[1] is host, port, we need to + // find the options and merge them in, normalize's options has only + // the host/port/path args that it knows about, not the tls options. + // This means that options.host overrides a host arg. + if (listArgs[1] !== null && typeof listArgs[1] === "object") { + ObjectAssign(options, listArgs[1]); + } else if (listArgs[2] !== null && typeof listArgs[2] === "object") { + ObjectAssign(options, listArgs[2]); + } + + return cb ? [options, cb] : [options]; +} + +let ipServernameWarned = false; + +export function Server(options: any, listener: any) { + return new ServerImpl(options, listener); +} + +export class ServerImpl extends EventEmitter { + listener?: Deno.TlsListener; + #closed = false; + constructor(public options: any, listener: any) { + super(); + if (listener) { + this.on("secureConnection", listener); + } + } + + listen(port: any, callback: any): this { + const key = this.options.key?.toString(); + const cert = this.options.cert?.toString(); + // TODO(kt3k): The default host should be "localhost" + const hostname = this.options.host ?? "0.0.0.0"; + + this.listener = Deno.listenTls({ port, hostname, cert, key }); + + callback?.call(this); + this.#listen(this.listener); + return this; + } + + async #listen(listener: Deno.TlsListener) { + while (!this.#closed) { + try { + // Creates TCP handle and socket directly from Deno.TlsConn. + // This works as TLS socket. We don't use TLSSocket class for doing + // this because Deno.startTls only supports client side tcp connection. + const handle = new TCP(TCPConstants.SOCKET, await listener.accept()); + const socket = new net.Socket({ handle }); + this.emit("secureConnection", socket); + } catch (e) { + if (e instanceof Deno.errors.BadResource) { + this.#closed = true; + } + // swallow + } + } + } + + close(cb?: (err?: Error) => void): this { + if (this.listener) { + this.listener.close(); + } + cb?.(); + nextTick(() => { + this.emit("close"); + }); + return this; + } + + address() { + const addr = this.listener!.addr as Deno.NetAddr; + return { + port: addr.port, + address: addr.hostname, + }; + } +} + +Server.prototype = ServerImpl.prototype; + +export function createServer(options: any, listener: any) { + return new ServerImpl(options, listener); +} + +function onConnectSecure(this: TLSSocket) { + this.authorized = true; + this.secureConnecting = false; + debug("client emit secureConnect. authorized:", this.authorized); + this.emit("secureConnect"); + + this.removeListener("end", onConnectEnd); +} + +export function connect(...args: any[]) { + args = normalizeConnectArgs(args); + let options = args[0]; + const cb = args[1]; + const allowUnauthorized = getAllowUnauthorized(); + + options = { + rejectUnauthorized: !allowUnauthorized, + ciphers: DEFAULT_CIPHERS, + checkServerIdentity, + minDHSize: 1024, + ...options, + }; + + if (!options.keepAlive) { + options.singleUse = true; + } + + assert(typeof options.checkServerIdentity === "function"); + assert( + typeof options.minDHSize === "number", + "options.minDHSize is not a number: " + options.minDHSize, + ); + assert( + options.minDHSize > 0, + "options.minDHSize is not a positive number: " + + options.minDHSize, + ); + + const context = options.secureContext || createSecureContext(options); + + const tlssock = new TLSSocket(options.socket, { + allowHalfOpen: options.allowHalfOpen, + pipe: !!options.path, + secureContext: context, + isServer: false, + requestCert: true, + rejectUnauthorized: options.rejectUnauthorized !== false, + session: options.session, + ALPNProtocols: options.ALPNProtocols, + requestOCSP: options.requestOCSP, + enableTrace: options.enableTrace, + pskCallback: options.pskCallback, + highWaterMark: options.highWaterMark, + onread: options.onread, + signal: options.signal, + ...options, // Caveat emptor: Node does not do this. + }); + + // rejectUnauthorized property can be explicitly defined as `undefined` + // causing the assignment to default value (`true`) fail. Before assigning + // it to the tlssock connection options, explicitly check if it is false + // and update rejectUnauthorized property. The property gets used by TLSSocket + // connection handler to allow or reject connection if unauthorized + options.rejectUnauthorized = options.rejectUnauthorized !== false; + + tlssock[kConnectOptions] = options; + + if (cb) { + tlssock.once("secureConnect", cb); + } + + if (!options.socket) { + // If user provided the socket, it's their responsibility to manage its + // connectivity. If we created one internally, we connect it. + if (options.timeout) { + tlssock.setTimeout(options.timeout); + } + + tlssock.connect(options, tlssock._start); + } + + tlssock._releaseControl(); + + if (options.session) { + tlssock.setSession(options.session); + } + + if (options.servername) { + if (!ipServernameWarned && net.isIP(options.servername)) { + emitWarning( + "Setting the TLS ServerName to an IP address is not permitted by " + + "RFC 6066. This will be ignored in a future version.", + "DeprecationWarning", + "DEP0123", + ); + ipServernameWarned = true; + } + tlssock.setServername(options.servername); + } + + if (options.socket) { + tlssock._start(); + } + + tlssock.on("secure", onConnectSecure); + tlssock.prependListener("end", onConnectEnd); + + return tlssock; +} + +function getAllowUnauthorized() { + return false; +} + +// TODO(kt3k): Implement this when Deno provides APIs for getting peer +// certificates. +export function checkServerIdentity(_hostname: string, _cert: any) { +} + +function unfqdn(host: string): string { + return StringPrototypeReplace(host, /[.]$/, ""); +} + +// Order matters. Mirrors ALL_CIPHER_SUITES from rustls/src/suites.rs but +// using openssl cipher names instead. Mutable in Node but not (yet) in Deno. +export const DEFAULT_CIPHERS = [ + // TLSv1.3 suites + "AES256-GCM-SHA384", + "AES128-GCM-SHA256", + "TLS_CHACHA20_POLY1305_SHA256", + // TLSv1.2 suites + "ECDHE-ECDSA-AES256-GCM-SHA384", + "ECDHE-ECDSA-AES128-GCM-SHA256", + "ECDHE-ECDSA-CHACHA20-POLY1305", + "ECDHE-RSA-AES256-GCM-SHA384", + "ECDHE-RSA-AES128-GCM-SHA256", + "ECDHE-RSA-CHACHA20-POLY1305", +].join(":"); + +export default { + TLSSocket, + connect, + createServer, + checkServerIdentity, + DEFAULT_CIPHERS, + Server, + unfqdn, +}; diff --git a/ext/node/polyfills/_util/_util_callbackify.ts b/ext/node/polyfills/_util/_util_callbackify.ts new file mode 100644 index 0000000000..fe83a227d0 --- /dev/null +++ b/ext/node/polyfills/_util/_util_callbackify.ts @@ -0,0 +1,129 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// +// Adapted from Node.js. 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. + +// These are simplified versions of the "real" errors in Node. + +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; + +class NodeFalsyValueRejectionError extends Error { + public reason: unknown; + public code = "ERR_FALSY_VALUE_REJECTION"; + constructor(reason: unknown) { + super("Promise was rejected with falsy value"); + this.reason = reason; + } +} +class NodeInvalidArgTypeError extends TypeError { + public code = "ERR_INVALID_ARG_TYPE"; + constructor(argumentName: string) { + super(`The ${argumentName} argument must be of type function.`); + } +} + +type Callback = + | ((err: Error) => void) + | ((err: null, result: ResultT) => void); + +function callbackify( + fn: () => PromiseLike, +): (callback: Callback) => void; +function callbackify( + fn: (arg: ArgT) => PromiseLike, +): (arg: ArgT, callback: Callback) => void; +function callbackify( + fn: (arg1: Arg1T, arg2: Arg2T) => PromiseLike, +): (arg1: Arg1T, arg2: Arg2T, callback: Callback) => void; +function callbackify( + fn: (arg1: Arg1T, arg2: Arg2T, arg3: Arg3T) => PromiseLike, +): (arg1: Arg1T, arg2: Arg2T, arg3: Arg3T, callback: Callback) => void; +function callbackify( + fn: ( + arg1: Arg1T, + arg2: Arg2T, + arg3: Arg3T, + arg4: Arg4T, + ) => PromiseLike, +): ( + arg1: Arg1T, + arg2: Arg2T, + arg3: Arg3T, + arg4: Arg4T, + callback: Callback, +) => void; +function callbackify( + fn: ( + arg1: Arg1T, + arg2: Arg2T, + arg3: Arg3T, + arg4: Arg4T, + arg5: Arg5T, + ) => PromiseLike, +): ( + arg1: Arg1T, + arg2: Arg2T, + arg3: Arg3T, + arg4: Arg4T, + arg5: Arg5T, + callback: Callback, +) => void; + +function callbackify( + original: (...args: unknown[]) => PromiseLike, +): (...args: unknown[]) => void { + if (typeof original !== "function") { + throw new NodeInvalidArgTypeError('"original"'); + } + + const callbackified = function (this: unknown, ...args: unknown[]) { + const maybeCb = args.pop(); + if (typeof maybeCb !== "function") { + throw new NodeInvalidArgTypeError("last"); + } + const cb = (...args: unknown[]) => { + maybeCb.apply(this, args); + }; + original.apply(this, args).then( + (ret: unknown) => { + nextTick(cb.bind(this, null, ret)); + }, + (rej: unknown) => { + rej = rej || new NodeFalsyValueRejectionError(rej); + nextTick(cb.bind(this, rej)); + }, + ); + }; + + const descriptors = Object.getOwnPropertyDescriptors(original); + // It is possible to manipulate a functions `length` or `name` property. This + // guards against the manipulation. + if (typeof descriptors.length.value === "number") { + descriptors.length.value++; + } + if (typeof descriptors.name.value === "string") { + descriptors.name.value += "Callbackified"; + } + Object.defineProperties(callbackified, descriptors); + return callbackified; +} + +export { callbackify }; diff --git a/ext/node/polyfills/_util/asserts.ts b/ext/node/polyfills/_util/asserts.ts new file mode 100644 index 0000000000..7760c86397 --- /dev/null +++ b/ext/node/polyfills/_util/asserts.ts @@ -0,0 +1,21 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +/** Assertion error class for node compat layer's internal code. */ +export class NodeCompatAssertionError extends Error { + constructor(message: string) { + super(message); + this.name = "NodeCompatAssertionError"; + } +} + +/** Make an assertion, if not `true`, then throw. */ +export function assert(expr: unknown, msg = ""): asserts expr { + if (!expr) { + throw new NodeCompatAssertionError(msg); + } +} + +/** Use this to assert unreachable code. */ +export function unreachable(): never { + throw new NodeCompatAssertionError("unreachable"); +} diff --git a/ext/node/polyfills/_util/async.ts b/ext/node/polyfills/_util/async.ts new file mode 100644 index 0000000000..b508dbfedc --- /dev/null +++ b/ext/node/polyfills/_util/async.ts @@ -0,0 +1,55 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// This module is vendored from std/async/deferred.ts and std/async/delay.ts +// (with some modifications) + +export interface Deferred extends Promise { + readonly state: "pending" | "fulfilled" | "rejected"; + resolve(value?: T | PromiseLike): void; + // deno-lint-ignore no-explicit-any + reject(reason?: any): void; +} + +/** Creates a Promise with the `reject` and `resolve` functions */ +export function deferred(): Deferred { + let methods; + let state = "pending"; + const promise = new Promise((resolve, reject) => { + methods = { + async resolve(value: T | PromiseLike) { + await value; + state = "fulfilled"; + resolve(value); + }, + // deno-lint-ignore no-explicit-any + reject(reason?: any) { + state = "rejected"; + reject(reason); + }, + }; + }); + Object.defineProperty(promise, "state", { get: () => state }); + return Object.assign(promise, methods) as Deferred; +} + +/** Resolve a Promise after a given amount of milliseconds. */ +export function delay( + ms: number, + options: { signal?: AbortSignal } = {}, +): Promise { + const { signal } = options; + if (signal?.aborted) { + return Promise.reject(new DOMException("Delay was aborted.", "AbortError")); + } + return new Promise((resolve, reject) => { + const abort = () => { + clearTimeout(i); + reject(new DOMException("Delay was aborted.", "AbortError")); + }; + const done = () => { + signal?.removeEventListener("abort", abort); + resolve(); + }; + const i = setTimeout(done, ms); + signal?.addEventListener("abort", abort, { once: true }); + }); +} diff --git a/ext/node/polyfills/_util/os.ts b/ext/node/polyfills/_util/os.ts new file mode 100644 index 0000000000..66d18534cc --- /dev/null +++ b/ext/node/polyfills/_util/os.ts @@ -0,0 +1,22 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export type OSType = "windows" | "linux" | "darwin" | "freebsd"; + +export const osType: OSType = (() => { + // deno-lint-ignore no-explicit-any + const { Deno } = globalThis as any; + if (typeof Deno?.build?.os === "string") { + return Deno.build.os; + } + + // deno-lint-ignore no-explicit-any + const { navigator } = globalThis as any; + if (navigator?.appVersion?.includes?.("Win")) { + return "windows"; + } + + return "linux"; +})(); + +export const isWindows = osType === "windows"; +export const isLinux = osType === "linux"; diff --git a/ext/node/polyfills/_util/std_asserts.ts b/ext/node/polyfills/_util/std_asserts.ts new file mode 100644 index 0000000000..8c4c80078d --- /dev/null +++ b/ext/node/polyfills/_util/std_asserts.ts @@ -0,0 +1,293 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// vendored from std/testing/asserts.ts + +import { red } from "internal:deno_node/polyfills/_util/std_fmt_colors.ts"; +import { + buildMessage, + diff, + diffstr, +} from "internal:deno_node/polyfills/_util/std_testing_diff.ts"; + +/** Converts the input into a string. Objects, Sets and Maps are sorted so as to + * make tests less flaky */ +export function format(v: unknown): string { + // deno-lint-ignore no-explicit-any + const { Deno } = globalThis as any; + return typeof Deno?.inspect === "function" + ? Deno.inspect(v, { + depth: Infinity, + sorted: true, + trailingComma: true, + compact: false, + iterableLimit: Infinity, + // getters should be true in assertEquals. + getters: true, + }) + : `"${String(v).replace(/(?=["\\])/g, "\\")}"`; +} + +const CAN_NOT_DISPLAY = "[Cannot display]"; + +export class AssertionError extends Error { + override name = "AssertionError"; + constructor(message: string) { + super(message); + } +} + +function isKeyedCollection(x: unknown): x is Set { + return [Symbol.iterator, "size"].every((k) => k in (x as Set)); +} + +/** Deep equality comparison used in assertions */ +export function equal(c: unknown, d: unknown): boolean { + const seen = new Map(); + return (function compare(a: unknown, b: unknown): boolean { + // Have to render RegExp & Date for string comparison + // unless it's mistreated as object + if ( + a && + b && + ((a instanceof RegExp && b instanceof RegExp) || + (a instanceof URL && b instanceof URL)) + ) { + return String(a) === String(b); + } + if (a instanceof Date && b instanceof Date) { + const aTime = a.getTime(); + const bTime = b.getTime(); + // Check for NaN equality manually since NaN is not + // equal to itself. + if (Number.isNaN(aTime) && Number.isNaN(bTime)) { + return true; + } + return aTime === bTime; + } + if (typeof a === "number" && typeof b === "number") { + return Number.isNaN(a) && Number.isNaN(b) || a === b; + } + if (Object.is(a, b)) { + return true; + } + if (a && typeof a === "object" && b && typeof b === "object") { + if (a && b && !constructorsEqual(a, b)) { + return false; + } + if (a instanceof WeakMap || b instanceof WeakMap) { + if (!(a instanceof WeakMap && b instanceof WeakMap)) return false; + throw new TypeError("cannot compare WeakMap instances"); + } + if (a instanceof WeakSet || b instanceof WeakSet) { + if (!(a instanceof WeakSet && b instanceof WeakSet)) return false; + throw new TypeError("cannot compare WeakSet instances"); + } + if (seen.get(a) === b) { + return true; + } + if (Object.keys(a || {}).length !== Object.keys(b || {}).length) { + return false; + } + seen.set(a, b); + if (isKeyedCollection(a) && isKeyedCollection(b)) { + if (a.size !== b.size) { + return false; + } + + let unmatchedEntries = a.size; + + for (const [aKey, aValue] of a.entries()) { + for (const [bKey, bValue] of b.entries()) { + /* Given that Map keys can be references, we need + * to ensure that they are also deeply equal */ + if ( + (aKey === aValue && bKey === bValue && compare(aKey, bKey)) || + (compare(aKey, bKey) && compare(aValue, bValue)) + ) { + unmatchedEntries--; + break; + } + } + } + + return unmatchedEntries === 0; + } + const merged = { ...a, ...b }; + for ( + const key of [ + ...Object.getOwnPropertyNames(merged), + ...Object.getOwnPropertySymbols(merged), + ] + ) { + type Key = keyof typeof merged; + if (!compare(a && a[key as Key], b && b[key as Key])) { + return false; + } + if (((key in a) && (!(key in b))) || ((key in b) && (!(key in a)))) { + return false; + } + } + if (a instanceof WeakRef || b instanceof WeakRef) { + if (!(a instanceof WeakRef && b instanceof WeakRef)) return false; + return compare(a.deref(), b.deref()); + } + return true; + } + return false; + })(c, d); +} + +// deno-lint-ignore ban-types +function constructorsEqual(a: object, b: object) { + return a.constructor === b.constructor || + a.constructor === Object && !b.constructor || + !a.constructor && b.constructor === Object; +} + +/** Make an assertion, error will be thrown if `expr` does not have truthy value. */ +export function assert(expr: unknown, msg = ""): asserts expr { + if (!expr) { + throw new AssertionError(msg); + } +} + +/** Make an assertion that `actual` and `expected` are equal, deeply. If not + * deeply equal, then throw. */ +export function assertEquals(actual: T, expected: T, msg?: string) { + if (equal(actual, expected)) { + return; + } + let message = ""; + const actualString = format(actual); + const expectedString = format(expected); + try { + const stringDiff = (typeof actual === "string") && + (typeof expected === "string"); + const diffResult = stringDiff + ? diffstr(actual as string, expected as string) + : diff(actualString.split("\n"), expectedString.split("\n")); + const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n"); + message = `Values are not equal:\n${diffMsg}`; + } catch { + message = `\n${red(red(CAN_NOT_DISPLAY))} + \n\n`; + } + if (msg) { + message = msg; + } + throw new AssertionError(message); +} + +/** Make an assertion that `actual` and `expected` are not equal, deeply. + * If not then throw. */ +export function assertNotEquals(actual: T, expected: T, msg?: string) { + if (!equal(actual, expected)) { + return; + } + let actualString: string; + let expectedString: string; + try { + actualString = String(actual); + } catch { + actualString = "[Cannot display]"; + } + try { + expectedString = String(expected); + } catch { + expectedString = "[Cannot display]"; + } + if (!msg) { + msg = `actual: ${actualString} expected not to be: ${expectedString}`; + } + throw new AssertionError(msg); +} + +/** Make an assertion that `actual` and `expected` are strictly equal. If + * not then throw. */ +export function assertStrictEquals( + actual: unknown, + expected: T, + msg?: string, +): asserts actual is T { + if (Object.is(actual, expected)) { + return; + } + + let message: string; + + if (msg) { + message = msg; + } else { + const actualString = format(actual); + const expectedString = format(expected); + + if (actualString === expectedString) { + const withOffset = actualString + .split("\n") + .map((l) => ` ${l}`) + .join("\n"); + message = + `Values have the same structure but are not reference-equal:\n\n${ + red(withOffset) + }\n`; + } else { + try { + const stringDiff = (typeof actual === "string") && + (typeof expected === "string"); + const diffResult = stringDiff + ? diffstr(actual as string, expected as string) + : diff(actualString.split("\n"), expectedString.split("\n")); + const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n"); + message = `Values are not strictly equal:\n${diffMsg}`; + } catch { + message = `\n${CAN_NOT_DISPLAY} + \n\n`; + } + } + } + + throw new AssertionError(message); +} + +/** Make an assertion that `actual` and `expected` are not strictly equal. + * If the values are strictly equal then throw. */ +export function assertNotStrictEquals( + actual: T, + expected: T, + msg?: string, +) { + if (!Object.is(actual, expected)) { + return; + } + + throw new AssertionError( + msg ?? `Expected "actual" to be strictly unequal to: ${format(actual)}\n`, + ); +} + +/** Make an assertion that `actual` match RegExp `expected`. If not + * then throw. */ +export function assertMatch( + actual: string, + expected: RegExp, + msg?: string, +) { + if (!expected.test(actual)) { + if (!msg) { + msg = `actual: "${actual}" expected to match: "${expected}"`; + } + throw new AssertionError(msg); + } +} + +/** Make an assertion that `actual` not match RegExp `expected`. If match + * then throw. */ +export function assertNotMatch( + actual: string, + expected: RegExp, + msg?: string, +) { + if (expected.test(actual)) { + if (!msg) { + msg = `actual: "${actual}" expected to not match: "${expected}"`; + } + throw new AssertionError(msg); + } +} diff --git a/ext/node/polyfills/_util/std_fmt_colors.ts b/ext/node/polyfills/_util/std_fmt_colors.ts new file mode 100644 index 0000000000..d54e2c83d2 --- /dev/null +++ b/ext/node/polyfills/_util/std_fmt_colors.ts @@ -0,0 +1,519 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// This file is vendored from std/fmt/colors.ts + +// TODO(kt3k): Initialize this at the start of runtime +// based on Deno.noColor +const noColor = false; + +interface Code { + open: string; + close: string; + regexp: RegExp; +} + +/** RGB 8-bits per channel. Each in range `0->255` or `0x00->0xff` */ +interface Rgb { + r: number; + g: number; + b: number; +} + +let enabled = !noColor; + +/** + * Set changing text color to enabled or disabled + * @param value + */ +export function setColorEnabled(value: boolean) { + if (noColor) { + return; + } + + enabled = value; +} + +/** Get whether text color change is enabled or disabled. */ +export function getColorEnabled(): boolean { + return enabled; +} + +/** + * Builds color code + * @param open + * @param close + */ +function code(open: number[], close: number): Code { + return { + open: `\x1b[${open.join(";")}m`, + close: `\x1b[${close}m`, + regexp: new RegExp(`\\x1b\\[${close}m`, "g"), + }; +} + +/** + * Applies color and background based on color code and its associated text + * @param str text to apply color settings to + * @param code color code to apply + */ +function run(str: string, code: Code): string { + return enabled + ? `${code.open}${str.replace(code.regexp, code.open)}${code.close}` + : str; +} + +/** + * Reset the text modified + * @param str text to reset + */ +export function reset(str: string): string { + return run(str, code([0], 0)); +} + +/** + * Make the text bold. + * @param str text to make bold + */ +export function bold(str: string): string { + return run(str, code([1], 22)); +} + +/** + * The text emits only a small amount of light. + * @param str text to dim + */ +export function dim(str: string): string { + return run(str, code([2], 22)); +} + +/** + * Make the text italic. + * @param str text to make italic + */ +export function italic(str: string): string { + return run(str, code([3], 23)); +} + +/** + * Make the text underline. + * @param str text to underline + */ +export function underline(str: string): string { + return run(str, code([4], 24)); +} + +/** + * Invert background color and text color. + * @param str text to invert its color + */ +export function inverse(str: string): string { + return run(str, code([7], 27)); +} + +/** + * Make the text hidden. + * @param str text to hide + */ +export function hidden(str: string): string { + return run(str, code([8], 28)); +} + +/** + * Put horizontal line through the center of the text. + * @param str text to strike through + */ +export function strikethrough(str: string): string { + return run(str, code([9], 29)); +} + +/** + * Set text color to black. + * @param str text to make black + */ +export function black(str: string): string { + return run(str, code([30], 39)); +} + +/** + * Set text color to red. + * @param str text to make red + */ +export function red(str: string): string { + return run(str, code([31], 39)); +} + +/** + * Set text color to green. + * @param str text to make green + */ +export function green(str: string): string { + return run(str, code([32], 39)); +} + +/** + * Set text color to yellow. + * @param str text to make yellow + */ +export function yellow(str: string): string { + return run(str, code([33], 39)); +} + +/** + * Set text color to blue. + * @param str text to make blue + */ +export function blue(str: string): string { + return run(str, code([34], 39)); +} + +/** + * Set text color to magenta. + * @param str text to make magenta + */ +export function magenta(str: string): string { + return run(str, code([35], 39)); +} + +/** + * Set text color to cyan. + * @param str text to make cyan + */ +export function cyan(str: string): string { + return run(str, code([36], 39)); +} + +/** + * Set text color to white. + * @param str text to make white + */ +export function white(str: string): string { + return run(str, code([37], 39)); +} + +/** + * Set text color to gray. + * @param str text to make gray + */ +export function gray(str: string): string { + return brightBlack(str); +} + +/** + * Set text color to bright black. + * @param str text to make bright-black + */ +export function brightBlack(str: string): string { + return run(str, code([90], 39)); +} + +/** + * Set text color to bright red. + * @param str text to make bright-red + */ +export function brightRed(str: string): string { + return run(str, code([91], 39)); +} + +/** + * Set text color to bright green. + * @param str text to make bright-green + */ +export function brightGreen(str: string): string { + return run(str, code([92], 39)); +} + +/** + * Set text color to bright yellow. + * @param str text to make bright-yellow + */ +export function brightYellow(str: string): string { + return run(str, code([93], 39)); +} + +/** + * Set text color to bright blue. + * @param str text to make bright-blue + */ +export function brightBlue(str: string): string { + return run(str, code([94], 39)); +} + +/** + * Set text color to bright magenta. + * @param str text to make bright-magenta + */ +export function brightMagenta(str: string): string { + return run(str, code([95], 39)); +} + +/** + * Set text color to bright cyan. + * @param str text to make bright-cyan + */ +export function brightCyan(str: string): string { + return run(str, code([96], 39)); +} + +/** + * Set text color to bright white. + * @param str text to make bright-white + */ +export function brightWhite(str: string): string { + return run(str, code([97], 39)); +} + +/** + * Set background color to black. + * @param str text to make its background black + */ +export function bgBlack(str: string): string { + return run(str, code([40], 49)); +} + +/** + * Set background color to red. + * @param str text to make its background red + */ +export function bgRed(str: string): string { + return run(str, code([41], 49)); +} + +/** + * Set background color to green. + * @param str text to make its background green + */ +export function bgGreen(str: string): string { + return run(str, code([42], 49)); +} + +/** + * Set background color to yellow. + * @param str text to make its background yellow + */ +export function bgYellow(str: string): string { + return run(str, code([43], 49)); +} + +/** + * Set background color to blue. + * @param str text to make its background blue + */ +export function bgBlue(str: string): string { + return run(str, code([44], 49)); +} + +/** + * Set background color to magenta. + * @param str text to make its background magenta + */ +export function bgMagenta(str: string): string { + return run(str, code([45], 49)); +} + +/** + * Set background color to cyan. + * @param str text to make its background cyan + */ +export function bgCyan(str: string): string { + return run(str, code([46], 49)); +} + +/** + * Set background color to white. + * @param str text to make its background white + */ +export function bgWhite(str: string): string { + return run(str, code([47], 49)); +} + +/** + * Set background color to bright black. + * @param str text to make its background bright-black + */ +export function bgBrightBlack(str: string): string { + return run(str, code([100], 49)); +} + +/** + * Set background color to bright red. + * @param str text to make its background bright-red + */ +export function bgBrightRed(str: string): string { + return run(str, code([101], 49)); +} + +/** + * Set background color to bright green. + * @param str text to make its background bright-green + */ +export function bgBrightGreen(str: string): string { + return run(str, code([102], 49)); +} + +/** + * Set background color to bright yellow. + * @param str text to make its background bright-yellow + */ +export function bgBrightYellow(str: string): string { + return run(str, code([103], 49)); +} + +/** + * Set background color to bright blue. + * @param str text to make its background bright-blue + */ +export function bgBrightBlue(str: string): string { + return run(str, code([104], 49)); +} + +/** + * Set background color to bright magenta. + * @param str text to make its background bright-magenta + */ +export function bgBrightMagenta(str: string): string { + return run(str, code([105], 49)); +} + +/** + * Set background color to bright cyan. + * @param str text to make its background bright-cyan + */ +export function bgBrightCyan(str: string): string { + return run(str, code([106], 49)); +} + +/** + * Set background color to bright white. + * @param str text to make its background bright-white + */ +export function bgBrightWhite(str: string): string { + return run(str, code([107], 49)); +} + +/* Special Color Sequences */ + +/** + * Clam and truncate color codes + * @param n + * @param max number to truncate to + * @param min number to truncate from + */ +function clampAndTruncate(n: number, max = 255, min = 0): number { + return Math.trunc(Math.max(Math.min(n, max), min)); +} + +/** + * Set text color using paletted 8bit colors. + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + * @param str text color to apply paletted 8bit colors to + * @param color code + */ +export function rgb8(str: string, color: number): string { + return run(str, code([38, 5, clampAndTruncate(color)], 39)); +} + +/** + * Set background color using paletted 8bit colors. + * https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit + * @param str text color to apply paletted 8bit background colors to + * @param color code + */ +export function bgRgb8(str: string, color: number): string { + return run(str, code([48, 5, clampAndTruncate(color)], 49)); +} + +/** + * Set text color using 24bit rgb. + * `color` can be a number in range `0x000000` to `0xffffff` or + * an `Rgb`. + * + * To produce the color magenta: + * + * ```ts + * import { rgb24 } from "https://deno.land/std@$STD_VERSION/fmt/colors.ts"; + * rgb24("foo", 0xff00ff); + * rgb24("foo", {r: 255, g: 0, b: 255}); + * ``` + * @param str text color to apply 24bit rgb to + * @param color code + */ +export function rgb24(str: string, color: number | Rgb): string { + if (typeof color === "number") { + return run( + str, + code( + [38, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], + 39, + ), + ); + } + return run( + str, + code( + [ + 38, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b), + ], + 39, + ), + ); +} + +/** + * Set background color using 24bit rgb. + * `color` can be a number in range `0x000000` to `0xffffff` or + * an `Rgb`. + * + * To produce the color magenta: + * + * ```ts + * import { bgRgb24 } from "https://deno.land/std@$STD_VERSION/fmt/colors.ts"; + * bgRgb24("foo", 0xff00ff); + * bgRgb24("foo", {r: 255, g: 0, b: 255}); + * ``` + * @param str text color to apply 24bit rgb to + * @param color code + */ +export function bgRgb24(str: string, color: number | Rgb): string { + if (typeof color === "number") { + return run( + str, + code( + [48, 2, (color >> 16) & 0xff, (color >> 8) & 0xff, color & 0xff], + 49, + ), + ); + } + return run( + str, + code( + [ + 48, + 2, + clampAndTruncate(color.r), + clampAndTruncate(color.g), + clampAndTruncate(color.b), + ], + 49, + ), + ); +} + +// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js +const ANSI_PATTERN = new RegExp( + [ + "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", + "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))", + ].join("|"), + "g", +); + +/** + * Remove ANSI escape codes from the string. + * @param string to remove ANSI escape codes from + */ +export function stripColor(string: string): string { + return string.replace(ANSI_PATTERN, ""); +} diff --git a/ext/node/polyfills/_util/std_testing_diff.ts b/ext/node/polyfills/_util/std_testing_diff.ts new file mode 100644 index 0000000000..766b5efdc4 --- /dev/null +++ b/ext/node/polyfills/_util/std_testing_diff.ts @@ -0,0 +1,440 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// This file was vendored from std/testing/_diff.ts + +import { + bgGreen, + bgRed, + bold, + gray, + green, + red, + white, +} from "internal:deno_node/polyfills/_util/std_fmt_colors.ts"; + +interface FarthestPoint { + y: number; + id: number; +} + +export enum DiffType { + removed = "removed", + common = "common", + added = "added", +} + +export interface DiffResult { + type: DiffType; + value: T; + details?: Array>; +} + +const REMOVED = 1; +const COMMON = 2; +const ADDED = 3; + +function createCommon(A: T[], B: T[], reverse?: boolean): T[] { + const common = []; + if (A.length === 0 || B.length === 0) return []; + for (let i = 0; i < Math.min(A.length, B.length); i += 1) { + if ( + A[reverse ? A.length - i - 1 : i] === B[reverse ? B.length - i - 1 : i] + ) { + common.push(A[reverse ? A.length - i - 1 : i]); + } else { + return common; + } + } + return common; +} + +/** + * Renders the differences between the actual and expected values + * @param A Actual value + * @param B Expected value + */ +export function diff(A: T[], B: T[]): Array> { + const prefixCommon = createCommon(A, B); + const suffixCommon = createCommon( + A.slice(prefixCommon.length), + B.slice(prefixCommon.length), + true, + ).reverse(); + A = suffixCommon.length + ? A.slice(prefixCommon.length, -suffixCommon.length) + : A.slice(prefixCommon.length); + B = suffixCommon.length + ? B.slice(prefixCommon.length, -suffixCommon.length) + : B.slice(prefixCommon.length); + const swapped = B.length > A.length; + [A, B] = swapped ? [B, A] : [A, B]; + const M = A.length; + const N = B.length; + if (!M && !N && !suffixCommon.length && !prefixCommon.length) return []; + if (!N) { + return [ + ...prefixCommon.map( + (c): DiffResult => ({ type: DiffType.common, value: c }), + ), + ...A.map( + (a): DiffResult => ({ + type: swapped ? DiffType.added : DiffType.removed, + value: a, + }), + ), + ...suffixCommon.map( + (c): DiffResult => ({ type: DiffType.common, value: c }), + ), + ]; + } + const offset = N; + const delta = M - N; + const size = M + N + 1; + const fp: FarthestPoint[] = Array.from( + { length: size }, + () => ({ y: -1, id: -1 }), + ); + /** + * INFO: + * This buffer is used to save memory and improve performance. + * The first half is used to save route and last half is used to save diff + * type. + * This is because, when I kept new uint8array area to save type,performance + * worsened. + */ + const routes = new Uint32Array((M * N + size + 1) * 2); + const diffTypesPtrOffset = routes.length / 2; + let ptr = 0; + let p = -1; + + function backTrace( + A: T[], + B: T[], + current: FarthestPoint, + swapped: boolean, + ): Array<{ + type: DiffType; + value: T; + }> { + const M = A.length; + const N = B.length; + const result = []; + let a = M - 1; + let b = N - 1; + let j = routes[current.id]; + let type = routes[current.id + diffTypesPtrOffset]; + while (true) { + if (!j && !type) break; + const prev = j; + if (type === REMOVED) { + result.unshift({ + type: swapped ? DiffType.removed : DiffType.added, + value: B[b], + }); + b -= 1; + } else if (type === ADDED) { + result.unshift({ + type: swapped ? DiffType.added : DiffType.removed, + value: A[a], + }); + a -= 1; + } else { + result.unshift({ type: DiffType.common, value: A[a] }); + a -= 1; + b -= 1; + } + j = routes[prev]; + type = routes[prev + diffTypesPtrOffset]; + } + return result; + } + + function createFP( + slide: FarthestPoint, + down: FarthestPoint, + k: number, + M: number, + ): FarthestPoint { + if (slide && slide.y === -1 && down && down.y === -1) { + return { y: 0, id: 0 }; + } + if ( + (down && down.y === -1) || + k === M || + (slide && slide.y) > (down && down.y) + 1 + ) { + const prev = slide.id; + ptr++; + routes[ptr] = prev; + routes[ptr + diffTypesPtrOffset] = ADDED; + return { y: slide.y, id: ptr }; + } else { + const prev = down.id; + ptr++; + routes[ptr] = prev; + routes[ptr + diffTypesPtrOffset] = REMOVED; + return { y: down.y + 1, id: ptr }; + } + } + + function snake( + k: number, + slide: FarthestPoint, + down: FarthestPoint, + _offset: number, + A: T[], + B: T[], + ): FarthestPoint { + const M = A.length; + const N = B.length; + if (k < -N || M < k) return { y: -1, id: -1 }; + const fp = createFP(slide, down, k, M); + while (fp.y + k < M && fp.y < N && A[fp.y + k] === B[fp.y]) { + const prev = fp.id; + ptr++; + fp.id = ptr; + fp.y += 1; + routes[ptr] = prev; + routes[ptr + diffTypesPtrOffset] = COMMON; + } + return fp; + } + + while (fp[delta + offset].y < N) { + p = p + 1; + for (let k = -p; k < delta; ++k) { + fp[k + offset] = snake( + k, + fp[k - 1 + offset], + fp[k + 1 + offset], + offset, + A, + B, + ); + } + for (let k = delta + p; k > delta; --k) { + fp[k + offset] = snake( + k, + fp[k - 1 + offset], + fp[k + 1 + offset], + offset, + A, + B, + ); + } + fp[delta + offset] = snake( + delta, + fp[delta - 1 + offset], + fp[delta + 1 + offset], + offset, + A, + B, + ); + } + return [ + ...prefixCommon.map( + (c): DiffResult => ({ type: DiffType.common, value: c }), + ), + ...backTrace(A, B, fp[delta + offset], swapped), + ...suffixCommon.map( + (c): DiffResult => ({ type: DiffType.common, value: c }), + ), + ]; +} + +/** + * Renders the differences between the actual and expected strings + * Partially inspired from https://github.com/kpdecker/jsdiff + * @param A Actual string + * @param B Expected string + */ +export function diffstr(A: string, B: string) { + function unescape(string: string): string { + // unescape invisible characters. + // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String#escape_sequences + return string + .replaceAll("\b", "\\b") + .replaceAll("\f", "\\f") + .replaceAll("\t", "\\t") + .replaceAll("\v", "\\v") + .replaceAll( // does not remove line breaks + /\r\n|\r|\n/g, + (str) => str === "\r" ? "\\r" : str === "\n" ? "\\n\n" : "\\r\\n\r\n", + ); + } + + function tokenize(string: string, { wordDiff = false } = {}): string[] { + if (wordDiff) { + // Split string on whitespace symbols + const tokens = string.split(/([^\S\r\n]+|[()[\]{}'"\r\n]|\b)/); + // Extended Latin character set + const words = + /^[a-zA-Z\u{C0}-\u{FF}\u{D8}-\u{F6}\u{F8}-\u{2C6}\u{2C8}-\u{2D7}\u{2DE}-\u{2FF}\u{1E00}-\u{1EFF}]+$/u; + + // Join boundary splits that we do not consider to be boundaries and merge empty strings surrounded by word chars + for (let i = 0; i < tokens.length - 1; i++) { + if ( + !tokens[i + 1] && tokens[i + 2] && words.test(tokens[i]) && + words.test(tokens[i + 2]) + ) { + tokens[i] += tokens[i + 2]; + tokens.splice(i + 1, 2); + i--; + } + } + return tokens.filter((token) => token); + } else { + // Split string on new lines symbols + const tokens = [], lines = string.split(/(\n|\r\n)/); + + // Ignore final empty token when text ends with a newline + if (!lines[lines.length - 1]) { + lines.pop(); + } + + // Merge the content and line separators into single tokens + for (let i = 0; i < lines.length; i++) { + if (i % 2) { + tokens[tokens.length - 1] += lines[i]; + } else { + tokens.push(lines[i]); + } + } + return tokens; + } + } + + // Create details by filtering relevant word-diff for current line + // and merge "space-diff" if surrounded by word-diff for cleaner displays + function createDetails( + line: DiffResult, + tokens: Array>, + ) { + return tokens.filter(({ type }) => + type === line.type || type === DiffType.common + ).map((result, i, t) => { + if ( + (result.type === DiffType.common) && (t[i - 1]) && + (t[i - 1]?.type === t[i + 1]?.type) && /\s+/.test(result.value) + ) { + return { + ...result, + type: t[i - 1].type, + }; + } + return result; + }); + } + + // Compute multi-line diff + const diffResult = diff( + tokenize(`${unescape(A)}\n`), + tokenize(`${unescape(B)}\n`), + ); + + const added = [], removed = []; + for (const result of diffResult) { + if (result.type === DiffType.added) { + added.push(result); + } + if (result.type === DiffType.removed) { + removed.push(result); + } + } + + // Compute word-diff + const aLines = added.length < removed.length ? added : removed; + const bLines = aLines === removed ? added : removed; + for (const a of aLines) { + let tokens = [] as Array>, + b: undefined | DiffResult; + // Search another diff line with at least one common token + while (bLines.length) { + b = bLines.shift(); + tokens = diff( + tokenize(a.value, { wordDiff: true }), + tokenize(b?.value ?? "", { wordDiff: true }), + ); + if ( + tokens.some(({ type, value }) => + type === DiffType.common && value.trim().length + ) + ) { + break; + } + } + // Register word-diff details + a.details = createDetails(a, tokens); + if (b) { + b.details = createDetails(b, tokens); + } + } + + return diffResult; +} + +/** + * Colors the output of assertion diffs + * @param diffType Difference type, either added or removed + */ +function createColor( + diffType: DiffType, + { background = false } = {}, +): (s: string) => string { + // TODO(@littledivy): Remove this when we can detect + // true color terminals. + // https://github.com/denoland/deno_std/issues/2575 + background = false; + switch (diffType) { + case DiffType.added: + return (s: string): string => + background ? bgGreen(white(s)) : green(bold(s)); + case DiffType.removed: + return (s: string): string => background ? bgRed(white(s)) : red(bold(s)); + default: + return white; + } +} + +/** + * Prefixes `+` or `-` in diff output + * @param diffType Difference type, either added or removed + */ +function createSign(diffType: DiffType): string { + switch (diffType) { + case DiffType.added: + return "+ "; + case DiffType.removed: + return "- "; + default: + return " "; + } +} + +export function buildMessage( + diffResult: ReadonlyArray>, + { stringDiff = false } = {}, +): string[] { + const messages: string[] = [], diffMessages: string[] = []; + messages.push(""); + messages.push(""); + messages.push( + ` ${gray(bold("[Diff]"))} ${red(bold("Actual"))} / ${ + green(bold("Expected")) + }`, + ); + messages.push(""); + messages.push(""); + diffResult.forEach((result: DiffResult) => { + const c = createColor(result.type); + const line = result.details?.map((detail) => + detail.type !== DiffType.common + ? createColor(detail.type, { background: true })(detail.value) + : detail.value + ).join("") ?? result.value; + diffMessages.push(c(`${createSign(result.type)}${line}`)); + }); + messages.push(...(stringDiff ? [diffMessages.join("")] : diffMessages)); + messages.push(""); + + return messages; +} diff --git a/ext/node/polyfills/_utils.ts b/ext/node/polyfills/_utils.ts new file mode 100644 index 0000000000..85398ead99 --- /dev/null +++ b/ext/node/polyfills/_utils.ts @@ -0,0 +1,210 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + TextDecoder, + TextEncoder, +} from "internal:deno_web/08_text_encoding.js"; +import { errorMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; + +export type BinaryEncodings = "binary"; + +export type TextEncodings = + | "ascii" + | "utf8" + | "utf-8" + | "utf16le" + | "ucs2" + | "ucs-2" + | "base64" + | "latin1" + | "hex"; + +export type Encodings = BinaryEncodings | TextEncodings; + +export function notImplemented(msg: string): never { + const message = msg ? `Not implemented: ${msg}` : "Not implemented"; + throw new Error(message); +} + +export function warnNotImplemented(msg?: string) { + const message = msg + ? `Warning: Not implemented: ${msg}` + : "Warning: Not implemented"; + console.warn(message); +} + +export type _TextDecoder = typeof TextDecoder.prototype; +export const _TextDecoder = TextDecoder; + +export type _TextEncoder = typeof TextEncoder.prototype; +export const _TextEncoder = TextEncoder; + +// API helpers + +export type MaybeNull = T | null; +export type MaybeDefined = T | undefined; +export type MaybeEmpty = T | null | undefined; + +export function intoCallbackAPI( + // deno-lint-ignore no-explicit-any + func: (...args: any[]) => Promise, + cb: MaybeEmpty<(err: MaybeNull, value?: MaybeEmpty) => void>, + // deno-lint-ignore no-explicit-any + ...args: any[] +) { + func(...args).then( + (value) => cb && cb(null, value), + (err) => cb && cb(err), + ); +} + +export function intoCallbackAPIWithIntercept( + // deno-lint-ignore no-explicit-any + func: (...args: any[]) => Promise, + interceptor: (v: T1) => T2, + cb: MaybeEmpty<(err: MaybeNull, value?: MaybeEmpty) => void>, + // deno-lint-ignore no-explicit-any + ...args: any[] +) { + func(...args).then( + (value) => cb && cb(null, interceptor(value)), + (err) => cb && cb(err), + ); +} + +export function spliceOne(list: string[], index: number) { + for (; index + 1 < list.length; index++) list[index] = list[index + 1]; + list.pop(); +} + +// Taken from: https://github.com/nodejs/node/blob/ba684805b6c0eded76e5cd89ee00328ac7a59365/lib/internal/util.js#L125 +// Return undefined if there is no match. +// Move the "slow cases" to a separate function to make sure this function gets +// inlined properly. That prioritizes the common case. +export function normalizeEncoding( + enc: string | null, +): TextEncodings | undefined { + if (enc == null || enc === "utf8" || enc === "utf-8") return "utf8"; + return slowCases(enc); +} + +// https://github.com/nodejs/node/blob/ba684805b6c0eded76e5cd89ee00328ac7a59365/lib/internal/util.js#L130 +function slowCases(enc: string): TextEncodings | undefined { + switch (enc.length) { + case 4: + if (enc === "UTF8") return "utf8"; + if (enc === "ucs2" || enc === "UCS2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf8") return "utf8"; + if (enc === "ucs2") return "utf16le"; + break; + case 3: + if (enc === "hex" || enc === "HEX" || `${enc}`.toLowerCase() === "hex") { + return "hex"; + } + break; + case 5: + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + if (enc === "UTF-8") return "utf8"; + if (enc === "ASCII") return "ascii"; + if (enc === "UCS-2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf-8") return "utf8"; + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + break; + case 6: + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + if (enc === "BASE64") return "base64"; + if (enc === "LATIN1" || enc === "BINARY") return "latin1"; + enc = `${enc}`.toLowerCase(); + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + break; + case 7: + if ( + enc === "utf16le" || + enc === "UTF16LE" || + `${enc}`.toLowerCase() === "utf16le" + ) { + return "utf16le"; + } + break; + case 8: + if ( + enc === "utf-16le" || + enc === "UTF-16LE" || + `${enc}`.toLowerCase() === "utf-16le" + ) { + return "utf16le"; + } + break; + default: + if (enc === "") return "utf8"; + } +} + +export function validateIntegerRange( + value: number, + name: string, + min = -2147483648, + max = 2147483647, +) { + // The defaults for min and max correspond to the limits of 32-bit integers. + if (!Number.isInteger(value)) { + throw new Error(`${name} must be 'an integer' but was ${value}`); + } + + if (value < min || value > max) { + throw new Error( + `${name} must be >= ${min} && <= ${max}. Value was ${value}`, + ); + } +} + +type OptionalSpread = T extends undefined ? [] + : [T]; + +export function once( + callback: (...args: OptionalSpread) => void, +) { + let called = false; + return function (this: unknown, ...args: OptionalSpread) { + if (called) return; + called = true; + callback.apply(this, args); + }; +} + +export function makeMethodsEnumerable(klass: { new (): unknown }) { + const proto = klass.prototype; + for (const key of Object.getOwnPropertyNames(proto)) { + const value = proto[key]; + if (typeof value === "function") { + const desc = Reflect.getOwnPropertyDescriptor(proto, key); + if (desc) { + desc.enumerable = true; + Object.defineProperty(proto, key, desc); + } + } + } +} + +const NumberIsSafeInteger = Number.isSafeInteger; + +/** + * Returns a system error name from an error code number. + * @param code error code number + */ +export function getSystemErrorName(code: number): string | undefined { + if (typeof code !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE("err", "number", code); + } + if (code >= 0 || !NumberIsSafeInteger(code)) { + throw new codes.ERR_OUT_OF_RANGE("err", "a negative integer", code); + } + return errorMap.get(code)?.[0]; +} diff --git a/ext/node/polyfills/_zlib.mjs b/ext/node/polyfills/_zlib.mjs new file mode 100644 index 0000000000..0b1cb2d5f2 --- /dev/null +++ b/ext/node/polyfills/_zlib.mjs @@ -0,0 +1,628 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright (c) 2014-2015 Devon Govett +// Forked from https://github.com/browserify/browserify-zlib + +// deno-lint-ignore-file + +import { Buffer, kMaxLength } from "internal:deno_node/polyfills/buffer.ts"; +import { Transform } from "internal:deno_node/polyfills/stream.ts"; +import * as binding from "internal:deno_node/polyfills/_zlib_binding.mjs"; +import util from "internal:deno_node/polyfills/util.ts"; +import { ok as assert } from "internal:deno_node/polyfills/assert.ts"; +import { zlib as zlibConstants } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; + +var kRangeErrorMessage = "Cannot create final Buffer. It would be larger " + + "than 0x" + kMaxLength.toString(16) + " bytes"; + +// translation table for return codes. +export const codes = Object.freeze({ + Z_OK: binding.Z_OK, + Z_STREAM_END: binding.Z_STREAM_END, + Z_NEED_DICT: binding.Z_NEED_DICT, + Z_ERRNO: binding.Z_ERRNO, + Z_STREAM_ERROR: binding.Z_STREAM_ERROR, + Z_DATA_ERROR: binding.Z_DATA_ERROR, + Z_MEM_ERROR: binding.Z_MEM_ERROR, + Z_BUF_ERROR: binding.Z_BUF_ERROR, + Z_VERSION_ERROR: binding.Z_VERSION_ERROR, + [binding.Z_OK]: "Z_OK", + [binding.Z_STREAM_END]: "Z_STREAM_END", + [binding.Z_NEED_DICT]: "Z_NEED_DICT", + [binding.Z_ERRNO]: "Z_ERRNO", + [binding.Z_STREAM_ERROR]: "Z_STREAM_ERROR", + [binding.Z_DATA_ERROR]: "Z_DATA_ERROR", + [binding.Z_MEM_ERROR]: "Z_MEM_ERROR", + [binding.Z_BUF_ERROR]: "Z_BUF_ERROR", + [binding.Z_VERSION_ERROR]: "Z_VERSION_ERROR", +}); + +export const createDeflate = function (o) { + return new Deflate(o); +}; + +export const createInflate = function (o) { + return new Inflate(o); +}; + +export const createDeflateRaw = function (o) { + return new DeflateRaw(o); +}; + +export const createInflateRaw = function (o) { + return new InflateRaw(o); +}; + +export const createGzip = function (o) { + return new Gzip(o); +}; + +export const createGunzip = function (o) { + return new Gunzip(o); +}; + +export const createUnzip = function (o) { + return new Unzip(o); +}; + +// Convenience methods. +// compress/decompress a string or buffer in one step. +export const deflate = function (buffer, opts, callback) { + if (typeof opts === "function") { + callback = opts; + opts = {}; + } + return zlibBuffer(new Deflate(opts), buffer, callback); +}; + +export const deflateSync = function (buffer, opts) { + return zlibBufferSync(new Deflate(opts), buffer); +}; + +export const gzip = function (buffer, opts, callback) { + if (typeof opts === "function") { + callback = opts; + opts = {}; + } + return zlibBuffer(new Gzip(opts), buffer, callback); +}; + +export const gzipSync = function (buffer, opts) { + return zlibBufferSync(new Gzip(opts), buffer); +}; + +export const deflateRaw = function (buffer, opts, callback) { + if (typeof opts === "function") { + callback = opts; + opts = {}; + } + return zlibBuffer(new DeflateRaw(opts), buffer, callback); +}; + +export const deflateRawSync = function (buffer, opts) { + return zlibBufferSync(new DeflateRaw(opts), buffer); +}; + +export const unzip = function (buffer, opts, callback) { + if (typeof opts === "function") { + callback = opts; + opts = {}; + } + return zlibBuffer(new Unzip(opts), buffer, callback); +}; + +export const unzipSync = function (buffer, opts) { + return zlibBufferSync(new Unzip(opts), buffer); +}; + +export const inflate = function (buffer, opts, callback) { + if (typeof opts === "function") { + callback = opts; + opts = {}; + } + return zlibBuffer(new Inflate(opts), buffer, callback); +}; + +export const inflateSync = function (buffer, opts) { + return zlibBufferSync(new Inflate(opts), buffer); +}; + +export const gunzip = function (buffer, opts, callback) { + if (typeof opts === "function") { + callback = opts; + opts = {}; + } + return zlibBuffer(new Gunzip(opts), buffer, callback); +}; + +export const gunzipSync = function (buffer, opts) { + return zlibBufferSync(new Gunzip(opts), buffer); +}; + +export const inflateRaw = function (buffer, opts, callback) { + if (typeof opts === "function") { + callback = opts; + opts = {}; + } + return zlibBuffer(new InflateRaw(opts), buffer, callback); +}; + +export const inflateRawSync = function (buffer, opts) { + return zlibBufferSync(new InflateRaw(opts), buffer); +}; + +function zlibBuffer(engine, buffer, callback) { + var buffers = []; + var nread = 0; + + engine.on("error", onError); + engine.on("end", onEnd); + + engine.end(buffer); + flow(); + + function flow() { + var chunk; + while (null !== (chunk = engine.read())) { + buffers.push(chunk); + nread += chunk.length; + } + engine.once("readable", flow); + } + + function onError(err) { + engine.removeListener("end", onEnd); + engine.removeListener("readable", flow); + callback(err); + } + + function onEnd() { + var buf; + var err = null; + + if (nread >= kMaxLength) { + err = new RangeError(kRangeErrorMessage); + } else { + buf = Buffer.concat(buffers, nread); + } + + buffers = []; + engine.close(); + callback(err, buf); + } +} + +function zlibBufferSync(engine, buffer) { + if (typeof buffer === "string") buffer = Buffer.from(buffer); + + if (!Buffer.isBuffer(buffer)) throw new TypeError("Not a string or buffer"); + + var flushFlag = engine._finishFlushFlag; + + return engine._processChunk(buffer, flushFlag); +} + +// generic zlib +// minimal 2-byte header +function Deflate(opts) { + if (!(this instanceof Deflate)) return new Deflate(opts); + Zlib.call(this, opts, binding.DEFLATE); +} + +function Inflate(opts) { + if (!(this instanceof Inflate)) return new Inflate(opts); + Zlib.call(this, opts, binding.INFLATE); +} + +// gzip - bigger header, same deflate compression +function Gzip(opts) { + if (!(this instanceof Gzip)) return new Gzip(opts); + Zlib.call(this, opts, binding.GZIP); +} + +function Gunzip(opts) { + if (!(this instanceof Gunzip)) return new Gunzip(opts); + Zlib.call(this, opts, binding.GUNZIP); +} + +// raw - no header +function DeflateRaw(opts) { + if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); + Zlib.call(this, opts, binding.DEFLATERAW); +} + +function InflateRaw(opts) { + if (!(this instanceof InflateRaw)) return new InflateRaw(opts); + Zlib.call(this, opts, binding.INFLATERAW); +} + +// auto-detect header. +function Unzip(opts) { + if (!(this instanceof Unzip)) return new Unzip(opts); + Zlib.call(this, opts, binding.UNZIP); +} + +function isValidFlushFlag(flag) { + return flag === binding.Z_NO_FLUSH || flag === binding.Z_PARTIAL_FLUSH || + flag === binding.Z_SYNC_FLUSH || flag === binding.Z_FULL_FLUSH || + flag === binding.Z_FINISH || flag === binding.Z_BLOCK; +} + +// the Zlib class they all inherit from +// This thing manages the queue of requests, and returns +// true or false if there is anything in the queue when +// you call the .write() method. + +function Zlib(opts, mode) { + var _this = this; + + this._opts = opts = opts || {}; + this._chunkSize = opts.chunkSize || zlibConstants.Z_DEFAULT_CHUNK; + + Transform.call(this, opts); + + if (opts.flush && !isValidFlushFlag(opts.flush)) { + throw new Error("Invalid flush flag: " + opts.flush); + } + if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) { + throw new Error("Invalid flush flag: " + opts.finishFlush); + } + + this._flushFlag = opts.flush || binding.Z_NO_FLUSH; + this._finishFlushFlag = typeof opts.finishFlush !== "undefined" + ? opts.finishFlush + : binding.Z_FINISH; + + if (opts.chunkSize) { + if ( + opts.chunkSize < zlibConstants.Z_MIN_CHUNK || + opts.chunkSize > zlibConstants.Z_MAX_CHUNK + ) { + throw new Error("Invalid chunk size: " + opts.chunkSize); + } + } + + if (opts.windowBits) { + if ( + opts.windowBits < zlibConstants.Z_MIN_WINDOWBITS || + opts.windowBits > zlibConstants.Z_MAX_WINDOWBITS + ) { + throw new Error("Invalid windowBits: " + opts.windowBits); + } + } + + if (opts.level) { + if ( + opts.level < zlibConstants.Z_MIN_LEVEL || + opts.level > zlibConstants.Z_MAX_LEVEL + ) { + throw new Error("Invalid compression level: " + opts.level); + } + } + + if (opts.memLevel) { + if ( + opts.memLevel < zlibConstants.Z_MIN_MEMLEVEL || + opts.memLevel > zlibConstants.Z_MAX_MEMLEVEL + ) { + throw new Error("Invalid memLevel: " + opts.memLevel); + } + } + + if (opts.strategy) { + if ( + opts.strategy != zlibConstants.Z_FILTERED && + opts.strategy != zlibConstants.Z_HUFFMAN_ONLY && + opts.strategy != zlibConstants.Z_RLE && + opts.strategy != zlibConstants.Z_FIXED && + opts.strategy != zlibConstants.Z_DEFAULT_STRATEGY + ) { + throw new Error("Invalid strategy: " + opts.strategy); + } + } + + if (opts.dictionary) { + if (!Buffer.isBuffer(opts.dictionary)) { + throw new Error("Invalid dictionary: it should be a Buffer instance"); + } + } + + this._handle = new binding.Zlib(mode); + + var self = this; + this._hadError = false; + this._handle.onerror = function (message, errno) { + // there is no way to cleanly recover. + // continuing only obscures problems. + _close(self); + self._hadError = true; + + var error = new Error(message); + error.errno = errno; + error.code = codes[errno]; + self.emit("error", error); + }; + + var level = zlibConstants.Z_DEFAULT_COMPRESSION; + if (typeof opts.level === "number") level = opts.level; + + var strategy = zlibConstants.Z_DEFAULT_STRATEGY; + if (typeof opts.strategy === "number") strategy = opts.strategy; + + this._handle.init( + opts.windowBits || zlibConstants.Z_DEFAULT_WINDOWBITS, + level, + opts.memLevel || zlibConstants.Z_DEFAULT_MEMLEVEL, + strategy, + opts.dictionary, + ); + + this._buffer = Buffer.allocUnsafe(this._chunkSize); + this._offset = 0; + this._level = level; + this._strategy = strategy; + + this.once("end", this.close); + + Object.defineProperty(this, "_closed", { + get: function () { + return !_this._handle; + }, + configurable: true, + enumerable: true, + }); +} + +util.inherits(Zlib, Transform); + +Zlib.prototype.params = function (level, strategy, callback) { + if (level < zlibConstants.Z_MIN_LEVEL || level > zlibConstants.Z_MAX_LEVEL) { + throw new RangeError("Invalid compression level: " + level); + } + if ( + strategy != zlibConstants.Z_FILTERED && + strategy != zlibConstants.Z_HUFFMAN_ONLY && + strategy != zlibConstants.Z_RLE && + strategy != zlibConstants.Z_FIXED && + strategy != zlibConstants.Z_DEFAULT_STRATEGY + ) { + throw new TypeError("Invalid strategy: " + strategy); + } + + if (this._level !== level || this._strategy !== strategy) { + var self = this; + this.flush(binding.Z_SYNC_FLUSH, function () { + assert(self._handle, "zlib binding closed"); + self._handle.params(level, strategy); + if (!self._hadError) { + self._level = level; + self._strategy = strategy; + if (callback) callback(); + } + }); + } else { + nextTick(callback); + } +}; + +Zlib.prototype.reset = function () { + assert(this._handle, "zlib binding closed"); + return this._handle.reset(); +}; + +// This is the _flush function called by the transform class, +// internally, when the last chunk has been written. +Zlib.prototype._flush = function (callback) { + this._transform(Buffer.alloc(0), "", callback); +}; + +Zlib.prototype.flush = function (kind, callback) { + var _this2 = this; + + var ws = this._writableState; + + if (typeof kind === "function" || kind === undefined && !callback) { + callback = kind; + kind = binding.Z_FULL_FLUSH; + } + + if (ws.ended) { + if (callback) nextTick(callback); + } else if (ws.ending) { + if (callback) this.once("end", callback); + } else if (ws.needDrain) { + if (callback) { + this.once("drain", function () { + return _this2.flush(kind, callback); + }); + } + } else { + this._flushFlag = kind; + this.write(Buffer.alloc(0), "", callback); + } +}; + +Zlib.prototype.close = function (callback) { + _close(this, callback); + nextTick(emitCloseNT, this); +}; + +function _close(engine, callback) { + if (callback) nextTick(callback); + + // Caller may invoke .close after a zlib error (which will null _handle). + if (!engine._handle) return; + + engine._handle.close(); + engine._handle = null; +} + +function emitCloseNT(self) { + self.emit("close"); +} + +Zlib.prototype._transform = function (chunk, encoding, cb) { + var flushFlag; + var ws = this._writableState; + var ending = ws.ending || ws.ended; + var last = ending && (!chunk || ws.length === chunk.length); + + if (chunk !== null && !Buffer.isBuffer(chunk)) { + return cb(new Error("invalid input")); + } + + if (!this._handle) return cb(new Error("zlib binding closed")); + + // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag + // (or whatever flag was provided using opts.finishFlush). + // If it's explicitly flushing at some other time, then we use + // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression + // goodness. + if (last) flushFlag = this._finishFlushFlag; + else { + flushFlag = this._flushFlag; + // once we've flushed the last of the queue, stop flushing and + // go back to the normal behavior. + if (chunk.length >= ws.length) { + this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH; + } + } + + this._processChunk(chunk, flushFlag, cb); +}; + +Zlib.prototype._processChunk = function (chunk, flushFlag, cb) { + var availInBefore = chunk && chunk.length; + var availOutBefore = this._chunkSize - this._offset; + var inOff = 0; + + var self = this; + + var async = typeof cb === "function"; + + if (!async) { + var buffers = []; + var nread = 0; + + var error; + this.on("error", function (er) { + error = er; + }); + + assert(this._handle, "zlib binding closed"); + do { + var res = this._handle.writeSync( + flushFlag, + chunk, // in + inOff, // in_off + availInBefore, // in_len + this._buffer, // out + this._offset, //out_off + availOutBefore, + ); // out_len + } while (!this._hadError && callback(res[0], res[1])); + + if (this._hadError) { + throw error; + } + + if (nread >= kMaxLength) { + _close(this); + throw new RangeError(kRangeErrorMessage); + } + + var buf = Buffer.concat(buffers, nread); + _close(this); + + return buf; + } + + assert(this._handle, "zlib binding closed"); + var req = this._handle.write( + flushFlag, + chunk, // in + inOff, // in_off + availInBefore, // in_len + this._buffer, // out + this._offset, //out_off + availOutBefore, + ); // out_len + + req.buffer = chunk; + req.callback = callback; + + function callback(availInAfter, availOutAfter) { + // When the callback is used in an async write, the callback's + // context is the `req` object that was created. The req object + // is === this._handle, and that's why it's important to null + // out the values after they are done being used. `this._handle` + // can stay in memory longer than the callback and buffer are needed. + if (this) { + this.buffer = null; + this.callback = null; + } + + if (self._hadError) return; + + var have = availOutBefore - availOutAfter; + assert(have >= 0, "have should not go down"); + + if (have > 0) { + var out = self._buffer.slice(self._offset, self._offset + have); + self._offset += have; + // serve some output to the consumer. + if (async) { + self.push(out); + } else { + buffers.push(out); + nread += out.length; + } + } + + // exhausted the output buffer, or used all the input create a new one. + if (availOutAfter === 0 || self._offset >= self._chunkSize) { + availOutBefore = self._chunkSize; + self._offset = 0; + self._buffer = Buffer.allocUnsafe(self._chunkSize); + } + + if (availOutAfter === 0) { + // Not actually done. Need to reprocess. + // Also, update the availInBefore to the availInAfter value, + // so that if we have to hit it a third (fourth, etc.) time, + // it'll have the correct byte counts. + inOff += availInBefore - availInAfter; + availInBefore = availInAfter; + + if (!async) return true; + + var newReq = self._handle.write( + flushFlag, + chunk, + inOff, + availInBefore, + self._buffer, + self._offset, + self._chunkSize, + ); + newReq.callback = callback; // this same function + newReq.buffer = chunk; + return; + } + + if (!async) return false; + + // finished with the chunk. + cb(); + } +}; + +util.inherits(Deflate, Zlib); +util.inherits(Inflate, Zlib); +util.inherits(Gzip, Zlib); +util.inherits(Gunzip, Zlib); +util.inherits(DeflateRaw, Zlib); +util.inherits(InflateRaw, Zlib); +util.inherits(Unzip, Zlib); + +export { Deflate, DeflateRaw, Gunzip, Gzip, Inflate, InflateRaw, Unzip }; diff --git a/ext/node/polyfills/_zlib_binding.mjs b/ext/node/polyfills/_zlib_binding.mjs new file mode 100644 index 0000000000..0286fefd59 --- /dev/null +++ b/ext/node/polyfills/_zlib_binding.mjs @@ -0,0 +1,511 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright (c) 2014-2015 Devon Govett +// Forked from https://github.com/browserify/browserify-zlib + +// deno-lint-ignore-file + +import assert from "internal:deno_node/polyfills/assert.ts"; +import { constants, zlib_deflate, zlib_inflate, Zstream } from "internal:deno_node/polyfills/_pako.mjs"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; + +export const Z_NO_FLUSH = constants.Z_NO_FLUSH; +export const Z_PARTIAL_FLUSH = constants.Z_PARTIAL_FLUSH; +export const Z_SYNC_FLUSH = constants.Z_SYNC_FLUSH; +export const Z_FULL_FLUSH = constants.Z_FULL_FLUSH; +export const Z_FINISH = constants.Z_FINISH; +export const Z_BLOCK = constants.Z_BLOCK; +export const Z_TREES = constants.Z_TREES; +export const Z_OK = constants.Z_OK; +export const Z_STREAM_END = constants.Z_STREAM_END; +export const Z_NEED_DICT = constants.Z_NEED_DICT; +export const Z_ERRNO = constants.Z_ERRNO; +export const Z_STREAM_ERROR = constants.Z_STREAM_ERROR; +export const Z_DATA_ERROR = constants.Z_DATA_ERROR; +export const Z_MEM_ERROR = constants.Z_MEM_ERROR; +export const Z_BUF_ERROR = constants.Z_BUF_ERROR; +export const Z_VERSION_ERROR = constants.Z_VERSION_ERROR; +export const Z_NO_COMPRESSION = constants.Z_NO_COMPRESSION; +export const Z_BEST_SPEED = constants.Z_BEST_SPEED; +export const Z_BEST_COMPRESSION = constants.Z_BEST_COMPRESSION; +export const Z_DEFAULT_COMPRESSION = constants.Z_DEFAULT_COMPRESSION; +export const Z_FILTERED = constants.Z_FILTERED; +export const Z_HUFFMAN_ONLY = constants.Z_HUFFMAN_ONLYZ_FILTERED; +export const Z_RLE = constants.Z_RLE; +export const Z_FIXED = constants.Z_FIXED; +export const Z_DEFAULT_STRATEGY = constants.Z_DEFAULT_STRATEGY; +export const Z_BINARY = constants.Z_BINARY; +export const Z_TEXT = constants.Z_TEXT; +export const Z_UNKNOWN = constants.Z_UNKNOWN; +export const Z_DEFLATED = constants.Z_DEFLATED; + +// zlib modes +export const NONE = 0; +export const DEFLATE = 1; +export const INFLATE = 2; +export const GZIP = 3; +export const GUNZIP = 4; +export const DEFLATERAW = 5; +export const INFLATERAW = 6; +export const UNZIP = 7; + +var GZIP_HEADER_ID1 = 0x1f; +var GZIP_HEADER_ID2 = 0x8b; + +/** + * Emulate Node's zlib C++ layer for use by the JS layer in index.js + */ +function Zlib(mode) { + if (typeof mode !== "number" || mode < DEFLATE || mode > UNZIP) { + throw new TypeError("Bad argument"); + } + + this.dictionary = null; + this.err = 0; + this.flush = 0; + this.init_done = false; + this.level = 0; + this.memLevel = 0; + this.mode = mode; + this.strategy = 0; + this.windowBits = 0; + this.write_in_progress = false; + this.pending_close = false; + this.gzip_id_bytes_read = 0; +} + +Zlib.prototype.close = function () { + if (this.write_in_progress) { + this.pending_close = true; + return; + } + + this.pending_close = false; + + assert(this.init_done, "close before init"); + assert(this.mode <= UNZIP); + + if (this.mode === DEFLATE || this.mode === GZIP || this.mode === DEFLATERAW) { + zlib_deflate.deflateEnd(this.strm); + } else if ( + this.mode === INFLATE || this.mode === GUNZIP || this.mode === INFLATERAW || + this.mode === UNZIP + ) { + zlib_inflate.inflateEnd(this.strm); + } + + this.mode = NONE; + + this.dictionary = null; +}; + +Zlib.prototype.write = function ( + flush, + input, + in_off, + in_len, + out, + out_off, + out_len, +) { + return this._write(true, flush, input, in_off, in_len, out, out_off, out_len); +}; + +Zlib.prototype.writeSync = function ( + flush, + input, + in_off, + in_len, + out, + out_off, + out_len, +) { + return this._write( + false, + flush, + input, + in_off, + in_len, + out, + out_off, + out_len, + ); +}; + +Zlib.prototype._write = function ( + async, + flush, + input, + in_off, + in_len, + out, + out_off, + out_len, +) { + assert.equal(arguments.length, 8); + + assert(this.init_done, "write before init"); + assert(this.mode !== NONE, "already finalized"); + assert.equal(false, this.write_in_progress, "write already in progress"); + assert.equal(false, this.pending_close, "close is pending"); + + this.write_in_progress = true; + + assert.equal(false, flush === undefined, "must provide flush value"); + + this.write_in_progress = true; + + if ( + flush !== Z_NO_FLUSH && flush !== Z_PARTIAL_FLUSH && + flush !== Z_SYNC_FLUSH && flush !== Z_FULL_FLUSH && flush !== Z_FINISH && + flush !== Z_BLOCK + ) { + throw new Error("Invalid flush value"); + } + + if (input == null) { + input = Buffer.alloc(0); + in_len = 0; + in_off = 0; + } + + this.strm.avail_in = in_len; + this.strm.input = input; + this.strm.next_in = in_off; + this.strm.avail_out = out_len; + this.strm.output = out; + this.strm.next_out = out_off; + this.flush = flush; + + if (!async) { + // sync version + this._process(); + + if (this._checkError()) { + return this._afterSync(); + } + return; + } + + // async version + var self = this; + nextTick(function () { + self._process(); + self._after(); + }); + + return this; +}; + +Zlib.prototype._afterSync = function () { + var avail_out = this.strm.avail_out; + var avail_in = this.strm.avail_in; + + this.write_in_progress = false; + + return [avail_in, avail_out]; +}; + +Zlib.prototype._process = function () { + var next_expected_header_byte = null; + + // If the avail_out is left at 0, then it means that it ran out + // of room. If there was avail_out left over, then it means + // that all of the input was consumed. + switch (this.mode) { + case DEFLATE: + case GZIP: + case DEFLATERAW: + this.err = zlib_deflate.deflate(this.strm, this.flush); + break; + case UNZIP: + if (this.strm.avail_in > 0) { + next_expected_header_byte = this.strm.next_in; + } + + switch (this.gzip_id_bytes_read) { + case 0: + if (next_expected_header_byte === null) { + break; + } + + if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID1) { + this.gzip_id_bytes_read = 1; + next_expected_header_byte++; + + if (this.strm.avail_in === 1) { + // The only available byte was already read. + break; + } + } else { + this.mode = INFLATE; + break; + } + + // fallthrough + + case 1: + if (next_expected_header_byte === null) { + break; + } + + if (this.strm.input[next_expected_header_byte] === GZIP_HEADER_ID2) { + this.gzip_id_bytes_read = 2; + this.mode = GUNZIP; + } else { + // There is no actual difference between INFLATE and INFLATERAW + // (after initialization). + this.mode = INFLATE; + } + + break; + default: + throw new Error("invalid number of gzip magic number bytes read"); + } + + // fallthrough + + case INFLATE: + case GUNZIP: + case INFLATERAW: + this.err = zlib_inflate.inflate(this.strm, this.flush); + + // If data was encoded with dictionary + if (this.err === Z_NEED_DICT && this.dictionary) { + // Load it + this.err = zlib_inflate.inflateSetDictionary( + this.strm, + this.dictionary, + ); + if (this.err === Z_OK) { + // And try to decode again + this.err = zlib_inflate.inflate(this.strm, this.flush); + } else if (this.err === Z_DATA_ERROR) { + // Both inflateSetDictionary() and inflate() return Z_DATA_ERROR. + // Make it possible for After() to tell a bad dictionary from bad + // input. + this.err = Z_NEED_DICT; + } + } + while ( + this.strm.avail_in > 0 && this.mode === GUNZIP && + this.err === Z_STREAM_END && this.strm.next_in[0] !== 0x00 + ) { + // Bytes remain in input buffer. Perhaps this is another compressed + // member in the same archive, or just trailing garbage. + // Trailing zero bytes are okay, though, since they are frequently + // used for padding. + + this.reset(); + this.err = zlib_inflate.inflate(this.strm, this.flush); + } + break; + default: + throw new Error("Unknown mode " + this.mode); + } +}; + +Zlib.prototype._checkError = function () { + // Acceptable error states depend on the type of zlib stream. + switch (this.err) { + case Z_OK: + case Z_BUF_ERROR: + if (this.strm.avail_out !== 0 && this.flush === Z_FINISH) { + this._error("unexpected end of file"); + return false; + } + break; + case Z_STREAM_END: + // normal statuses, not fatal + break; + case Z_NEED_DICT: + if (this.dictionary == null) { + this._error("Missing dictionary"); + } else { + this._error("Bad dictionary"); + } + return false; + default: + // something else. + this._error("Zlib error"); + return false; + } + + return true; +}; + +Zlib.prototype._after = function () { + if (!this._checkError()) { + return; + } + + var avail_out = this.strm.avail_out; + var avail_in = this.strm.avail_in; + + this.write_in_progress = false; + + // call the write() cb + this.callback(avail_in, avail_out); + + if (this.pending_close) { + this.close(); + } +}; + +Zlib.prototype._error = function (message) { + if (this.strm.msg) { + message = this.strm.msg; + } + this.onerror(message, this.err); + + // no hope of rescue. + this.write_in_progress = false; + if (this.pending_close) { + this.close(); + } +}; + +Zlib.prototype.init = function ( + windowBits, + level, + memLevel, + strategy, + dictionary, +) { + assert( + arguments.length === 4 || arguments.length === 5, + "init(windowBits, level, memLevel, strategy, [dictionary])", + ); + + assert(windowBits >= 8 && windowBits <= 15, "invalid windowBits"); + assert(level >= -1 && level <= 9, "invalid compression level"); + + assert(memLevel >= 1 && memLevel <= 9, "invalid memlevel"); + + assert( + strategy === Z_FILTERED || strategy === Z_HUFFMAN_ONLY || + strategy === Z_RLE || strategy === Z_FIXED || + strategy === Z_DEFAULT_STRATEGY, + "invalid strategy", + ); + + this._init(level, windowBits, memLevel, strategy, dictionary); + this._setDictionary(); +}; + +Zlib.prototype.params = function () { + throw new Error("deflateParams Not supported"); +}; + +Zlib.prototype.reset = function () { + this._reset(); + this._setDictionary(); +}; + +Zlib.prototype._init = function ( + level, + windowBits, + memLevel, + strategy, + dictionary, +) { + this.level = level; + this.windowBits = windowBits; + this.memLevel = memLevel; + this.strategy = strategy; + + this.flush = Z_NO_FLUSH; + + this.err = Z_OK; + + if (this.mode === GZIP || this.mode === GUNZIP) { + this.windowBits += 16; + } + + if (this.mode === UNZIP) { + this.windowBits += 32; + } + + if (this.mode === DEFLATERAW || this.mode === INFLATERAW) { + this.windowBits = -1 * this.windowBits; + } + + this.strm = new Zstream(); + + switch (this.mode) { + case DEFLATE: + case GZIP: + case DEFLATERAW: + this.err = zlib_deflate.deflateInit2( + this.strm, + this.level, + Z_DEFLATED, + this.windowBits, + this.memLevel, + this.strategy, + ); + break; + case INFLATE: + case GUNZIP: + case INFLATERAW: + case UNZIP: + this.err = zlib_inflate.inflateInit2(this.strm, this.windowBits); + break; + default: + throw new Error("Unknown mode " + this.mode); + } + + if (this.err !== Z_OK) { + this._error("Init error"); + } + + this.dictionary = dictionary; + + this.write_in_progress = false; + this.init_done = true; +}; + +Zlib.prototype._setDictionary = function () { + if (this.dictionary == null) { + return; + } + + this.err = Z_OK; + + switch (this.mode) { + case DEFLATE: + case DEFLATERAW: + this.err = zlib_deflate.deflateSetDictionary(this.strm, this.dictionary); + break; + default: + break; + } + + if (this.err !== Z_OK) { + this._error("Failed to set dictionary"); + } +}; + +Zlib.prototype._reset = function () { + this.err = Z_OK; + + switch (this.mode) { + case DEFLATE: + case DEFLATERAW: + case GZIP: + this.err = zlib_deflate.deflateReset(this.strm); + break; + case INFLATE: + case INFLATERAW: + case GUNZIP: + this.err = zlib_inflate.inflateReset(this.strm); + break; + default: + break; + } + + if (this.err !== Z_OK) { + this._error("Failed to reset stream"); + } +}; + +export { Zlib }; diff --git a/ext/node/polyfills/assert.ts b/ext/node/polyfills/assert.ts new file mode 100644 index 0000000000..2d5e86e587 --- /dev/null +++ b/ext/node/polyfills/assert.ts @@ -0,0 +1,940 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file ban-types +import { + AssertionError, + AssertionErrorConstructorOptions, +} from "internal:deno_node/polyfills/assertion_error.ts"; +import * as asserts from "internal:deno_node/polyfills/_util/std_asserts.ts"; +import { inspect } from "internal:deno_node/polyfills/util.ts"; +import { + ERR_AMBIGUOUS_ARGUMENT, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_RETURN_VALUE, + ERR_MISSING_ARGS, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { isDeepEqual } from "internal:deno_node/polyfills/internal/util/comparisons.ts"; + +function innerFail(obj: { + actual?: unknown; + expected?: unknown; + message?: string | Error; + operator?: string; +}) { + if (obj.message instanceof Error) { + throw obj.message; + } + + throw new AssertionError({ + actual: obj.actual, + expected: obj.expected, + message: obj.message, + operator: obj.operator, + }); +} + +interface ExtendedAssertionErrorConstructorOptions + extends AssertionErrorConstructorOptions { + generatedMessage?: boolean; +} + +// TODO(uki00a): This function is a workaround for setting the `generatedMessage` property flexibly. +function createAssertionError( + options: ExtendedAssertionErrorConstructorOptions, +): AssertionError { + const error = new AssertionError(options); + if (options.generatedMessage) { + error.generatedMessage = true; + } + return error; +} + +/** Converts the std assertion error to node.js assertion error */ +function toNode( + fn: () => void, + opts?: { + actual: unknown; + expected: unknown; + message?: string | Error; + operator?: string; + }, +) { + const { operator, message, actual, expected } = opts || {}; + try { + fn(); + } catch (e) { + if (e instanceof asserts.AssertionError) { + if (typeof message === "string") { + throw new AssertionError({ + operator, + message, + actual, + expected, + }); + } else if (message instanceof Error) { + throw message; + } else { + throw new AssertionError({ + operator, + message: e.message, + actual, + expected, + }); + } + } + throw e; + } +} + +function assert(actual: unknown, message?: string | Error): asserts actual { + if (arguments.length === 0) { + throw new AssertionError({ + message: "No value argument passed to `assert.ok()`", + }); + } + toNode( + () => asserts.assert(actual), + { message, actual, expected: true }, + ); +} +const ok = assert; + +function throws( + fn: () => void, + error?: RegExp | Function | Error, + message?: string, +) { + // Check arg types + if (typeof fn !== "function") { + throw new ERR_INVALID_ARG_TYPE("fn", "function", fn); + } + if ( + typeof error === "object" && error !== null && + Object.getPrototypeOf(error) === Object.prototype && + Object.keys(error).length === 0 + ) { + // error is an empty object + throw new ERR_INVALID_ARG_VALUE( + "error", + error, + "may not be an empty object", + ); + } + if (typeof message === "string") { + if ( + !(error instanceof RegExp) && typeof error !== "function" && + !(error instanceof Error) && typeof error !== "object" + ) { + throw new ERR_INVALID_ARG_TYPE("error", [ + "Function", + "Error", + "RegExp", + "Object", + ], error); + } + } else { + if ( + typeof error !== "undefined" && typeof error !== "string" && + !(error instanceof RegExp) && typeof error !== "function" && + !(error instanceof Error) && typeof error !== "object" + ) { + throw new ERR_INVALID_ARG_TYPE("error", [ + "Function", + "Error", + "RegExp", + "Object", + ], error); + } + } + + // Checks test function + try { + fn(); + } catch (e) { + if ( + validateThrownError(e, error, message, { + operator: throws, + }) + ) { + return; + } + } + if (message) { + let msg = `Missing expected exception: ${message}`; + if (typeof error === "function" && error?.name) { + msg = `Missing expected exception (${error.name}): ${message}`; + } + throw new AssertionError({ + message: msg, + operator: "throws", + actual: undefined, + expected: error, + }); + } else if (typeof error === "string") { + // Use case of throws(fn, message) + throw new AssertionError({ + message: `Missing expected exception: ${error}`, + operator: "throws", + actual: undefined, + expected: undefined, + }); + } else if (typeof error === "function" && error?.prototype !== undefined) { + throw new AssertionError({ + message: `Missing expected exception (${error.name}).`, + operator: "throws", + actual: undefined, + expected: error, + }); + } else { + throw new AssertionError({ + message: "Missing expected exception.", + operator: "throws", + actual: undefined, + expected: error, + }); + } +} + +function doesNotThrow( + fn: () => void, + message?: string, +): void; +function doesNotThrow( + fn: () => void, + error?: Function, + message?: string | Error, +): void; +function doesNotThrow( + fn: () => void, + error?: RegExp, + message?: string, +): void; +function doesNotThrow( + fn: () => void, + expected?: Function | RegExp | string, + message?: string | Error, +) { + // Check arg type + if (typeof fn !== "function") { + throw new ERR_INVALID_ARG_TYPE("fn", "function", fn); + } else if ( + !(expected instanceof RegExp) && typeof expected !== "function" && + typeof expected !== "string" && typeof expected !== "undefined" + ) { + throw new ERR_INVALID_ARG_TYPE("expected", ["Function", "RegExp"], fn); + } + + // Checks test function + try { + fn(); + } catch (e) { + gotUnwantedException(e, expected, message, doesNotThrow); + } +} + +function equal( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + if (actual == expected) { + return; + } + + if (Number.isNaN(actual) && Number.isNaN(expected)) { + return; + } + + if (typeof message === "string") { + throw new AssertionError({ + message, + }); + } else if (message instanceof Error) { + throw message; + } + + toNode( + () => asserts.assertStrictEquals(actual, expected), + { + message: message || `${actual} == ${expected}`, + operator: "==", + actual, + expected, + }, + ); +} +function notEqual( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + if (Number.isNaN(actual) && Number.isNaN(expected)) { + throw new AssertionError({ + message: `${actual} != ${expected}`, + operator: "!=", + actual, + expected, + }); + } + if (actual != expected) { + return; + } + + if (typeof message === "string") { + throw new AssertionError({ + message, + }); + } else if (message instanceof Error) { + throw message; + } + + toNode( + () => asserts.assertNotStrictEquals(actual, expected), + { + message: message || `${actual} != ${expected}`, + operator: "!=", + actual, + expected, + }, + ); +} +function strictEqual( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + toNode( + () => asserts.assertStrictEquals(actual, expected), + { message, operator: "strictEqual", actual, expected }, + ); +} +function notStrictEqual( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + toNode( + () => asserts.assertNotStrictEquals(actual, expected), + { message, actual, expected, operator: "notStrictEqual" }, + ); +} + +function deepEqual( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + if (!isDeepEqual(actual, expected)) { + innerFail({ actual, expected, message, operator: "deepEqual" }); + } +} +function notDeepEqual( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + if (isDeepEqual(actual, expected)) { + innerFail({ actual, expected, message, operator: "notDeepEqual" }); + } +} +function deepStrictEqual( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + toNode( + () => asserts.assertEquals(actual, expected), + { message, actual, expected, operator: "deepStrictEqual" }, + ); +} +function notDeepStrictEqual( + actual: unknown, + expected: unknown, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "expected"); + } + + toNode( + () => asserts.assertNotEquals(actual, expected), + { message, actual, expected, operator: "deepNotStrictEqual" }, + ); +} + +function fail(message?: string | Error): never { + if (typeof message === "string" || message == null) { + throw createAssertionError({ + message: message ?? "Failed", + operator: "fail", + generatedMessage: message == null, + }); + } else { + throw message; + } +} +function match(actual: string, regexp: RegExp, message?: string | Error) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("actual", "regexp"); + } + if (!(regexp instanceof RegExp)) { + throw new ERR_INVALID_ARG_TYPE("regexp", "RegExp", regexp); + } + + toNode( + () => asserts.assertMatch(actual, regexp), + { message, actual, expected: regexp, operator: "match" }, + ); +} + +function doesNotMatch( + string: string, + regexp: RegExp, + message?: string | Error, +) { + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("string", "regexp"); + } + if (!(regexp instanceof RegExp)) { + throw new ERR_INVALID_ARG_TYPE("regexp", "RegExp", regexp); + } + if (typeof string !== "string") { + if (message instanceof Error) { + throw message; + } + throw new AssertionError({ + message: message || + `The "string" argument must be of type string. Received type ${typeof string} (${ + inspect(string) + })`, + actual: string, + expected: regexp, + operator: "doesNotMatch", + }); + } + + toNode( + () => asserts.assertNotMatch(string, regexp), + { message, actual: string, expected: regexp, operator: "doesNotMatch" }, + ); +} + +function strict(actual: unknown, message?: string | Error): asserts actual { + if (arguments.length === 0) { + throw new AssertionError({ + message: "No value argument passed to `assert.ok()`", + }); + } + assert(actual, message); +} + +function rejects( + // deno-lint-ignore no-explicit-any + asyncFn: Promise | (() => Promise), + error?: RegExp | Function | Error, +): Promise; + +function rejects( + // deno-lint-ignore no-explicit-any + asyncFn: Promise | (() => Promise), + message?: string, +): Promise; + +// Intentionally avoid using async/await because test-assert-async.js requires it +function rejects( + // deno-lint-ignore no-explicit-any + asyncFn: Promise | (() => Promise), + error?: RegExp | Function | Error | string, + message?: string, +) { + let promise: Promise; + if (typeof asyncFn === "function") { + try { + promise = asyncFn(); + } catch (err) { + // If `asyncFn` throws an error synchronously, this function returns a rejected promise. + return Promise.reject(err); + } + + if (!isValidThenable(promise)) { + return Promise.reject( + new ERR_INVALID_RETURN_VALUE( + "instance of Promise", + "promiseFn", + promise, + ), + ); + } + } else if (!isValidThenable(asyncFn)) { + return Promise.reject( + new ERR_INVALID_ARG_TYPE("promiseFn", ["function", "Promise"], asyncFn), + ); + } else { + promise = asyncFn; + } + + function onFulfilled() { + let message = "Missing expected rejection"; + if (typeof error === "string") { + message += `: ${error}`; + } else if (typeof error === "function" && error.prototype !== undefined) { + message += ` (${error.name}).`; + } else { + message += "."; + } + return Promise.reject(createAssertionError({ + message, + operator: "rejects", + generatedMessage: true, + })); + } + + // deno-lint-ignore camelcase + function rejects_onRejected(e: Error) { // TODO(uki00a): In order to `test-assert-async.js` pass, intentionally adds `rejects_` as a prefix. + if ( + validateThrownError(e, error, message, { + operator: rejects, + validationFunctionName: "validate", + }) + ) { + return; + } + } + + return promise.then(onFulfilled, rejects_onRejected); +} + +function doesNotReject( + // deno-lint-ignore no-explicit-any + asyncFn: Promise | (() => Promise), + error?: RegExp | Function, +): Promise; + +function doesNotReject( + // deno-lint-ignore no-explicit-any + asyncFn: Promise | (() => Promise), + message?: string, +): Promise; + +// Intentionally avoid using async/await because test-assert-async.js requires it +function doesNotReject( + // deno-lint-ignore no-explicit-any + asyncFn: Promise | (() => Promise), + error?: RegExp | Function | string, + message?: string, +) { + // deno-lint-ignore no-explicit-any + let promise: Promise; + if (typeof asyncFn === "function") { + try { + const value = asyncFn(); + if (!isValidThenable(value)) { + return Promise.reject( + new ERR_INVALID_RETURN_VALUE( + "instance of Promise", + "promiseFn", + value, + ), + ); + } + promise = value; + } catch (e) { + // If `asyncFn` throws an error synchronously, this function returns a rejected promise. + return Promise.reject(e); + } + } else if (!isValidThenable(asyncFn)) { + return Promise.reject( + new ERR_INVALID_ARG_TYPE("promiseFn", ["function", "Promise"], asyncFn), + ); + } else { + promise = asyncFn; + } + + return promise.then( + () => {}, + (e) => gotUnwantedException(e, error, message, doesNotReject), + ); +} + +function gotUnwantedException( + // deno-lint-ignore no-explicit-any + e: any, + expected: RegExp | Function | string | null | undefined, + message: string | Error | null | undefined, + operator: Function, +): never { + if (typeof expected === "string") { + // The use case of doesNotThrow(fn, message); + throw new AssertionError({ + message: + `Got unwanted exception: ${expected}\nActual message: "${e.message}"`, + operator: operator.name, + }); + } else if ( + typeof expected === "function" && expected.prototype !== undefined + ) { + // The use case of doesNotThrow(fn, Error, message); + if (e instanceof expected) { + let msg = `Got unwanted exception: ${e.constructor?.name}`; + if (message) { + msg += ` ${String(message)}`; + } + throw new AssertionError({ + message: msg, + operator: operator.name, + }); + } else if (expected.prototype instanceof Error) { + throw e; + } else { + const result = expected(e); + if (result === true) { + let msg = `Got unwanted rejection.\nActual message: "${e.message}"`; + if (message) { + msg += ` ${String(message)}`; + } + throw new AssertionError({ + message: msg, + operator: operator.name, + }); + } + } + throw e; + } else { + if (message) { + throw new AssertionError({ + message: `Got unwanted exception: ${message}\nActual message: "${ + e ? e.message : String(e) + }"`, + operator: operator.name, + }); + } + throw new AssertionError({ + message: `Got unwanted exception.\nActual message: "${ + e ? e.message : String(e) + }"`, + operator: operator.name, + }); + } +} + +/** + * Throws `value` if the value is not `null` or `undefined`. + * + * @param err + */ +// deno-lint-ignore no-explicit-any +function ifError(err: any) { + if (err !== null && err !== undefined) { + let message = "ifError got unwanted exception: "; + + if (typeof err === "object" && typeof err.message === "string") { + if (err.message.length === 0 && err.constructor) { + message += err.constructor.name; + } else { + message += err.message; + } + } else { + message += inspect(err); + } + + const newErr = new AssertionError({ + actual: err, + expected: null, + operator: "ifError", + message, + stackStartFn: ifError, + }); + + // Make sure we actually have a stack trace! + const origStack = err.stack; + + if (typeof origStack === "string") { + // This will remove any duplicated frames from the error frames taken + // from within `ifError` and add the original error frames to the newly + // created ones. + const tmp2 = origStack.split("\n"); + tmp2.shift(); + + // Filter all frames existing in err.stack. + let tmp1 = newErr!.stack?.split("\n"); + + for (const errFrame of tmp2) { + // Find the first occurrence of the frame. + const pos = tmp1?.indexOf(errFrame); + + if (pos !== -1) { + // Only keep new frames. + tmp1 = tmp1?.slice(0, pos); + + break; + } + } + + newErr.stack = `${tmp1?.join("\n")}\n${tmp2.join("\n")}`; + } + + throw newErr; + } +} + +interface ValidateThrownErrorOptions { + operator: Function; + validationFunctionName?: string; +} + +function validateThrownError( + // deno-lint-ignore no-explicit-any + e: any, + error: RegExp | Function | Error | string | null | undefined, + message: string | undefined | null, + options: ValidateThrownErrorOptions, +): boolean { + if (typeof error === "string") { + if (message != null) { + throw new ERR_INVALID_ARG_TYPE( + "error", + ["Object", "Error", "Function", "RegExp"], + error, + ); + } else if (typeof e === "object" && e !== null) { + if (e.message === error) { + throw new ERR_AMBIGUOUS_ARGUMENT( + "error/message", + `The error message "${e.message}" is identical to the message.`, + ); + } + } else if (e === error) { + throw new ERR_AMBIGUOUS_ARGUMENT( + "error/message", + `The error "${e}" is identical to the message.`, + ); + } + message = error; + error = undefined; + } + if ( + error instanceof Function && error.prototype !== undefined && + error.prototype instanceof Error + ) { + // error is a constructor + if (e instanceof error) { + return true; + } + throw createAssertionError({ + message: + `The error is expected to be an instance of "${error.name}". Received "${e?.constructor?.name}"\n\nError message:\n\n${e?.message}`, + actual: e, + expected: error, + operator: options.operator.name, + generatedMessage: true, + }); + } + if (error instanceof Function) { + const received = error(e); + if (received === true) { + return true; + } + throw createAssertionError({ + message: `The ${ + options.validationFunctionName + ? `"${options.validationFunctionName}" validation` + : "validation" + } function is expected to return "true". Received ${ + inspect(received) + }\n\nCaught error:\n\n${e}`, + actual: e, + expected: error, + operator: options.operator.name, + generatedMessage: true, + }); + } + if (error instanceof RegExp) { + if (error.test(String(e))) { + return true; + } + throw createAssertionError({ + message: + `The input did not match the regular expression ${error.toString()}. Input:\n\n'${ + String(e) + }'\n`, + actual: e, + expected: error, + operator: options.operator.name, + generatedMessage: true, + }); + } + if (typeof error === "object" && error !== null) { + const keys = Object.keys(error); + if (error instanceof Error) { + keys.push("name", "message"); + } + for (const k of keys) { + if (e == null) { + throw createAssertionError({ + message: message || "object is expected to thrown, but got null", + actual: e, + expected: error, + operator: options.operator.name, + generatedMessage: message == null, + }); + } + + if (typeof e === "string") { + throw createAssertionError({ + message: message || + `object is expected to thrown, but got string: ${e}`, + actual: e, + expected: error, + operator: options.operator.name, + generatedMessage: message == null, + }); + } + if (typeof e === "number") { + throw createAssertionError({ + message: message || + `object is expected to thrown, but got number: ${e}`, + actual: e, + expected: error, + operator: options.operator.name, + generatedMessage: message == null, + }); + } + if (!(k in e)) { + throw createAssertionError({ + message: message || `A key in the expected object is missing: ${k}`, + actual: e, + expected: error, + operator: options.operator.name, + generatedMessage: message == null, + }); + } + const actual = e[k]; + // deno-lint-ignore no-explicit-any + const expected = (error as any)[k]; + if (typeof actual === "string" && expected instanceof RegExp) { + match(actual, expected); + } else { + deepStrictEqual(actual, expected); + } + } + return true; + } + if (typeof error === "undefined") { + return true; + } + throw createAssertionError({ + message: `Invalid expectation: ${error}`, + operator: options.operator.name, + generatedMessage: true, + }); +} + +// deno-lint-ignore no-explicit-any +function isValidThenable(maybeThennable: any): boolean { + if (!maybeThennable) { + return false; + } + + if (maybeThennable instanceof Promise) { + return true; + } + + const isThenable = typeof maybeThennable.then === "function" && + typeof maybeThennable.catch === "function"; + + return isThenable && typeof maybeThennable !== "function"; +} + +Object.assign(strict, { + AssertionError, + deepEqual: deepStrictEqual, + deepStrictEqual, + doesNotMatch, + doesNotReject, + doesNotThrow, + equal: strictEqual, + fail, + ifError, + match, + notDeepEqual: notDeepStrictEqual, + notDeepStrictEqual, + notEqual: notStrictEqual, + notStrictEqual, + ok, + rejects, + strict, + strictEqual, + throws, +}); + +export default Object.assign(assert, { + AssertionError, + deepEqual, + deepStrictEqual, + doesNotMatch, + doesNotReject, + doesNotThrow, + equal, + fail, + ifError, + match, + notDeepEqual, + notDeepStrictEqual, + notEqual, + notStrictEqual, + ok, + rejects, + strict, + strictEqual, + throws, +}); + +export { + AssertionError, + deepEqual, + deepStrictEqual, + doesNotMatch, + doesNotReject, + doesNotThrow, + equal, + fail, + ifError, + match, + notDeepEqual, + notDeepStrictEqual, + notEqual, + notStrictEqual, + ok, + rejects, + strict, + strictEqual, + throws, +}; diff --git a/ext/node/polyfills/assert/strict.ts b/ext/node/polyfills/assert/strict.ts new file mode 100644 index 0000000000..eab0f4e78d --- /dev/null +++ b/ext/node/polyfills/assert/strict.ts @@ -0,0 +1,26 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { strict } from "internal:deno_node/polyfills/assert.ts"; + +export { + AssertionError, + deepEqual, + deepStrictEqual, + doesNotMatch, + doesNotReject, + doesNotThrow, + equal, + fail, + ifError, + match, + notDeepEqual, + notDeepStrictEqual, + notEqual, + notStrictEqual, + ok, + rejects, + strictEqual, + throws, +} from "internal:deno_node/polyfills/assert.ts"; + +export { strict }; +export default strict; diff --git a/ext/node/polyfills/assertion_error.ts b/ext/node/polyfills/assertion_error.ts new file mode 100644 index 0000000000..f2e221f202 --- /dev/null +++ b/ext/node/polyfills/assertion_error.ts @@ -0,0 +1,579 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// Adapted from Node.js. 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. + +import { inspect } from "internal:deno_node/polyfills/util.ts"; +import { stripColor as removeColors } from "internal:deno_node/polyfills/_util/std_fmt_colors.ts"; + +function getConsoleWidth(): number { + try { + return Deno.consoleSize().columns; + } catch { + return 80; + } +} + +// TODO(schwarzkopfb): we should implement Node's concept of "primordials" +// Ref: https://github.com/denoland/deno/issues/6040#issuecomment-637305828 +const MathMax = Math.max; +const { Error } = globalThis; +const { + create: ObjectCreate, + defineProperty: ObjectDefineProperty, + getPrototypeOf: ObjectGetPrototypeOf, + getOwnPropertyDescriptor: ObjectGetOwnPropertyDescriptor, + keys: ObjectKeys, +} = Object; + +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; + +let blue = ""; +let green = ""; +let red = ""; +let defaultColor = ""; + +const kReadableOperator: { [key: string]: string } = { + deepStrictEqual: "Expected values to be strictly deep-equal:", + strictEqual: "Expected values to be strictly equal:", + strictEqualObject: 'Expected "actual" to be reference-equal to "expected":', + deepEqual: "Expected values to be loosely deep-equal:", + notDeepStrictEqual: 'Expected "actual" not to be strictly deep-equal to:', + notStrictEqual: 'Expected "actual" to be strictly unequal to:', + notStrictEqualObject: + 'Expected "actual" not to be reference-equal to "expected":', + notDeepEqual: 'Expected "actual" not to be loosely deep-equal to:', + notIdentical: "Values have same structure but are not reference-equal:", + notDeepEqualUnequal: "Expected values not to be loosely deep-equal:", +}; + +// Comparing short primitives should just show === / !== instead of using the +// diff. +const kMaxShortLength = 12; + +export function copyError(source: Error): Error { + const keys = ObjectKeys(source); + const target = ObjectCreate(ObjectGetPrototypeOf(source)); + for (const key of keys) { + const desc = ObjectGetOwnPropertyDescriptor(source, key); + + if (desc !== undefined) { + ObjectDefineProperty(target, key, desc); + } + } + ObjectDefineProperty(target, "message", { value: source.message }); + return target; +} + +export function inspectValue(val: unknown): string { + // The util.inspect default values could be changed. This makes sure the + // error messages contain the necessary information nevertheless. + return inspect( + val, + { + compact: true, + customInspect: false, + depth: 1000, + maxArrayLength: Infinity, + // Assert compares only enumerable properties (with a few exceptions). + showHidden: false, + // Assert does not detect proxies currently. + showProxy: false, + sorted: true, + // Inspect getters as we also check them when comparing entries. + getters: true, + }, + ); +} + +export function createErrDiff( + actual: unknown, + expected: unknown, + operator: string, +): string { + let other = ""; + let res = ""; + let end = ""; + let skipped = false; + const actualInspected = inspectValue(actual); + const actualLines = actualInspected.split("\n"); + const expectedLines = inspectValue(expected).split("\n"); + + let i = 0; + let indicator = ""; + + // In case both values are objects or functions explicitly mark them as not + // reference equal for the `strictEqual` operator. + if ( + operator === "strictEqual" && + ((typeof actual === "object" && actual !== null && + typeof expected === "object" && expected !== null) || + (typeof actual === "function" && typeof expected === "function")) + ) { + operator = "strictEqualObject"; + } + + // If "actual" and "expected" fit on a single line and they are not strictly + // equal, check further special handling. + if ( + actualLines.length === 1 && expectedLines.length === 1 && + actualLines[0] !== expectedLines[0] + ) { + // Check for the visible length using the `removeColors()` function, if + // appropriate. + const c = inspect.defaultOptions.colors; + const actualRaw = c ? removeColors(actualLines[0]) : actualLines[0]; + const expectedRaw = c ? removeColors(expectedLines[0]) : expectedLines[0]; + const inputLength = actualRaw.length + expectedRaw.length; + // If the character length of "actual" and "expected" together is less than + // kMaxShortLength and if neither is an object and at least one of them is + // not `zero`, use the strict equal comparison to visualize the output. + if (inputLength <= kMaxShortLength) { + if ( + (typeof actual !== "object" || actual === null) && + (typeof expected !== "object" || expected === null) && + (actual !== 0 || expected !== 0) + ) { // -0 === +0 + return `${kReadableOperator[operator]}\n\n` + + `${actualLines[0]} !== ${expectedLines[0]}\n`; + } + } else if (operator !== "strictEqualObject") { + // If the stderr is a tty and the input length is lower than the current + // columns per line, add a mismatch indicator below the output. If it is + // not a tty, use a default value of 80 characters. + const maxLength = Deno.isatty(Deno.stderr.rid) ? getConsoleWidth() : 80; + if (inputLength < maxLength) { + while (actualRaw[i] === expectedRaw[i]) { + i++; + } + // Ignore the first characters. + if (i > 2) { + // Add position indicator for the first mismatch in case it is a + // single line and the input length is less than the column length. + indicator = `\n ${" ".repeat(i)}^`; + i = 0; + } + } + } + } + + // Remove all ending lines that match (this optimizes the output for + // readability by reducing the number of total changed lines). + let a = actualLines[actualLines.length - 1]; + let b = expectedLines[expectedLines.length - 1]; + while (a === b) { + if (i++ < 3) { + end = `\n ${a}${end}`; + } else { + other = a; + } + actualLines.pop(); + expectedLines.pop(); + if (actualLines.length === 0 || expectedLines.length === 0) { + break; + } + a = actualLines[actualLines.length - 1]; + b = expectedLines[expectedLines.length - 1]; + } + + const maxLines = MathMax(actualLines.length, expectedLines.length); + // Strict equal with identical objects that are not identical by reference. + // E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() }) + if (maxLines === 0) { + // We have to get the result again. The lines were all removed before. + const actualLines = actualInspected.split("\n"); + + // Only remove lines in case it makes sense to collapse those. + if (actualLines.length > 50) { + actualLines[46] = `${blue}...${defaultColor}`; + while (actualLines.length > 47) { + actualLines.pop(); + } + } + + return `${kReadableOperator.notIdentical}\n\n${actualLines.join("\n")}\n`; + } + + // There were at least five identical lines at the end. Mark a couple of + // skipped. + if (i >= 5) { + end = `\n${blue}...${defaultColor}${end}`; + skipped = true; + } + if (other !== "") { + end = `\n ${other}${end}`; + other = ""; + } + + let printedLines = 0; + let identical = 0; + const msg = kReadableOperator[operator] + + `\n${green}+ actual${defaultColor} ${red}- expected${defaultColor}`; + const skippedMsg = ` ${blue}...${defaultColor} Lines skipped`; + + let lines = actualLines; + let plusMinus = `${green}+${defaultColor}`; + let maxLength = expectedLines.length; + if (actualLines.length < maxLines) { + lines = expectedLines; + plusMinus = `${red}-${defaultColor}`; + maxLength = actualLines.length; + } + + for (i = 0; i < maxLines; i++) { + if (maxLength < i + 1) { + // If more than two former lines are identical, print them. Collapse them + // in case more than five lines were identical. + if (identical > 2) { + if (identical > 3) { + if (identical > 4) { + if (identical === 5) { + res += `\n ${lines[i - 3]}`; + printedLines++; + } else { + res += `\n${blue}...${defaultColor}`; + skipped = true; + } + } + res += `\n ${lines[i - 2]}`; + printedLines++; + } + res += `\n ${lines[i - 1]}`; + printedLines++; + } + // No identical lines before. + identical = 0; + // Add the expected line to the cache. + if (lines === actualLines) { + res += `\n${plusMinus} ${lines[i]}`; + } else { + other += `\n${plusMinus} ${lines[i]}`; + } + printedLines++; + // Only extra actual lines exist + // Lines diverge + } else { + const expectedLine = expectedLines[i]; + let actualLine = actualLines[i]; + // If the lines diverge, specifically check for lines that only diverge by + // a trailing comma. In that case it is actually identical and we should + // mark it as such. + let divergingLines = actualLine !== expectedLine && + (!actualLine.endsWith(",") || + actualLine.slice(0, -1) !== expectedLine); + // If the expected line has a trailing comma but is otherwise identical, + // add a comma at the end of the actual line. Otherwise the output could + // look weird as in: + // + // [ + // 1 // No comma at the end! + // + 2 + // ] + // + if ( + divergingLines && + expectedLine.endsWith(",") && + expectedLine.slice(0, -1) === actualLine + ) { + divergingLines = false; + actualLine += ","; + } + if (divergingLines) { + // If more than two former lines are identical, print them. Collapse + // them in case more than five lines were identical. + if (identical > 2) { + if (identical > 3) { + if (identical > 4) { + if (identical === 5) { + res += `\n ${actualLines[i - 3]}`; + printedLines++; + } else { + res += `\n${blue}...${defaultColor}`; + skipped = true; + } + } + res += `\n ${actualLines[i - 2]}`; + printedLines++; + } + res += `\n ${actualLines[i - 1]}`; + printedLines++; + } + // No identical lines before. + identical = 0; + // Add the actual line to the result and cache the expected diverging + // line so consecutive diverging lines show up as +++--- and not +-+-+-. + res += `\n${green}+${defaultColor} ${actualLine}`; + other += `\n${red}-${defaultColor} ${expectedLine}`; + printedLines += 2; + // Lines are identical + } else { + // Add all cached information to the result before adding other things + // and reset the cache. + res += other; + other = ""; + identical++; + // The very first identical line since the last diverging line is be + // added to the result. + if (identical <= 2) { + res += `\n ${actualLine}`; + printedLines++; + } + } + } + // Inspected object to big (Show ~50 rows max) + if (printedLines > 50 && i < maxLines - 2) { + return `${msg}${skippedMsg}\n${res}\n${blue}...${defaultColor}${other}\n` + + `${blue}...${defaultColor}`; + } + } + + return `${msg}${skipped ? skippedMsg : ""}\n${res}${other}${end}${indicator}`; +} + +export interface AssertionErrorDetailsDescriptor { + message: string; + actual: unknown; + expected: unknown; + operator: string; + stack: Error; +} + +export interface AssertionErrorConstructorOptions { + message?: string; + actual?: unknown; + expected?: unknown; + operator?: string; + details?: AssertionErrorDetailsDescriptor[]; + // deno-lint-ignore ban-types + stackStartFn?: Function; + // Compatibility with older versions. + // deno-lint-ignore ban-types + stackStartFunction?: Function; +} + +interface ErrorWithStackTraceLimit extends ErrorConstructor { + stackTraceLimit: number; +} + +export class AssertionError extends Error { + [key: string]: unknown; + + // deno-lint-ignore constructor-super + constructor(options: AssertionErrorConstructorOptions) { + if (typeof options !== "object" || options === null) { + throw new ERR_INVALID_ARG_TYPE("options", "Object", options); + } + const { + message, + operator, + stackStartFn, + details, + // Compatibility with older versions. + stackStartFunction, + } = options; + let { + actual, + expected, + } = options; + + // TODO(schwarzkopfb): `stackTraceLimit` should be added to `ErrorConstructor` in + // cli/dts/lib.deno.shared_globals.d.ts + const limit = (Error as ErrorWithStackTraceLimit).stackTraceLimit; + (Error as ErrorWithStackTraceLimit).stackTraceLimit = 0; + + if (message != null) { + super(String(message)); + } else { + if (Deno.isatty(Deno.stderr.rid)) { + // Reset on each call to make sure we handle dynamically set environment + // variables correct. + if (Deno.noColor) { + blue = ""; + green = ""; + defaultColor = ""; + red = ""; + } else { + blue = "\u001b[34m"; + green = "\u001b[32m"; + defaultColor = "\u001b[39m"; + red = "\u001b[31m"; + } + } + // Prevent the error stack from being visible by duplicating the error + // in a very close way to the original in case both sides are actually + // instances of Error. + if ( + typeof actual === "object" && actual !== null && + typeof expected === "object" && expected !== null && + "stack" in actual && actual instanceof Error && + "stack" in expected && expected instanceof Error + ) { + actual = copyError(actual); + expected = copyError(expected); + } + + if (operator === "deepStrictEqual" || operator === "strictEqual") { + super(createErrDiff(actual, expected, operator)); + } else if ( + operator === "notDeepStrictEqual" || + operator === "notStrictEqual" + ) { + // In case the objects are equal but the operator requires unequal, show + // the first object and say A equals B + let base = kReadableOperator[operator]; + const res = inspectValue(actual).split("\n"); + + // In case "actual" is an object or a function, it should not be + // reference equal. + if ( + operator === "notStrictEqual" && + ((typeof actual === "object" && actual !== null) || + typeof actual === "function") + ) { + base = kReadableOperator.notStrictEqualObject; + } + + // Only remove lines in case it makes sense to collapse those. + if (res.length > 50) { + res[46] = `${blue}...${defaultColor}`; + while (res.length > 47) { + res.pop(); + } + } + + // Only print a single input. + if (res.length === 1) { + super(`${base}${res[0].length > 5 ? "\n\n" : " "}${res[0]}`); + } else { + super(`${base}\n\n${res.join("\n")}\n`); + } + } else { + let res = inspectValue(actual); + let other = inspectValue(expected); + const knownOperator = kReadableOperator[operator ?? ""]; + if (operator === "notDeepEqual" && res === other) { + res = `${knownOperator}\n\n${res}`; + if (res.length > 1024) { + res = `${res.slice(0, 1021)}...`; + } + super(res); + } else { + if (res.length > 512) { + res = `${res.slice(0, 509)}...`; + } + if (other.length > 512) { + other = `${other.slice(0, 509)}...`; + } + if (operator === "deepEqual") { + res = `${knownOperator}\n\n${res}\n\nshould loosely deep-equal\n\n`; + } else { + const newOp = kReadableOperator[`${operator}Unequal`]; + if (newOp) { + res = `${newOp}\n\n${res}\n\nshould not loosely deep-equal\n\n`; + } else { + other = ` ${operator} ${other}`; + } + } + super(`${res}${other}`); + } + } + } + + (Error as ErrorWithStackTraceLimit).stackTraceLimit = limit; + + this.generatedMessage = !message; + ObjectDefineProperty(this, "name", { + __proto__: null, + value: "AssertionError [ERR_ASSERTION]", + enumerable: false, + writable: true, + configurable: true, + // deno-lint-ignore no-explicit-any + } as any); + this.code = "ERR_ASSERTION"; + + if (details) { + this.actual = undefined; + this.expected = undefined; + this.operator = undefined; + + for (let i = 0; i < details.length; i++) { + this["message " + i] = details[i].message; + this["actual " + i] = details[i].actual; + this["expected " + i] = details[i].expected; + this["operator " + i] = details[i].operator; + this["stack trace " + i] = details[i].stack; + } + } else { + this.actual = actual; + this.expected = expected; + this.operator = operator; + } + + // @ts-ignore this function is not available in lib.dom.d.ts + Error.captureStackTrace(this, stackStartFn || stackStartFunction); + // Create error message including the error code in the name. + this.stack; + // Reset the name. + this.name = "AssertionError"; + } + + override toString() { + return `${this.name} [${this.code}]: ${this.message}`; + } + + [inspect.custom](_recurseTimes: number, ctx: Record) { + // Long strings should not be fully inspected. + const tmpActual = this.actual; + const tmpExpected = this.expected; + + for (const name of ["actual", "expected"]) { + if (typeof this[name] === "string") { + const value = this[name] as string; + const lines = value.split("\n"); + if (lines.length > 10) { + lines.length = 10; + this[name] = `${lines.join("\n")}\n...`; + } else if (value.length > 512) { + this[name] = `${value.slice(512)}...`; + } + } + } + + // This limits the `actual` and `expected` property default inspection to + // the minimum depth. Otherwise those values would be too verbose compared + // to the actual error message which contains a combined view of these two + // input values. + const result = inspect(this, { + ...ctx, + customInspect: false, + depth: 0, + }); + + // Reset the properties after inspection. + this.actual = tmpActual; + this.expected = tmpExpected; + + return result; + } +} + +export default AssertionError; diff --git a/ext/node/polyfills/async_hooks.ts b/ext/node/polyfills/async_hooks.ts new file mode 100644 index 0000000000..295f01ec85 --- /dev/null +++ b/ext/node/polyfills/async_hooks.ts @@ -0,0 +1,331 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +// This implementation is inspired by "workerd" AsyncLocalStorage implementation: +// https://github.com/cloudflare/workerd/blob/77fd0ed6ddba184414f0216508fc62b06e716cab/src/workerd/api/node/async-hooks.c++#L9 + +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { core } from "internal:deno_node/polyfills/_core.ts"; + +function assert(cond: boolean) { + if (!cond) throw new Error("Assertion failed"); +} +const asyncContextStack: AsyncContextFrame[] = []; + +function pushAsyncFrame(frame: AsyncContextFrame) { + asyncContextStack.push(frame); +} + +function popAsyncFrame() { + assert(asyncContextStack.length > 0); + asyncContextStack.pop(); +} + +let rootAsyncFrame: AsyncContextFrame | undefined = undefined; +let promiseHooksSet = false; + +const asyncContext = Symbol("asyncContext"); +function isRejected(promise: Promise) { + const [state] = core.getPromiseDetails(promise); + return state == 2; +} + +function setPromiseHooks() { + if (promiseHooksSet) { + return; + } + promiseHooksSet = true; + + const init = (promise: Promise) => { + const currentFrame = AsyncContextFrame.current(); + if (!currentFrame.isRoot()) { + assert(AsyncContextFrame.tryGetContext(promise) == null); + AsyncContextFrame.attachContext(promise); + } + }; + const before = (promise: Promise) => { + const maybeFrame = AsyncContextFrame.tryGetContext(promise); + if (maybeFrame) { + pushAsyncFrame(maybeFrame); + } else { + pushAsyncFrame(AsyncContextFrame.getRootAsyncContext()); + } + }; + const after = (promise: Promise) => { + popAsyncFrame(); + if (!isRejected(promise)) { + // @ts-ignore promise async context + delete promise[asyncContext]; + } + }; + const resolve = (promise: Promise) => { + const currentFrame = AsyncContextFrame.current(); + if ( + !currentFrame.isRoot() && isRejected(promise) && + AsyncContextFrame.tryGetContext(promise) == null + ) { + AsyncContextFrame.attachContext(promise); + } + }; + + core.setPromiseHooks(init, before, after, resolve); +} + +class AsyncContextFrame { + storage: StorageEntry[]; + constructor( + maybeParent?: AsyncContextFrame | null, + maybeStorageEntry?: StorageEntry | null, + isRoot = false, + ) { + this.storage = []; + + setPromiseHooks(); + + const propagate = (parent: AsyncContextFrame) => { + parent.storage = parent.storage.filter((entry) => !entry.key.isDead()); + parent.storage.forEach((entry) => this.storage.push(entry)); + + if (maybeStorageEntry) { + const existingEntry = this.storage.find((entry) => + entry.key === maybeStorageEntry.key + ); + if (existingEntry) { + existingEntry.value = maybeStorageEntry.value; + } else { + this.storage.push(maybeStorageEntry); + } + } + }; + + if (!isRoot) { + if (maybeParent) { + propagate(maybeParent); + } else { + propagate(AsyncContextFrame.current()); + } + } + } + + static tryGetContext(promise: Promise) { + // @ts-ignore promise async context + return promise[asyncContext]; + } + + static attachContext(promise: Promise) { + assert(!(asyncContext in promise)); + // @ts-ignore promise async context + promise[asyncContext] = AsyncContextFrame.current(); + } + + static getRootAsyncContext() { + if (typeof rootAsyncFrame !== "undefined") { + return rootAsyncFrame; + } + + rootAsyncFrame = new AsyncContextFrame(null, null, true); + return rootAsyncFrame; + } + + static current() { + if (asyncContextStack.length === 0) { + return AsyncContextFrame.getRootAsyncContext(); + } + + return asyncContextStack[asyncContextStack.length - 1]; + } + + static create( + maybeParent?: AsyncContextFrame | null, + maybeStorageEntry?: StorageEntry | null, + ) { + return new AsyncContextFrame(maybeParent, maybeStorageEntry); + } + + static wrap( + fn: () => unknown, + maybeFrame: AsyncContextFrame | undefined, + // deno-lint-ignore no-explicit-any + thisArg: any, + ) { + // deno-lint-ignore no-explicit-any + return (...args: any) => { + const frame = maybeFrame || AsyncContextFrame.current(); + Scope.enter(frame); + try { + return fn.apply(thisArg, args); + } finally { + Scope.exit(); + } + }; + } + + get(key: StorageKey) { + assert(!key.isDead()); + this.storage = this.storage.filter((entry) => !entry.key.isDead()); + const entry = this.storage.find((entry) => entry.key === key); + if (entry) { + return entry.value; + } + return undefined; + } + + isRoot() { + return AsyncContextFrame.getRootAsyncContext() == this; + } +} + +export class AsyncResource { + frame: AsyncContextFrame; + type: string; + constructor(type: string) { + this.type = type; + this.frame = AsyncContextFrame.current(); + } + + runInAsyncScope( + fn: (...args: unknown[]) => unknown, + thisArg: unknown, + ...args: unknown[] + ) { + Scope.enter(this.frame); + + try { + return fn.apply(thisArg, args); + } finally { + Scope.exit(); + } + } + + bind(fn: (...args: unknown[]) => unknown, thisArg = this) { + validateFunction(fn, "fn"); + const frame = AsyncContextFrame.current(); + const bound = AsyncContextFrame.wrap(fn, frame, thisArg); + + Object.defineProperties(bound, { + "length": { + configurable: true, + enumerable: false, + value: fn.length, + writable: false, + }, + "asyncResource": { + configurable: true, + enumerable: true, + value: this, + writable: true, + }, + }); + return bound; + } + + static bind( + fn: (...args: unknown[]) => unknown, + type?: string, + thisArg?: AsyncResource, + ) { + type = type || fn.name; + return (new AsyncResource(type || "AsyncResource")).bind(fn, thisArg); + } +} + +class Scope { + static enter(maybeFrame?: AsyncContextFrame) { + if (maybeFrame) { + pushAsyncFrame(maybeFrame); + } else { + pushAsyncFrame(AsyncContextFrame.getRootAsyncContext()); + } + } + + static exit() { + popAsyncFrame(); + } +} + +class StorageEntry { + key: StorageKey; + value: unknown; + constructor(key: StorageKey, value: unknown) { + this.key = key; + this.value = value; + } +} + +class StorageKey { + #dead = false; + + reset() { + this.#dead = true; + } + + isDead() { + return this.#dead; + } +} + +const fnReg = new FinalizationRegistry((key: StorageKey) => { + key.reset(); +}); + +export class AsyncLocalStorage { + #key; + + constructor() { + this.#key = new StorageKey(); + fnReg.register(this, this.#key); + } + + // deno-lint-ignore no-explicit-any + run(store: any, callback: any, ...args: any[]): any { + const frame = AsyncContextFrame.create( + null, + new StorageEntry(this.#key, store), + ); + Scope.enter(frame); + let res; + try { + res = callback(...args); + } finally { + Scope.exit(); + } + return res; + } + + // deno-lint-ignore no-explicit-any + exit(callback: (...args: unknown[]) => any, ...args: any[]): any { + return this.run(undefined, callback, args); + } + + // deno-lint-ignore no-explicit-any + getStore(): any { + const currentFrame = AsyncContextFrame.current(); + return currentFrame.get(this.#key); + } +} + +export function executionAsyncId() { + return 1; +} + +class AsyncHook { + enable() { + } + + disable() { + } +} + +export function createHook() { + return new AsyncHook(); +} + +// Placing all exports down here because the exported classes won't export +// otherwise. +export default { + // Embedder API + AsyncResource, + executionAsyncId, + createHook, + AsyncLocalStorage, +}; diff --git a/ext/node/polyfills/buffer.ts b/ext/node/polyfills/buffer.ts new file mode 100644 index 0000000000..8a4426c97a --- /dev/null +++ b/ext/node/polyfills/buffer.ts @@ -0,0 +1,13 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// @deno-types="./internal/buffer.d.ts" +export { + atob, + Blob, + btoa, + Buffer, + constants, + default, + kMaxLength, + kStringMaxLength, + SlowBuffer, +} from "internal:deno_node/polyfills/internal/buffer.mjs"; diff --git a/ext/node/polyfills/child_process.ts b/ext/node/polyfills/child_process.ts new file mode 100644 index 0000000000..06269e0251 --- /dev/null +++ b/ext/node/polyfills/child_process.ts @@ -0,0 +1,832 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// This module implements 'child_process' module of Node.JS API. +// ref: https://nodejs.org/api/child_process.html +import { core } from "internal:deno_node/polyfills/_core.ts"; +import { + ChildProcess, + ChildProcessOptions, + normalizeSpawnArguments, + type SpawnOptions, + spawnSync as _spawnSync, + type SpawnSyncOptions, + type SpawnSyncResult, + stdioStringToArray, +} from "internal:deno_node/polyfills/internal/child_process.ts"; +import { + validateAbortSignal, + validateFunction, + validateObject, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + ERR_CHILD_PROCESS_IPC_REQUIRED, + ERR_CHILD_PROCESS_STDIO_MAXBUFFER, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_OUT_OF_RANGE, + genericNodeError, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + ArrayIsArray, + ArrayPrototypeJoin, + ArrayPrototypePush, + ArrayPrototypeSlice, + ObjectAssign, + StringPrototypeSlice, +} from "internal:deno_node/polyfills/internal/primordials.mjs"; +import { + getSystemErrorName, + promisify, +} from "internal:deno_node/polyfills/util.ts"; +import { createDeferredPromise } from "internal:deno_node/polyfills/internal/util.mjs"; +import { process } from "internal:deno_node/polyfills/process.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + convertToValidSignal, + kEmptyObject, +} from "internal:deno_node/polyfills/internal/util.mjs"; + +const MAX_BUFFER = 1024 * 1024; + +type ForkOptions = ChildProcessOptions; + +/** + * Spawns a new Node.js process + fork. + * @param modulePath + * @param args + * @param option + * @returns + */ +export function fork( + modulePath: string, + _args?: string[], + _options?: ForkOptions, +) { + validateString(modulePath, "modulePath"); + + // Get options and args arguments. + let execArgv; + let options: SpawnOptions & { + execArgv?: string; + execPath?: string; + silent?: boolean; + } = {}; + let args: string[] = []; + let pos = 1; + if (pos < arguments.length && Array.isArray(arguments[pos])) { + args = arguments[pos++]; + } + + if (pos < arguments.length && arguments[pos] == null) { + pos++; + } + + if (pos < arguments.length && arguments[pos] != null) { + if (typeof arguments[pos] !== "object") { + throw new ERR_INVALID_ARG_VALUE(`arguments[${pos}]`, arguments[pos]); + } + + options = { ...arguments[pos++] }; + } + + // Prepare arguments for fork: + execArgv = options.execArgv || process.execArgv; + + if (execArgv === process.execArgv && process._eval != null) { + const index = execArgv.lastIndexOf(process._eval); + if (index > 0) { + // Remove the -e switch to avoid fork bombing ourselves. + execArgv = execArgv.slice(0); + execArgv.splice(index - 1, 2); + } + } + + // TODO(bartlomieju): this is incomplete, currently only handling a single + // V8 flag to get Prisma integration running, we should fill this out with + // more + const v8Flags: string[] = []; + if (Array.isArray(execArgv)) { + for (let index = 0; index < execArgv.length; index++) { + const flag = execArgv[index]; + if (flag.startsWith("--max-old-space-size")) { + execArgv.splice(index, 1); + v8Flags.push(flag); + } + } + } + const stringifiedV8Flags: string[] = []; + if (v8Flags.length > 0) { + stringifiedV8Flags.push("--v8-flags=" + v8Flags.join(",")); + } + args = [ + "run", + "--unstable", // TODO(kt3k): Remove when npm: is stable + "--node-modules-dir", + "-A", + ...stringifiedV8Flags, + ...execArgv, + modulePath, + ...args, + ]; + + if (typeof options.stdio === "string") { + options.stdio = stdioStringToArray(options.stdio, "ipc"); + } else if (!Array.isArray(options.stdio)) { + // Use a separate fd=3 for the IPC channel. Inherit stdin, stdout, + // and stderr from the parent if silent isn't set. + options.stdio = stdioStringToArray( + options.silent ? "pipe" : "inherit", + "ipc", + ); + } else if (!options.stdio.includes("ipc")) { + throw new ERR_CHILD_PROCESS_IPC_REQUIRED("options.stdio"); + } + + options.execPath = options.execPath || Deno.execPath(); + options.shell = false; + + Object.assign(options.env ??= {}, { + DENO_DONT_USE_INTERNAL_NODE_COMPAT_STATE: core.ops + .op_npm_process_state(), + }); + + return spawn(options.execPath, args, options); +} + +export function spawn(command: string): ChildProcess; +export function spawn(command: string, options: SpawnOptions): ChildProcess; +export function spawn(command: string, args: string[]): ChildProcess; +export function spawn( + command: string, + args: string[], + options: SpawnOptions, +): ChildProcess; +/** + * Spawns a child process using `command`. + */ +export function spawn( + command: string, + argsOrOptions?: string[] | SpawnOptions, + maybeOptions?: SpawnOptions, +): ChildProcess { + const args = Array.isArray(argsOrOptions) ? argsOrOptions : []; + let options = !Array.isArray(argsOrOptions) && argsOrOptions != null + ? argsOrOptions + : maybeOptions as SpawnOptions; + + options = normalizeSpawnArguments(command, args, options); + + validateAbortSignal(options?.signal, "options.signal"); + return new ChildProcess(command, args, options); +} + +function validateTimeout(timeout?: number) { + if (timeout != null && !(Number.isInteger(timeout) && timeout >= 0)) { + throw new ERR_OUT_OF_RANGE("timeout", "an unsigned integer", timeout); + } +} + +function validateMaxBuffer(maxBuffer?: number) { + if ( + maxBuffer != null && + !(typeof maxBuffer === "number" && maxBuffer >= 0) + ) { + throw new ERR_OUT_OF_RANGE( + "options.maxBuffer", + "a positive number", + maxBuffer, + ); + } +} + +function sanitizeKillSignal(killSignal?: string | number) { + if (typeof killSignal === "string" || typeof killSignal === "number") { + return convertToValidSignal(killSignal); + } else if (killSignal != null) { + throw new ERR_INVALID_ARG_TYPE( + "options.killSignal", + ["string", "number"], + killSignal, + ); + } +} + +export function spawnSync( + command: string, + argsOrOptions?: string[] | SpawnSyncOptions, + maybeOptions?: SpawnSyncOptions, +): SpawnSyncResult { + const args = Array.isArray(argsOrOptions) ? argsOrOptions : []; + let options = !Array.isArray(argsOrOptions) && argsOrOptions + ? argsOrOptions + : maybeOptions as SpawnSyncOptions; + + options = { + maxBuffer: MAX_BUFFER, + ...normalizeSpawnArguments(command, args, options), + }; + + // Validate the timeout, if present. + validateTimeout(options.timeout); + + // Validate maxBuffer, if present. + validateMaxBuffer(options.maxBuffer); + + // Validate and translate the kill signal, if present. + sanitizeKillSignal(options.killSignal); + + return _spawnSync(command, args, options); +} + +interface ExecOptions extends + Pick< + ChildProcessOptions, + | "env" + | "signal" + | "uid" + | "gid" + | "windowsHide" + > { + cwd?: string | URL; + encoding?: string; + /** + * Shell to execute the command with. + */ + shell?: string; + timeout?: number; + /** + * Largest amount of data in bytes allowed on stdout or stderr. If exceeded, the child process is terminated and any output is truncated. + */ + maxBuffer?: number; + killSignal?: string | number; +} +type ExecException = ChildProcessError; +type ExecCallback = ( + error: ExecException | null, + stdout?: string | Buffer, + stderr?: string | Buffer, +) => void; +type ExecSyncOptions = SpawnSyncOptions; +type ExecFileSyncOptions = SpawnSyncOptions; +function normalizeExecArgs( + command: string, + optionsOrCallback?: ExecOptions | ExecSyncOptions | ExecCallback, + maybeCallback?: ExecCallback, +) { + let callback: ExecFileCallback | undefined = maybeCallback; + + if (typeof optionsOrCallback === "function") { + callback = optionsOrCallback; + optionsOrCallback = undefined; + } + + // Make a shallow copy so we don't clobber the user's options object. + const options: ExecOptions | ExecSyncOptions = { ...optionsOrCallback }; + options.shell = typeof options.shell === "string" ? options.shell : true; + + return { + file: command, + options: options!, + callback: callback!, + }; +} + +/** + * Spawns a shell executing the given command. + */ +export function exec(command: string): ChildProcess; +export function exec(command: string, options: ExecOptions): ChildProcess; +export function exec(command: string, callback: ExecCallback): ChildProcess; +export function exec( + command: string, + options: ExecOptions, + callback: ExecCallback, +): ChildProcess; +export function exec( + command: string, + optionsOrCallback?: ExecOptions | ExecCallback, + maybeCallback?: ExecCallback, +): ChildProcess { + const opts = normalizeExecArgs(command, optionsOrCallback, maybeCallback); + return execFile(opts.file, opts.options as ExecFileOptions, opts.callback); +} + +interface PromiseWithChild extends Promise { + child: ChildProcess; +} +type ExecOutputForPromisify = { + stdout?: string | Buffer; + stderr?: string | Buffer; +}; +type ExecExceptionForPromisify = ExecException & ExecOutputForPromisify; + +const customPromiseExecFunction = (orig: typeof exec) => { + return (...args: [command: string, options: ExecOptions]) => { + const { promise, resolve, reject } = createDeferredPromise() as unknown as { + promise: PromiseWithChild; + resolve?: (value: ExecOutputForPromisify) => void; + reject?: (reason?: ExecExceptionForPromisify) => void; + }; + + promise.child = orig(...args, (err, stdout, stderr) => { + if (err !== null) { + const _err: ExecExceptionForPromisify = err; + _err.stdout = stdout; + _err.stderr = stderr; + reject && reject(_err); + } else { + resolve && resolve({ stdout, stderr }); + } + }); + + return promise; + }; +}; + +Object.defineProperty(exec, promisify.custom, { + enumerable: false, + value: customPromiseExecFunction(exec), +}); + +interface ExecFileOptions extends ChildProcessOptions { + encoding?: string; + timeout?: number; + maxBuffer?: number; + killSignal?: string | number; +} +interface ChildProcessError extends Error { + code?: string | number; + killed?: boolean; + signal?: AbortSignal; + cmd?: string; +} +class ExecFileError extends Error implements ChildProcessError { + code?: string | number; + + constructor(message: string) { + super(message); + this.code = "UNKNOWN"; + } +} +type ExecFileCallback = ( + error: ChildProcessError | null, + stdout?: string | Buffer, + stderr?: string | Buffer, +) => void; +export function execFile(file: string): ChildProcess; +export function execFile( + file: string, + callback: ExecFileCallback, +): ChildProcess; +export function execFile(file: string, args: string[]): ChildProcess; +export function execFile( + file: string, + args: string[], + callback: ExecFileCallback, +): ChildProcess; +export function execFile(file: string, options: ExecFileOptions): ChildProcess; +export function execFile( + file: string, + options: ExecFileOptions, + callback: ExecFileCallback, +): ChildProcess; +export function execFile( + file: string, + args: string[], + options: ExecFileOptions, + callback: ExecFileCallback, +): ChildProcess; +export function execFile( + file: string, + argsOrOptionsOrCallback?: string[] | ExecFileOptions | ExecFileCallback, + optionsOrCallback?: ExecFileOptions | ExecFileCallback, + maybeCallback?: ExecFileCallback, +): ChildProcess { + let args: string[] = []; + let options: ExecFileOptions = {}; + let callback: ExecFileCallback | undefined; + + if (Array.isArray(argsOrOptionsOrCallback)) { + args = argsOrOptionsOrCallback; + } else if (argsOrOptionsOrCallback instanceof Function) { + callback = argsOrOptionsOrCallback; + } else if (argsOrOptionsOrCallback) { + options = argsOrOptionsOrCallback; + } + if (optionsOrCallback instanceof Function) { + callback = optionsOrCallback; + } else if (optionsOrCallback) { + options = optionsOrCallback; + callback = maybeCallback; + } + + const execOptions = { + encoding: "utf8", + timeout: 0, + maxBuffer: MAX_BUFFER, + killSignal: "SIGTERM", + shell: false, + ...options, + }; + if (!Number.isInteger(execOptions.timeout) || execOptions.timeout < 0) { + // In Node source, the first argument to error constructor is "timeout" instead of "options.timeout". + // timeout is indeed a member of options object. + throw new ERR_OUT_OF_RANGE( + "timeout", + "an unsigned integer", + execOptions.timeout, + ); + } + if (execOptions.maxBuffer < 0) { + throw new ERR_OUT_OF_RANGE( + "options.maxBuffer", + "a positive number", + execOptions.maxBuffer, + ); + } + const spawnOptions: SpawnOptions = { + cwd: execOptions.cwd, + env: execOptions.env, + gid: execOptions.gid, + shell: execOptions.shell, + signal: execOptions.signal, + uid: execOptions.uid, + windowsHide: !!execOptions.windowsHide, + windowsVerbatimArguments: !!execOptions.windowsVerbatimArguments, + }; + + const child = spawn(file, args, spawnOptions); + + let encoding: string | null; + const _stdout: (string | Uint8Array)[] = []; + const _stderr: (string | Uint8Array)[] = []; + if ( + execOptions.encoding !== "buffer" && Buffer.isEncoding(execOptions.encoding) + ) { + encoding = execOptions.encoding; + } else { + encoding = null; + } + let stdoutLen = 0; + let stderrLen = 0; + let killed = false; + let exited = false; + let timeoutId: number | null; + + let ex: ChildProcessError | null = null; + + let cmd = file; + + function exithandler(code = 0, signal?: AbortSignal) { + if (exited) return; + exited = true; + + if (timeoutId) { + clearTimeout(timeoutId); + timeoutId = null; + } + + if (!callback) return; + + // merge chunks + let stdout; + let stderr; + if ( + encoding || + ( + child.stdout && + child.stdout.readableEncoding + ) + ) { + stdout = _stdout.join(""); + } else { + stdout = Buffer.concat(_stdout as Buffer[]); + } + if ( + encoding || + ( + child.stderr && + child.stderr.readableEncoding + ) + ) { + stderr = _stderr.join(""); + } else { + stderr = Buffer.concat(_stderr as Buffer[]); + } + + if (!ex && code === 0 && signal === null) { + callback(null, stdout, stderr); + return; + } + + if (args?.length) { + cmd += ` ${args.join(" ")}`; + } + + if (!ex) { + ex = new ExecFileError( + "Command failed: " + cmd + "\n" + stderr, + ); + ex.code = code < 0 ? getSystemErrorName(code) : code; + ex.killed = child.killed || killed; + ex.signal = signal; + } + + ex.cmd = cmd; + callback(ex, stdout, stderr); + } + + function errorhandler(e: ExecFileError) { + ex = e; + + if (child.stdout) { + child.stdout.destroy(); + } + + if (child.stderr) { + child.stderr.destroy(); + } + + exithandler(); + } + + function kill() { + if (child.stdout) { + child.stdout.destroy(); + } + + if (child.stderr) { + child.stderr.destroy(); + } + + killed = true; + try { + child.kill(execOptions.killSignal); + } catch (e) { + if (e) { + ex = e as ChildProcessError; + } + exithandler(); + } + } + + if (execOptions.timeout > 0) { + timeoutId = setTimeout(function delayedKill() { + kill(); + timeoutId = null; + }, execOptions.timeout); + } + + if (child.stdout) { + if (encoding) { + child.stdout.setEncoding(encoding); + } + + child.stdout.on("data", function onChildStdout(chunk: string | Buffer) { + // Do not need to count the length + if (execOptions.maxBuffer === Infinity) { + ArrayPrototypePush(_stdout, chunk); + return; + } + + const encoding = child.stdout?.readableEncoding; + const length = encoding + ? Buffer.byteLength(chunk, encoding) + : chunk.length; + const slice = encoding + ? StringPrototypeSlice + : (buf: string | Buffer, ...args: number[]) => buf.slice(...args); + stdoutLen += length; + + if (stdoutLen > execOptions.maxBuffer) { + const truncatedLen = execOptions.maxBuffer - (stdoutLen - length); + ArrayPrototypePush(_stdout, slice(chunk, 0, truncatedLen)); + + ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER("stdout"); + kill(); + } else { + ArrayPrototypePush(_stdout, chunk); + } + }); + } + + if (child.stderr) { + if (encoding) { + child.stderr.setEncoding(encoding); + } + + child.stderr.on("data", function onChildStderr(chunk: string | Buffer) { + // Do not need to count the length + if (execOptions.maxBuffer === Infinity) { + ArrayPrototypePush(_stderr, chunk); + return; + } + + const encoding = child.stderr?.readableEncoding; + const length = encoding + ? Buffer.byteLength(chunk, encoding) + : chunk.length; + const slice = encoding + ? StringPrototypeSlice + : (buf: string | Buffer, ...args: number[]) => buf.slice(...args); + stderrLen += length; + + if (stderrLen > execOptions.maxBuffer) { + const truncatedLen = execOptions.maxBuffer - (stderrLen - length); + ArrayPrototypePush(_stderr, slice(chunk, 0, truncatedLen)); + + ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER("stderr"); + kill(); + } else { + ArrayPrototypePush(_stderr, chunk); + } + }); + } + + child.addListener("close", exithandler); + child.addListener("error", errorhandler); + + return child; +} + +type ExecFileExceptionForPromisify = ExecFileError & ExecOutputForPromisify; + +const customPromiseExecFileFunction = ( + orig: ( + file: string, + argsOrOptionsOrCallback?: string[] | ExecFileOptions | ExecFileCallback, + optionsOrCallback?: ExecFileOptions | ExecFileCallback, + maybeCallback?: ExecFileCallback, + ) => ChildProcess, +) => { + return ( + ...args: [ + file: string, + argsOrOptions?: string[] | ExecFileOptions, + options?: ExecFileOptions, + ] + ) => { + const { promise, resolve, reject } = createDeferredPromise() as unknown as { + promise: PromiseWithChild; + resolve?: (value: ExecOutputForPromisify) => void; + reject?: (reason?: ExecFileExceptionForPromisify) => void; + }; + + promise.child = orig(...args, (err, stdout, stderr) => { + if (err !== null) { + const _err: ExecFileExceptionForPromisify = err; + _err.stdout = stdout; + _err.stderr = stderr; + reject && reject(_err); + } else { + resolve && resolve({ stdout, stderr }); + } + }); + + return promise; + }; +}; + +Object.defineProperty(execFile, promisify.custom, { + enumerable: false, + value: customPromiseExecFileFunction(execFile), +}); + +function checkExecSyncError( + ret: SpawnSyncResult, + args: string[], + cmd?: string, +) { + let err; + if (ret.error) { + err = ret.error; + ObjectAssign(err, ret); + } else if (ret.status !== 0) { + let msg = "Command failed: "; + msg += cmd || ArrayPrototypeJoin(args, " "); + if (ret.stderr && ret.stderr.length > 0) { + msg += `\n${ret.stderr.toString()}`; + } + err = genericNodeError(msg, ret); + } + return err; +} + +export function execSync(command: string, options: ExecSyncOptions) { + const opts = normalizeExecArgs(command, options); + const inheritStderr = !(opts.options as ExecSyncOptions).stdio; + + const ret = spawnSync(opts.file, opts.options as SpawnSyncOptions); + + if (inheritStderr && ret.stderr) { + process.stderr.write(ret.stderr); + } + + const err = checkExecSyncError(ret, [], command); + + if (err) { + throw err; + } + + return ret.stdout; +} + +function normalizeExecFileArgs( + file: string, + args?: string[] | null | ExecFileSyncOptions | ExecFileCallback, + options?: ExecFileSyncOptions | null | ExecFileCallback, + callback?: ExecFileCallback, +): { + file: string; + args: string[]; + options: ExecFileSyncOptions; + callback?: ExecFileCallback; +} { + if (ArrayIsArray(args)) { + args = ArrayPrototypeSlice(args); + } else if (args != null && typeof args === "object") { + callback = options as ExecFileCallback; + options = args as ExecFileSyncOptions; + args = null; + } else if (typeof args === "function") { + callback = args; + options = null; + args = null; + } + + if (args == null) { + args = []; + } + + if (typeof options === "function") { + callback = options as ExecFileCallback; + } else if (options != null) { + validateObject(options, "options"); + } + + if (options == null) { + options = kEmptyObject; + } + + args = args as string[]; + options = options as ExecFileSyncOptions; + + if (callback != null) { + validateFunction(callback, "callback"); + } + + // Validate argv0, if present. + if (options.argv0 != null) { + validateString(options.argv0, "options.argv0"); + } + + return { file, args, options, callback }; +} + +export function execFileSync(file: string): string | Buffer; +export function execFileSync(file: string, args: string[]): string | Buffer; +export function execFileSync( + file: string, + options: ExecFileSyncOptions, +): string | Buffer; +export function execFileSync( + file: string, + args: string[], + options: ExecFileSyncOptions, +): string | Buffer; +export function execFileSync( + file: string, + args?: string[] | ExecFileSyncOptions, + options?: ExecFileSyncOptions, +): string | Buffer { + ({ file, args, options } = normalizeExecFileArgs(file, args, options)); + + const inheritStderr = !options.stdio; + const ret = spawnSync(file, args, options); + + if (inheritStderr && ret.stderr) { + process.stderr.write(ret.stderr); + } + + const errArgs: string[] = [options.argv0 || file, ...(args as string[])]; + const err = checkExecSyncError(ret, errArgs); + + if (err) { + throw err; + } + + return ret.stdout as string | Buffer; +} + +export default { + fork, + spawn, + exec, + execFile, + execFileSync, + execSync, + ChildProcess, + spawnSync, +}; +export { ChildProcess }; diff --git a/ext/node/polyfills/cluster.ts b/ext/node/polyfills/cluster.ts new file mode 100644 index 0000000000..70d70384fa --- /dev/null +++ b/ext/node/polyfills/cluster.ts @@ -0,0 +1,69 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +/** A Worker object contains all public information and method about a worker. + * In the primary it can be obtained using cluster.workers. In a worker it can + * be obtained using cluster.worker. + */ +export class Worker { + constructor() { + notImplemented("cluster.Worker.prototype.constructor"); + } +} +/** Calls .disconnect() on each worker in cluster.workers. */ +export function disconnected() { + notImplemented("cluster.disconnected"); +} +/** Spawn a new worker process. */ +export function fork() { + notImplemented("cluster.fork"); +} +/** True if the process is a primary. This is determined by + * the process.env.NODE_UNIQUE_ID. If process.env.NODE_UNIQUE_ID is undefined, + * then isPrimary is true. */ +export const isPrimary = undefined; +/** True if the process is not a primary (it is the negation of + * cluster.isPrimary). */ +export const isWorker = undefined; +/** Deprecated alias for cluster.isPrimary. details. */ +export const isMaster = isPrimary; +/** The scheduling policy, either cluster.SCHED_RR for round-robin or + * cluster.SCHED_NONE to leave it to the operating system. This is a global + * setting and effectively frozen once either the first worker is spawned, or + * .setupPrimary() is called, whichever comes first. */ +export const schedulingPolicy = undefined; +/** The settings object */ +export const settings = undefined; +/** Deprecated alias for .setupPrimary(). */ +export function setupMaster() { + notImplemented("cluster.setupMaster"); +} +/** setupPrimary is used to change the default 'fork' behavior. Once called, + * the settings will be present in cluster.settings. */ +export function setupPrimary() { + notImplemented("cluster.setupPrimary"); +} +/** A reference to the current worker object. Not available in the primary + * process. */ +export const worker = undefined; +/** A hash that stores the active worker objects, keyed by id field. Makes it + * easy to loop through all the workers. It is only available in the primary + * process. */ +export const workers = undefined; + +export default { + Worker, + disconnected, + fork, + isPrimary, + isWorker, + isMaster, + schedulingPolicy, + settings, + setupMaster, + setupPrimary, + worker, + workers, +}; diff --git a/ext/node/polyfills/console.ts b/ext/node/polyfills/console.ts new file mode 100644 index 0000000000..bfc9be0515 --- /dev/null +++ b/ext/node/polyfills/console.ts @@ -0,0 +1,33 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { Console } from "internal:deno_node/polyfills/internal/console/constructor.mjs"; + +export default Object.assign({}, console, { Console }); + +export { Console }; +export const { + assert, + clear, + count, + countReset, + debug, + dir, + dirxml, + error, + group, + groupCollapsed, + groupEnd, + info, + log, + profile, + profileEnd, + table, + time, + timeEnd, + timeLog, + timeStamp, + trace, + warn, +} = console; +// deno-lint-ignore no-explicit-any +export const indentLevel = (console as any)?.indentLevel; diff --git a/ext/node/polyfills/constants.ts b/ext/node/polyfills/constants.ts new file mode 100644 index 0000000000..f934224060 --- /dev/null +++ b/ext/node/polyfills/constants.ts @@ -0,0 +1,182 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// Based on: https://github.com/nodejs/node/blob/0646eda/lib/constants.js + +import { constants as fsConstants } from "internal:deno_node/polyfills/fs.ts"; +import { constants as osConstants } from "internal:deno_node/polyfills/os.ts"; + +export default { + ...fsConstants, + ...osConstants.dlopen, + ...osConstants.errno, + ...osConstants.signals, + ...osConstants.priority, +}; + +export const { + F_OK, + R_OK, + W_OK, + X_OK, + O_RDONLY, + O_WRONLY, + O_RDWR, + O_NOCTTY, + O_TRUNC, + O_APPEND, + O_DIRECTORY, + O_NOFOLLOW, + O_SYNC, + O_DSYNC, + O_SYMLINK, + O_NONBLOCK, + O_CREAT, + O_EXCL, + S_IRUSR, + S_IWUSR, + S_IXUSR, + S_IRGRP, + S_IWGRP, + S_IXGRP, + S_IROTH, + S_IWOTH, + S_IXOTH, + COPYFILE_EXCL, + COPYFILE_FICLONE, + COPYFILE_FICLONE_FORCE, + UV_FS_COPYFILE_EXCL, + UV_FS_COPYFILE_FICLONE, + UV_FS_COPYFILE_FICLONE_FORCE, +} = fsConstants; +export const { + RTLD_DEEPBIND, + RTLD_GLOBAL, + RTLD_LAZY, + RTLD_LOCAL, + RTLD_NOW, +} = osConstants.dlopen; +export const { + E2BIG, + EACCES, + EADDRINUSE, + EADDRNOTAVAIL, + EAFNOSUPPORT, + EAGAIN, + EALREADY, + EBADF, + EBADMSG, + EBUSY, + ECANCELED, + ECHILD, + ECONNABORTED, + ECONNREFUSED, + ECONNRESET, + EDEADLK, + EDESTADDRREQ, + EDOM, + EDQUOT, + EEXIST, + EFAULT, + EFBIG, + EHOSTUNREACH, + EIDRM, + EILSEQ, + EINPROGRESS, + EINTR, + EINVAL, + EIO, + EISCONN, + EISDIR, + ELOOP, + EMFILE, + EMLINK, + EMSGSIZE, + EMULTIHOP, + ENAMETOOLONG, + ENETDOWN, + ENETRESET, + ENETUNREACH, + ENFILE, + ENOBUFS, + ENODATA, + ENODEV, + ENOENT, + ENOEXEC, + ENOLCK, + ENOLINK, + ENOMEM, + ENOMSG, + ENOPROTOOPT, + ENOSPC, + ENOSR, + ENOSTR, + ENOSYS, + ENOTCONN, + ENOTDIR, + ENOTEMPTY, + ENOTSOCK, + ENOTSUP, + ENOTTY, + ENXIO, + EOPNOTSUPP, + EOVERFLOW, + EPERM, + EPIPE, + EPROTO, + EPROTONOSUPPORT, + EPROTOTYPE, + ERANGE, + EROFS, + ESPIPE, + ESRCH, + ESTALE, + ETIME, + ETIMEDOUT, + ETXTBSY, + EWOULDBLOCK, + EXDEV, +} = osConstants.errno; +export const { + PRIORITY_ABOVE_NORMAL, + PRIORITY_BELOW_NORMAL, + PRIORITY_HIGH, + PRIORITY_HIGHEST, + PRIORITY_LOW, + PRIORITY_NORMAL, +} = osConstants.priority; +export const { + SIGABRT, + SIGALRM, + SIGBUS, + SIGCHLD, + SIGCONT, + SIGFPE, + SIGHUP, + SIGILL, + SIGINT, + SIGIO, + SIGIOT, + SIGKILL, + SIGPIPE, + SIGPOLL, + SIGPROF, + SIGPWR, + SIGQUIT, + SIGSEGV, + SIGSTKFLT, + SIGSTOP, + SIGSYS, + SIGTERM, + SIGTRAP, + SIGTSTP, + SIGTTIN, + SIGTTOU, + SIGUNUSED, + SIGURG, + SIGUSR1, + SIGUSR2, + SIGVTALRM, + SIGWINCH, + SIGXCPU, + SIGXFSZ, +} = osConstants.signals; diff --git a/ext/node/polyfills/crypto.ts b/ext/node/polyfills/crypto.ts new file mode 100644 index 0000000000..b59158456a --- /dev/null +++ b/ext/node/polyfills/crypto.ts @@ -0,0 +1,513 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { ERR_CRYPTO_FIPS_FORCED } from "internal:deno_node/polyfills/internal/errors.ts"; +import { crypto as constants } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { getOptionValue } from "internal:deno_node/polyfills/internal/options.ts"; +import { + getFipsCrypto, + setFipsCrypto, + timingSafeEqual, +} from "internal:deno_node/polyfills/internal_binding/crypto.ts"; +import { + checkPrime, + checkPrimeSync, + generatePrime, + generatePrimeSync, + randomBytes, + randomFill, + randomFillSync, + randomInt, + randomUUID, +} from "internal:deno_node/polyfills/internal/crypto/random.ts"; +import type { + CheckPrimeOptions, + GeneratePrimeOptions, + GeneratePrimeOptionsArrayBuffer, + GeneratePrimeOptionsBigInt, + LargeNumberLike, +} from "internal:deno_node/polyfills/internal/crypto/random.ts"; +import { + pbkdf2, + pbkdf2Sync, +} from "internal:deno_node/polyfills/internal/crypto/pbkdf2.ts"; +import type { + Algorithms, + NormalizedAlgorithms, +} from "internal:deno_node/polyfills/internal/crypto/pbkdf2.ts"; +import { + scrypt, + scryptSync, +} from "internal:deno_node/polyfills/internal/crypto/scrypt.ts"; +import { + hkdf, + hkdfSync, +} from "internal:deno_node/polyfills/internal/crypto/hkdf.ts"; +import { + generateKey, + generateKeyPair, + generateKeyPairSync, + generateKeySync, +} from "internal:deno_node/polyfills/internal/crypto/keygen.ts"; +import type { + BasePrivateKeyEncodingOptions, + DSAKeyPairKeyObjectOptions, + DSAKeyPairOptions, + ECKeyPairKeyObjectOptions, + ECKeyPairOptions, + ED25519KeyPairKeyObjectOptions, + ED25519KeyPairOptions, + ED448KeyPairKeyObjectOptions, + ED448KeyPairOptions, + KeyPairKeyObjectResult, + KeyPairSyncResult, + RSAKeyPairKeyObjectOptions, + RSAKeyPairOptions, + RSAPSSKeyPairKeyObjectOptions, + RSAPSSKeyPairOptions, + X25519KeyPairKeyObjectOptions, + X25519KeyPairOptions, + X448KeyPairKeyObjectOptions, + X448KeyPairOptions, +} from "internal:deno_node/polyfills/internal/crypto/keygen.ts"; +import { + createPrivateKey, + createPublicKey, + createSecretKey, + KeyObject, +} from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import type { + AsymmetricKeyDetails, + JsonWebKeyInput, + JwkKeyExportOptions, + KeyExportOptions, + KeyObjectType, +} from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import { + DiffieHellman, + diffieHellman, + DiffieHellmanGroup, + ECDH, +} from "internal:deno_node/polyfills/internal/crypto/diffiehellman.ts"; +import { + Cipheriv, + Decipheriv, + getCipherInfo, + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, +} from "internal:deno_node/polyfills/internal/crypto/cipher.ts"; +import type { + Cipher, + CipherCCM, + CipherCCMOptions, + CipherCCMTypes, + CipherGCM, + CipherGCMOptions, + CipherGCMTypes, + CipherKey, + CipherOCB, + CipherOCBOptions, + CipherOCBTypes, + Decipher, + DecipherCCM, + DecipherGCM, + DecipherOCB, +} from "internal:deno_node/polyfills/internal/crypto/cipher.ts"; +import type { + BinaryLike, + BinaryToTextEncoding, + CharacterEncoding, + ECDHKeyFormat, + Encoding, + HASH_DATA, + KeyFormat, + KeyType, + LegacyCharacterEncoding, + PrivateKeyInput, + PublicKeyInput, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { + Sign, + signOneShot, + Verify, + verifyOneShot, +} from "internal:deno_node/polyfills/internal/crypto/sig.ts"; +import type { + DSAEncoding, + KeyLike, + SigningOptions, + SignKeyObjectInput, + SignPrivateKeyInput, + VerifyKeyObjectInput, + VerifyPublicKeyInput, +} from "internal:deno_node/polyfills/internal/crypto/sig.ts"; +import { + createHash, + Hash, + Hmac, +} from "internal:deno_node/polyfills/internal/crypto/hash.ts"; +import { X509Certificate } from "internal:deno_node/polyfills/internal/crypto/x509.ts"; +import type { + PeerCertificate, + X509CheckOptions, +} from "internal:deno_node/polyfills/internal/crypto/x509.ts"; +import { + getCiphers, + getCurves, + getHashes, + secureHeapUsed, + setEngine, +} from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import type { SecureHeapUsage } from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import Certificate from "internal:deno_node/polyfills/internal/crypto/certificate.ts"; +import type { + TransformOptions, + WritableOptions, +} from "internal:deno_node/polyfills/_stream.d.ts"; + +const webcrypto = globalThis.crypto; +const fipsForced = getOptionValue("--force-fips"); + +function createCipheriv( + algorithm: CipherCCMTypes, + key: CipherKey, + iv: BinaryLike, + options: CipherCCMOptions, +): CipherCCM; +function createCipheriv( + algorithm: CipherOCBTypes, + key: CipherKey, + iv: BinaryLike, + options: CipherOCBOptions, +): CipherOCB; +function createCipheriv( + algorithm: CipherGCMTypes, + key: CipherKey, + iv: BinaryLike, + options?: CipherGCMOptions, +): CipherGCM; +function createCipheriv( + algorithm: string, + key: CipherKey, + iv: BinaryLike | null, + options?: TransformOptions, +): Cipher; +function createCipheriv( + cipher: string, + key: CipherKey, + iv: BinaryLike | null, + options?: TransformOptions, +): Cipher { + return new Cipheriv(cipher, key, iv, options); +} + +function createDecipheriv( + algorithm: CipherCCMTypes, + key: CipherKey, + iv: BinaryLike, + options: CipherCCMOptions, +): DecipherCCM; +function createDecipheriv( + algorithm: CipherOCBTypes, + key: CipherKey, + iv: BinaryLike, + options: CipherOCBOptions, +): DecipherOCB; +function createDecipheriv( + algorithm: CipherGCMTypes, + key: CipherKey, + iv: BinaryLike, + options?: CipherGCMOptions, +): DecipherGCM; +function createDecipheriv( + algorithm: string, + key: CipherKey, + iv: BinaryLike | null, + options?: TransformOptions, +): Decipher { + return new Decipheriv(algorithm, key, iv, options); +} + +function createDiffieHellman( + primeLength: number, + generator?: number | ArrayBufferView, +): DiffieHellman; +function createDiffieHellman(prime: ArrayBufferView): DiffieHellman; +function createDiffieHellman( + prime: string, + primeEncoding: BinaryToTextEncoding, +): DiffieHellman; +function createDiffieHellman( + prime: string, + primeEncoding: BinaryToTextEncoding, + generator: number | ArrayBufferView, +): DiffieHellman; +function createDiffieHellman( + prime: string, + primeEncoding: BinaryToTextEncoding, + generator: string, + generatorEncoding: BinaryToTextEncoding, +): DiffieHellman; +function createDiffieHellman( + sizeOrKey: number | string | ArrayBufferView, + keyEncoding?: number | ArrayBufferView | BinaryToTextEncoding, + generator?: number | ArrayBufferView | string, + generatorEncoding?: BinaryToTextEncoding, +): DiffieHellman { + return new DiffieHellman( + sizeOrKey, + keyEncoding, + generator, + generatorEncoding, + ); +} + +function createDiffieHellmanGroup(name: string): DiffieHellmanGroup { + return new DiffieHellmanGroup(name); +} + +function createECDH(curve: string): ECDH { + return new ECDH(curve); +} + +function createHmac( + hmac: string, + key: string | ArrayBuffer | KeyObject, + options?: TransformOptions, +) { + return Hmac(hmac, key, options); +} + +function createSign(algorithm: string, options?: WritableOptions): Sign { + return new Sign(algorithm, options); +} + +function createVerify(algorithm: string, options?: WritableOptions): Verify { + return new Verify(algorithm, options); +} + +function setFipsForced(val: boolean) { + if (val) { + return; + } + + throw new ERR_CRYPTO_FIPS_FORCED(); +} + +function getFipsForced() { + return 1; +} + +Object.defineProperty(constants, "defaultCipherList", { + value: getOptionValue("--tls-cipher-list"), +}); + +const getDiffieHellman = createDiffieHellmanGroup; + +const getFips = fipsForced ? getFipsForced : getFipsCrypto; +const setFips = fipsForced ? setFipsForced : setFipsCrypto; + +const sign = signOneShot; +const verify = verifyOneShot; + +export default { + Certificate, + checkPrime, + checkPrimeSync, + Cipheriv, + constants, + createCipheriv, + createDecipheriv, + createDiffieHellman, + createDiffieHellmanGroup, + createECDH, + createHash, + createHmac, + createPrivateKey, + createPublicKey, + createSecretKey, + createSign, + createVerify, + Decipheriv, + DiffieHellman, + diffieHellman, + DiffieHellmanGroup, + ECDH, + generateKey, + generateKeyPair, + generateKeyPairSync, + generateKeySync, + generatePrime, + generatePrimeSync, + getCipherInfo, + getCiphers, + getCurves, + getDiffieHellman, + getFips, + getHashes, + Hash, + hkdf, + hkdfSync, + Hmac, + KeyObject, + pbkdf2, + pbkdf2Sync, + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, + randomBytes, + randomFill, + randomFillSync, + randomInt, + randomUUID, + scrypt, + scryptSync, + secureHeapUsed, + setEngine, + setFips, + Sign, + sign, + timingSafeEqual, + Verify, + verify, + webcrypto, + X509Certificate, +}; + +export type { + Algorithms, + AsymmetricKeyDetails, + BasePrivateKeyEncodingOptions, + BinaryLike, + BinaryToTextEncoding, + CharacterEncoding, + CheckPrimeOptions, + Cipher, + CipherCCM, + CipherCCMOptions, + CipherCCMTypes, + CipherGCM, + CipherGCMOptions, + CipherGCMTypes, + CipherKey, + CipherOCB, + CipherOCBOptions, + CipherOCBTypes, + Decipher, + DecipherCCM, + DecipherGCM, + DecipherOCB, + DSAEncoding, + DSAKeyPairKeyObjectOptions, + DSAKeyPairOptions, + ECDHKeyFormat, + ECKeyPairKeyObjectOptions, + ECKeyPairOptions, + ED25519KeyPairKeyObjectOptions, + ED25519KeyPairOptions, + ED448KeyPairKeyObjectOptions, + ED448KeyPairOptions, + Encoding, + GeneratePrimeOptions, + GeneratePrimeOptionsArrayBuffer, + GeneratePrimeOptionsBigInt, + HASH_DATA, + JsonWebKeyInput, + JwkKeyExportOptions, + KeyExportOptions, + KeyFormat, + KeyLike, + KeyObjectType, + KeyPairKeyObjectResult, + KeyPairSyncResult, + KeyType, + LargeNumberLike, + LegacyCharacterEncoding, + NormalizedAlgorithms, + PeerCertificate, + PrivateKeyInput, + PublicKeyInput, + RSAKeyPairKeyObjectOptions, + RSAKeyPairOptions, + RSAPSSKeyPairKeyObjectOptions, + RSAPSSKeyPairOptions, + SecureHeapUsage, + SigningOptions, + SignKeyObjectInput, + SignPrivateKeyInput, + VerifyKeyObjectInput, + VerifyPublicKeyInput, + X25519KeyPairKeyObjectOptions, + X25519KeyPairOptions, + X448KeyPairKeyObjectOptions, + X448KeyPairOptions, + X509CheckOptions, +}; + +export { + Certificate, + checkPrime, + checkPrimeSync, + Cipheriv, + constants, + createCipheriv, + createDecipheriv, + createDiffieHellman, + createDiffieHellmanGroup, + createECDH, + createHash, + createHmac, + createPrivateKey, + createPublicKey, + createSecretKey, + createSign, + createVerify, + Decipheriv, + DiffieHellman, + diffieHellman, + DiffieHellmanGroup, + ECDH, + generateKey, + generateKeyPair, + generateKeyPairSync, + generateKeySync, + generatePrime, + generatePrimeSync, + getCipherInfo, + getCiphers, + getCurves, + getDiffieHellman, + getFips, + getHashes, + Hash, + hkdf, + hkdfSync, + Hmac, + KeyObject, + pbkdf2, + pbkdf2Sync, + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, + randomBytes, + randomFill, + randomFillSync, + randomInt, + randomUUID, + scrypt, + scryptSync, + secureHeapUsed, + setEngine, + setFips, + Sign, + sign, + timingSafeEqual, + Verify, + verify, + webcrypto, + X509Certificate, +}; diff --git a/ext/node/polyfills/dgram.ts b/ext/node/polyfills/dgram.ts new file mode 100644 index 0000000000..222421eb00 --- /dev/null +++ b/ext/node/polyfills/dgram.ts @@ -0,0 +1,1558 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { lookup as defaultLookup } from "internal:deno_node/polyfills/dns.ts"; +import type { + ErrnoException, + NodeSystemErrorCtx, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + ERR_BUFFER_OUT_OF_BOUNDS, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_FD_TYPE, + ERR_MISSING_ARGS, + ERR_SOCKET_ALREADY_BOUND, + ERR_SOCKET_BAD_BUFFER_SIZE, + ERR_SOCKET_BUFFER_SIZE, + ERR_SOCKET_DGRAM_IS_CONNECTED, + ERR_SOCKET_DGRAM_NOT_CONNECTED, + ERR_SOCKET_DGRAM_NOT_RUNNING, + errnoException, + exceptionWithHostPort, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import type { Abortable } from "internal:deno_node/polyfills/_events.d.ts"; +import { + kStateSymbol, + newHandle, +} from "internal:deno_node/polyfills/internal/dgram.ts"; +import type { SocketType } from "internal:deno_node/polyfills/internal/dgram.ts"; +import { + asyncIdSymbol, + defaultTriggerAsyncIdScope, + ownerSymbol, +} from "internal:deno_node/polyfills/internal/async_hooks.ts"; +import { + SendWrap, + UDP, +} from "internal:deno_node/polyfills/internal_binding/udp_wrap.ts"; +import { + isInt32, + validateAbortSignal, + validateNumber, + validatePort, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { guessHandleType } from "internal:deno_node/polyfills/internal_binding/util.ts"; +import { os } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { nextTick } from "internal:deno_node/polyfills/process.ts"; +import { channel } from "internal:deno_node/polyfills/diagnostics_channel.ts"; +import { isArrayBufferView } from "internal:deno_node/polyfills/internal/util/types.ts"; + +const { UV_UDP_REUSEADDR, UV_UDP_IPV6ONLY } = os; + +const udpSocketChannel = channel("udp.socket"); + +const BIND_STATE_UNBOUND = 0; +const BIND_STATE_BINDING = 1; +const BIND_STATE_BOUND = 2; + +const CONNECT_STATE_DISCONNECTED = 0; +const CONNECT_STATE_CONNECTING = 1; +const CONNECT_STATE_CONNECTED = 2; + +const RECV_BUFFER = true; +const SEND_BUFFER = false; + +export interface AddressInfo { + address: string; + family: number; + port: number; +} + +export type MessageType = string | Uint8Array | Buffer | DataView; + +export type RemoteInfo = { + address: string; + family: "IPv4" | "IPv6"; + port: number; + size?: number; +}; + +export interface BindOptions { + port?: number; + address?: string; + exclusive?: boolean; + fd?: number; +} + +export interface SocketOptions extends Abortable { + type: SocketType; + reuseAddr?: boolean; + /** + * @default false + */ + ipv6Only?: boolean; + recvBufferSize?: number; + sendBufferSize?: number; + lookup?: typeof defaultLookup; +} + +interface SocketInternalState { + handle: UDP | null; + receiving: boolean; + bindState: + | typeof BIND_STATE_UNBOUND + | typeof BIND_STATE_BINDING + | typeof BIND_STATE_BOUND; + connectState: + | typeof CONNECT_STATE_DISCONNECTED + | typeof CONNECT_STATE_CONNECTING + | typeof CONNECT_STATE_CONNECTED; + queue?: Array<() => void>; + reuseAddr?: boolean; + ipv6Only?: boolean; + recvBufferSize?: number; + sendBufferSize?: number; +} + +const isSocketOptions = ( + socketOption: unknown, +): socketOption is SocketOptions => + socketOption !== null && typeof socketOption === "object"; + +const isUdpHandle = (handle: unknown): handle is UDP => + handle !== null && + typeof handle === "object" && + typeof (handle as UDP).recvStart === "function"; + +const isBindOptions = (options: unknown): options is BindOptions => + options !== null && typeof options === "object"; + +/** + * Encapsulates the datagram functionality. + * + * New instances of `dgram.Socket` are created using `createSocket`. + * The `new` keyword is not to be used to create `dgram.Socket` instances. + */ +export class Socket extends EventEmitter { + [asyncIdSymbol]!: number; + [kStateSymbol]!: SocketInternalState; + + type!: SocketType; + + constructor( + type: SocketType | SocketOptions, + listener?: (msg: Buffer, rinfo: RemoteInfo) => void, + ) { + super(); + + let lookup; + let recvBufferSize; + let sendBufferSize; + + let options: SocketOptions | undefined; + + if (isSocketOptions(type)) { + options = type; + type = options.type; + lookup = options.lookup; + recvBufferSize = options.recvBufferSize; + sendBufferSize = options.sendBufferSize; + } + + const handle = newHandle(type, lookup); + handle[ownerSymbol] = this; + + this[asyncIdSymbol] = handle.getAsyncId(); + this.type = type; + + if (typeof listener === "function") { + this.on("message", listener); + } + + this[kStateSymbol] = { + handle, + receiving: false, + bindState: BIND_STATE_UNBOUND, + connectState: CONNECT_STATE_DISCONNECTED, + queue: undefined, + reuseAddr: options && options.reuseAddr, // Use UV_UDP_REUSEADDR if true. + ipv6Only: options && options.ipv6Only, + recvBufferSize, + sendBufferSize, + }; + + if (options?.signal !== undefined) { + const { signal } = options; + + validateAbortSignal(signal, "options.signal"); + + const onAborted = () => { + this.close(); + }; + + if (signal.aborted) { + onAborted(); + } else { + signal.addEventListener("abort", onAborted); + + this.once( + "close", + () => signal.removeEventListener("abort", onAborted), + ); + } + } + + if (udpSocketChannel.hasSubscribers) { + udpSocketChannel.publish({ + socket: this, + }); + } + } + + /** + * Tells the kernel to join a multicast group at the given `multicastAddress` + * and `multicastInterface` using the `IP_ADD_MEMBERSHIP` socket option. If + * the`multicastInterface` argument is not specified, the operating system + * will choose one interface and will add membership to it. To add membership + * to every available interface, call `addMembership` multiple times, once + * per interface. + * + * When called on an unbound socket, this method will implicitly bind to a + * random port, listening on all interfaces. + * + * When sharing a UDP socket across multiple `cluster` workers, the + * `socket.addMembership()` function must be called only once or an + * `EADDRINUSE` error will occur: + * + * ```js + * import cluster from "internal:deno_node/polyfills/cluster"; + * import dgram from "internal:deno_node/polyfills/dgram"; + * + * if (cluster.isPrimary) { + * cluster.fork(); // Works ok. + * cluster.fork(); // Fails with EADDRINUSE. + * } else { + * const s = dgram.createSocket('udp4'); + * s.bind(1234, () => { + * s.addMembership('224.0.0.114'); + * }); + * } + * ``` + */ + addMembership(multicastAddress: string, interfaceAddress?: string) { + healthCheck(this); + + if (!multicastAddress) { + throw new ERR_MISSING_ARGS("multicastAddress"); + } + + const { handle } = this[kStateSymbol]; + const err = handle!.addMembership(multicastAddress, interfaceAddress); + + if (err) { + throw errnoException(err, "addMembership"); + } + } + + /** + * Tells the kernel to join a source-specific multicast channel at the given + * `sourceAddress` and `groupAddress`, using the `multicastInterface` with + * the `IP_ADD_SOURCE_MEMBERSHIP` socket option. If the `multicastInterface` + * argument is not specified, the operating system will choose one interface + * and will add membership to it. To add membership to every available + * interface, call `socket.addSourceSpecificMembership()` multiple times, + * once per interface. + * + * When called on an unbound socket, this method will implicitly bind to a + * random port, listening on all interfaces. + */ + addSourceSpecificMembership( + sourceAddress: string, + groupAddress: string, + interfaceAddress?: string, + ) { + healthCheck(this); + + validateString(sourceAddress, "sourceAddress"); + validateString(groupAddress, "groupAddress"); + + const err = this[kStateSymbol].handle!.addSourceSpecificMembership( + sourceAddress, + groupAddress, + interfaceAddress, + ); + + if (err) { + throw errnoException(err, "addSourceSpecificMembership"); + } + } + + /** + * Returns an object containing the address information for a socket. + * For UDP sockets, this object will contain `address`, `family` and `port`properties. + * + * This method throws `EBADF` if called on an unbound socket. + */ + address(): AddressInfo { + healthCheck(this); + + const out = {}; + const err = this[kStateSymbol].handle!.getsockname(out); + + if (err) { + throw errnoException(err, "getsockname"); + } + + return out as AddressInfo; + } + + /** + * For UDP sockets, causes the `dgram.Socket` to listen for datagram + * messages on a named `port` and optional `address`. If `port` is not + * specified or is `0`, the operating system will attempt to bind to a + * random port. If `address` is not specified, the operating system will + * attempt to listen on all addresses. Once binding is complete, a + * `'listening'` event is emitted and the optional `callback` function is + * called. + * + * Specifying both a `'listening'` event listener and passing a `callback` to + * the `socket.bind()` method is not harmful but not very useful. + * + * A bound datagram socket keeps the Node.js process running to receive + * datagram messages. + * + * If binding fails, an `'error'` event is generated. In rare case (e.g. + * attempting to bind with a closed socket), an `Error` may be thrown. + * + * Example of a UDP server listening on port 41234: + * + * ```js + * import dgram from "internal:deno_node/polyfills/dgram"; + * + * const server = dgram.createSocket('udp4'); + * + * server.on('error', (err) => { + * console.log(`server error:\n${err.stack}`); + * server.close(); + * }); + * + * server.on('message', (msg, rinfo) => { + * console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`); + * }); + * + * server.on('listening', () => { + * const address = server.address(); + * console.log(`server listening ${address.address}:${address.port}`); + * }); + * + * server.bind(41234); + * // Prints: server listening 0.0.0.0:41234 + * ``` + * + * @param callback with no parameters. Called when binding is complete. + */ + bind(port?: number, address?: string, callback?: () => void): this; + bind(port: number, callback?: () => void): this; + bind(callback: () => void): this; + bind(options: BindOptions, callback?: () => void): this; + bind(port_?: unknown, address_?: unknown /* callback */): this { + let port = typeof port_ === "function" ? null : port_; + + healthCheck(this); + + const state = this[kStateSymbol]; + + if (state.bindState !== BIND_STATE_UNBOUND) { + throw new ERR_SOCKET_ALREADY_BOUND(); + } + + state.bindState = BIND_STATE_BINDING; + + const cb = arguments.length && arguments[arguments.length - 1]; + + if (typeof cb === "function") { + // deno-lint-ignore no-inner-declarations + function removeListeners(this: Socket) { + this.removeListener("error", removeListeners); + this.removeListener("listening", onListening); + } + + // deno-lint-ignore no-inner-declarations + function onListening(this: Socket) { + removeListeners.call(this); + cb.call(this); + } + + this.on("error", removeListeners); + this.on("listening", onListening); + } + + if (isUdpHandle(port)) { + replaceHandle(this, port); + startListening(this); + + return this; + } + + // Open an existing fd instead of creating a new one. + if (isBindOptions(port) && isInt32(port.fd!) && port.fd! > 0) { + const fd = port.fd!; + const state = this[kStateSymbol]; + + // TODO(cmorten): here we deviate somewhat from the Node implementation which + // makes use of the https://nodejs.org/api/cluster.html module to run servers + // across a "cluster" of Node processes to take advantage of multi-core + // systems. + // + // Though Deno has has a Worker capability from which we could simulate this, + // for now we assert that we are _always_ on the primary process. + + const type = guessHandleType(fd); + + if (type !== "UDP") { + throw new ERR_INVALID_FD_TYPE(type); + } + + const err = state.handle!.open(fd); + + if (err) { + throw errnoException(err, "open"); + } + + startListening(this); + + return this; + } + + let address: string; + + if (isBindOptions(port)) { + address = port.address || ""; + port = port.port; + } else { + address = typeof address_ === "function" ? "" : (address_ as string); + } + + // Defaulting address for bind to all interfaces + if (!address) { + if (this.type === "udp4") { + address = "0.0.0.0"; + } else { + address = "::"; + } + } + + // Resolve address first + state.handle!.lookup(address, (lookupError, ip) => { + if (lookupError) { + state.bindState = BIND_STATE_UNBOUND; + this.emit("error", lookupError); + + return; + } + + let flags: number | undefined = 0; + + if (state.reuseAddr) { + flags |= UV_UDP_REUSEADDR; + } + if (state.ipv6Only) { + flags |= UV_UDP_IPV6ONLY!; + } + + // TODO(cmorten): here we deviate somewhat from the Node implementation which + // makes use of the https://nodejs.org/api/cluster.html module to run servers + // across a "cluster" of Node processes to take advantage of multi-core + // systems. + // + // Though Deno has has a Worker capability from which we could simulate this, + // for now we assert that we are _always_ on the primary process. + + if (!state.handle) { + return; // Handle has been closed in the mean time + } + + const err = state.handle.bind(ip, port as number || 0, flags); + + if (err) { + const ex = exceptionWithHostPort(err, "bind", ip, port as number); + state.bindState = BIND_STATE_UNBOUND; + this.emit("error", ex); + + // Todo(@bartlomieju): close? + return; + } + + startListening(this); + }); + + return this; + } + + /** + * Close the underlying socket and stop listening for data on it. If a + * callback is provided, it is added as a listener for the `'close'` event. + * + * @param callback Called when the socket has been closed. + */ + close(callback?: () => void): this { + const state = this[kStateSymbol]; + const queue = state.queue; + + if (typeof callback === "function") { + this.on("close", callback); + } + + if (queue !== undefined) { + queue.push(this.close.bind(this)); + + return this; + } + + healthCheck(this); + stopReceiving(this); + + state.handle!.close(); + state.handle = null; + + defaultTriggerAsyncIdScope( + this[asyncIdSymbol], + nextTick, + socketCloseNT, + this, + ); + + return this; + } + + /** + * Associates the `dgram.Socket` to a remote address and port. Every + * message sent by this handle is automatically sent to that destination. + * Also, the socket will only receive messages from that remote peer. + * Trying to call `connect()` on an already connected socket will result + * in an `ERR_SOCKET_DGRAM_IS_CONNECTED` exception. If `address` is not + * provided, `'127.0.0.1'` (for `udp4` sockets) or `'::1'` (for `udp6` sockets) + * will be used by default. Once the connection is complete, a `'connect'` event + * is emitted and the optional `callback` function is called. In case of failure, + * the `callback` is called or, failing this, an `'error'` event is emitted. + * + * @param callback Called when the connection is completed or on error. + */ + connect( + port: number, + address?: string, + callback?: (err?: ErrnoException) => void, + ): void; + connect(port: number, callback: (err?: ErrnoException) => void): void; + connect(port: number, address?: unknown, callback?: unknown) { + port = validatePort(port, "Port", false); + + if (typeof address === "function") { + callback = address; + address = ""; + } else if (address === undefined) { + address = ""; + } + + validateString(address, "address"); + + const state = this[kStateSymbol]; + + if (state.connectState !== CONNECT_STATE_DISCONNECTED) { + throw new ERR_SOCKET_DGRAM_IS_CONNECTED(); + } + + state.connectState = CONNECT_STATE_CONNECTING; + + if (state.bindState === BIND_STATE_UNBOUND) { + this.bind({ port: 0, exclusive: true }); + } + + if (state.bindState !== BIND_STATE_BOUND) { + enqueue( + this, + _connect.bind( + this, + port, + address as string, + callback as (err?: ErrnoException) => void, + ), + ); + + return; + } + + Reflect.apply(_connect, this, [port, address, callback]); + } + + /** + * A synchronous function that disassociates a connected `dgram.Socket` from + * its remote address. Trying to call `disconnect()` on an unbound or already + * disconnected socket will result in an `ERR_SOCKET_DGRAM_NOT_CONNECTED` + * exception. + */ + disconnect() { + const state = this[kStateSymbol]; + + if (state.connectState !== CONNECT_STATE_CONNECTED) { + throw new ERR_SOCKET_DGRAM_NOT_CONNECTED(); + } + + const err = state.handle!.disconnect(); + + if (err) { + throw errnoException(err, "connect"); + } else { + state.connectState = CONNECT_STATE_DISCONNECTED; + } + } + + /** + * Instructs the kernel to leave a multicast group at `multicastAddress` + * using the `IP_DROP_MEMBERSHIP` socket option. This method is automatically + * called by the kernel when the socket is closed or the process terminates, + * so most apps will never have reason to call this. + * + * If `multicastInterface` is not specified, the operating system will + * attempt to drop membership on all valid interfaces. + */ + dropMembership(multicastAddress: string, interfaceAddress?: string) { + healthCheck(this); + + if (!multicastAddress) { + throw new ERR_MISSING_ARGS("multicastAddress"); + } + + const err = this[kStateSymbol].handle!.dropMembership( + multicastAddress, + interfaceAddress, + ); + + if (err) { + throw errnoException(err, "dropMembership"); + } + } + + /** + * Instructs the kernel to leave a source-specific multicast channel at the + * given `sourceAddress` and `groupAddress` using the + * `IP_DROP_SOURCE_MEMBERSHIP` socket option. This method is automatically + * called by the kernel when the socket is closed or the process terminates, + * so most apps will never have reason to call this. + * + * If `multicastInterface` is not specified, the operating system will + * attempt to drop membership on all valid interfaces. + */ + dropSourceSpecificMembership( + sourceAddress: string, + groupAddress: string, + interfaceAddress?: string, + ) { + healthCheck(this); + + validateString(sourceAddress, "sourceAddress"); + validateString(groupAddress, "groupAddress"); + + const err = this[kStateSymbol].handle!.dropSourceSpecificMembership( + sourceAddress, + groupAddress, + interfaceAddress, + ); + + if (err) { + throw errnoException(err, "dropSourceSpecificMembership"); + } + } + + /** + * This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound + * socket. + * + * @return the `SO_RCVBUF` socket receive buffer size in bytes. + */ + getRecvBufferSize(): number { + return bufferSize(this, 0, RECV_BUFFER); + } + + /** + * This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound + * socket. + * + * @return the `SO_SNDBUF` socket send buffer size in bytes. + */ + getSendBufferSize(): number { + return bufferSize(this, 0, SEND_BUFFER); + } + + /** + * By default, binding a socket will cause it to block the Node.js process + * from exiting as long as the socket is open. The `socket.unref()` method + * can be used to exclude the socket from the reference counting that keeps + * the Node.js process active. The `socket.ref()` method adds the socket back + * to the reference counting and restores the default behavior. + * + * Calling `socket.ref()` multiples times will have no additional effect. + * + * The `socket.ref()` method returns a reference to the socket so calls can + * be chained. + */ + ref(): this { + const handle = this[kStateSymbol].handle; + + if (handle) { + handle.ref(); + } + + return this; + } + + /** + * Returns an object containing the `address`, `family`, and `port` of the + * remote endpoint. This method throws an `ERR_SOCKET_DGRAM_NOT_CONNECTED` + * exception if the socket is not connected. + */ + remoteAddress(): AddressInfo { + healthCheck(this); + + const state = this[kStateSymbol]; + + if (state.connectState !== CONNECT_STATE_CONNECTED) { + throw new ERR_SOCKET_DGRAM_NOT_CONNECTED(); + } + + const out = {}; + const err = state.handle!.getpeername(out); + + if (err) { + throw errnoException(err, "getpeername"); + } + + return out as AddressInfo; + } + + /** + * Broadcasts a datagram on the socket. + * For connectionless sockets, the destination `port` and `address` must be + * specified. Connected sockets, on the other hand, will use their associated + * remote endpoint, so the `port` and `address` arguments must not be set. + * + * The `msg` argument contains the message to be sent. + * Depending on its type, different behavior can apply. If `msg` is a + * `Buffer`, any `TypedArray` or a `DataView`, + * the `offset` and `length` specify the offset within the `Buffer` where the + * message begins and the number of bytes in the message, respectively. + * If `msg` is a `String`, then it is automatically converted to a `Buffer` + * with `'utf8'` encoding. With messages that contain multi-byte characters, + * `offset` and `length` will be calculated with respect to `byte length` and + * not the character position. If `msg` is an array, `offset` and `length` + * must not be specified. + * + * The `address` argument is a string. If the value of `address` is a host + * name, DNS will be used to resolve the address of the host. If `address` + * is not provided or otherwise nullish, `'127.0.0.1'` (for `udp4` sockets) + * or `'::1'`(for `udp6` sockets) will be used by default. + * + * If the socket has not been previously bound with a call to `bind`, the + * socket is assigned a random port number and is bound to the "all + * interfaces" address (`'0.0.0.0'` for `udp4` sockets, `'::0'` for `udp6` + * sockets.) + * + * An optional `callback` function may be specified to as a way of + * reporting DNS errors or for determining when it is safe to reuse the `buf` + * object. DNS lookups delay the time to send for at least one tick of the + * Node.js event loop. + * + * The only way to know for sure that the datagram has been sent is by using + * a `callback`. If an error occurs and a `callback` is given, the error will + * be passed as the first argument to the `callback`. If a `callback` is not + * given, the error is emitted as an `'error'` event on the `socket` object. + * + * Offset and length are optional but both _must_ be set if either are used. + * They are supported only when the first argument is a `Buffer`, a + * `TypedArray`, or a `DataView`. + * + * This method throws `ERR_SOCKET_BAD_PORT` if called on an unbound socket. + * + * Example of sending a UDP packet to a port on `localhost`; + * + * ```js + * import dgram from "internal:deno_node/polyfills/dgram"; + * import { Buffer } from "internal:deno_node/polyfills/buffer"; + * + * const message = Buffer.from('Some bytes'); + * const client = dgram.createSocket('udp4'); + * client.send(message, 41234, 'localhost', (err) => { + * client.close(); + * }); + * ``` + * + * Example of sending a UDP packet composed of multiple buffers to a port on + * `127.0.0.1`; + * + * ```js + * import dgram from "internal:deno_node/polyfills/dgram"; + * import { Buffer } from "internal:deno_node/polyfills/buffer"; + * + * const buf1 = Buffer.from('Some '); + * const buf2 = Buffer.from('bytes'); + * const client = dgram.createSocket('udp4'); + * client.send([buf1, buf2], 41234, (err) => { + * client.close(); + * }); + * ``` + * + * Sending multiple buffers might be faster or slower depending on the + * application and operating system. Run benchmarks to + * determine the optimal strategy on a case-by-case basis. Generally + * speaking, however, sending multiple buffers is faster. + * + * Example of sending a UDP packet using a socket connected to a port on + * `localhost`: + * + * ```js + * import dgram from "internal:deno_node/polyfills/dgram"; + * import { Buffer } from "internal:deno_node/polyfills/buffer"; + * + * const message = Buffer.from('Some bytes'); + * const client = dgram.createSocket('udp4'); + * client.connect(41234, 'localhost', (err) => { + * client.send(message, (err) => { + * client.close(); + * }); + * }); + * ``` + * + * @param msg Message to be sent. + * @param offset Offset in the buffer where the message starts. + * @param length Number of bytes in the message. + * @param port Destination port. + * @param address Destination host name or IP address. + * @param callback Called when the message has been sent. + */ + send( + msg: MessageType | ReadonlyArray, + port?: number, + address?: string, + callback?: (error: ErrnoException | null, bytes?: number) => void, + ): void; + send( + msg: MessageType | ReadonlyArray, + port?: number, + callback?: (error: ErrnoException | null, bytes?: number) => void, + ): void; + send( + msg: MessageType | ReadonlyArray, + callback?: (error: ErrnoException | null, bytes?: number) => void, + ): void; + send( + msg: MessageType, + offset: number, + length: number, + port?: number, + address?: string, + callback?: (error: ErrnoException | null, bytes?: number) => void, + ): void; + send( + msg: MessageType, + offset: number, + length: number, + port?: number, + callback?: (error: ErrnoException | null, bytes?: number) => void, + ): void; + send( + msg: MessageType, + offset: number, + length: number, + callback?: (error: ErrnoException | null, bytes?: number) => void, + ): void; + send( + buffer: unknown, + offset?: unknown, + length?: unknown, + port?: unknown, + address?: unknown, + callback?: unknown, + ) { + let list: MessageType[] | null; + + const state = this[kStateSymbol]; + const connected = state.connectState === CONNECT_STATE_CONNECTED; + + if (!connected) { + if (address || (port && typeof port !== "function")) { + buffer = sliceBuffer( + buffer as MessageType, + offset as number, + length as number, + ); + } else { + callback = port; + port = offset; + address = length; + } + } else { + if (typeof length === "number") { + buffer = sliceBuffer(buffer as MessageType, offset as number, length); + + if (typeof port === "function") { + callback = port; + port = null; + } + } else { + callback = offset; + } + + if (port || address) { + throw new ERR_SOCKET_DGRAM_IS_CONNECTED(); + } + } + + if (!Array.isArray(buffer)) { + if (typeof buffer === "string") { + list = [Buffer.from(buffer)]; + } else if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + "buffer", + ["Buffer", "TypedArray", "DataView", "string"], + buffer, + ); + } else { + list = [buffer as MessageType]; + } + } else if (!(list = fixBufferList(buffer))) { + throw new ERR_INVALID_ARG_TYPE( + "buffer list arguments", + ["Buffer", "TypedArray", "DataView", "string"], + buffer, + ); + } + + if (!connected) { + port = validatePort(port, "Port", false); + } + + // Normalize callback so it's either a function or undefined but not anything + // else. + if (typeof callback !== "function") { + callback = undefined; + } + + if (typeof address === "function") { + callback = address; + address = undefined; + } else if (address && typeof address !== "string") { + throw new ERR_INVALID_ARG_TYPE("address", ["string", "falsy"], address); + } + + healthCheck(this); + + if (state.bindState === BIND_STATE_UNBOUND) { + this.bind({ port: 0, exclusive: true }); + } + + if (list.length === 0) { + list.push(Buffer.alloc(0)); + } + + // If the socket hasn't been bound yet, push the outbound packet onto the + // send queue and send after binding is complete. + if (state.bindState !== BIND_STATE_BOUND) { + // @ts-ignore mapping unknowns back onto themselves doesn't type nicely + enqueue(this, this.send.bind(this, list, port, address, callback)); + + return; + } + + const afterDns = (ex: ErrnoException | null, ip: string) => { + defaultTriggerAsyncIdScope( + this[asyncIdSymbol], + doSend, + ex, + this, + ip, + list, + address, + port, + callback, + ); + }; + + if (!connected) { + state.handle!.lookup(address as string, afterDns); + } else { + afterDns(null, ""); + } + } + + /** + * Sets or clears the `SO_BROADCAST` socket option. When set to `true`, UDP + * packets may be sent to a local interface's broadcast address. + * + * This method throws `EBADF` if called on an unbound socket. + */ + setBroadcast(arg: boolean) { + const err = this[kStateSymbol].handle!.setBroadcast(arg ? 1 : 0); + + if (err) { + throw errnoException(err, "setBroadcast"); + } + } + + /** + * _All references to scope in this section are referring to [IPv6 Zone Indices](https://en.wikipedia.org/wiki/IPv6_address#Scoped_literal_IPv6_addresses), which are defined by [RFC + * 4007](https://tools.ietf.org/html/rfc4007). In string form, an IP_ + * _with a scope index is written as `'IP%scope'` where scope is an interface name_ + * _or interface number._ + * + * Sets the default outgoing multicast interface of the socket to a chosen + * interface or back to system interface selection. The `multicastInterface` must + * be a valid string representation of an IP from the socket's family. + * + * For IPv4 sockets, this should be the IP configured for the desired physical + * interface. All packets sent to multicast on the socket will be sent on the + * interface determined by the most recent successful use of this call. + * + * For IPv6 sockets, `multicastInterface` should include a scope to indicate the + * interface as in the examples that follow. In IPv6, individual `send` calls can + * also use explicit scope in addresses, so only packets sent to a multicast + * address without specifying an explicit scope are affected by the most recent + * successful use of this call. + * + * This method throws `EBADF` if called on an unbound socket. + * + * #### Example: IPv6 outgoing multicast interface + * + * On most systems, where scope format uses the interface name: + * + * ```js + * const socket = dgram.createSocket('udp6'); + * + * socket.bind(1234, () => { + * socket.setMulticastInterface('::%eth1'); + * }); + * ``` + * + * On Windows, where scope format uses an interface number: + * + * ```js + * const socket = dgram.createSocket('udp6'); + * + * socket.bind(1234, () => { + * socket.setMulticastInterface('::%2'); + * }); + * ``` + * + * #### Example: IPv4 outgoing multicast interface + * + * All systems use an IP of the host on the desired physical interface: + * + * ```js + * const socket = dgram.createSocket('udp4'); + * + * socket.bind(1234, () => { + * socket.setMulticastInterface('10.0.0.2'); + * }); + * ``` + */ + setMulticastInterface(interfaceAddress: string) { + healthCheck(this); + validateString(interfaceAddress, "interfaceAddress"); + + const err = this[kStateSymbol].handle!.setMulticastInterface( + interfaceAddress, + ); + + if (err) { + throw errnoException(err, "setMulticastInterface"); + } + } + + /** + * Sets or clears the `IP_MULTICAST_LOOP` socket option. When set to `true`, + * multicast packets will also be received on the local interface. + * + * This method throws `EBADF` if called on an unbound socket. + */ + setMulticastLoopback(arg: boolean): typeof arg { + const err = this[kStateSymbol].handle!.setMulticastLoopback(arg ? 1 : 0); + + if (err) { + throw errnoException(err, "setMulticastLoopback"); + } + + return arg; // 0.4 compatibility + } + + /** + * Sets the `IP_MULTICAST_TTL` socket option. While TTL generally stands for + * "Time to Live", in this context it specifies the number of IP hops that a + * packet is allowed to travel through, specifically for multicast traffic. Each + * router or gateway that forwards a packet decrements the TTL. If the TTL is + * decremented to 0 by a router, it will not be forwarded. + * + * The `ttl` argument may be between 0 and 255\. The default on most systems is `1`. + * + * This method throws `EBADF` if called on an unbound socket. + */ + setMulticastTTL(ttl: number): typeof ttl { + validateNumber(ttl, "ttl"); + + const err = this[kStateSymbol].handle!.setMulticastTTL(ttl); + + if (err) { + throw errnoException(err, "setMulticastTTL"); + } + + return ttl; + } + + /** + * Sets the `SO_RCVBUF` socket option. Sets the maximum socket receive buffer + * in bytes. + * + * This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket. + */ + setRecvBufferSize(size: number) { + bufferSize(this, size, RECV_BUFFER); + } + + /** + * Sets the `SO_SNDBUF` socket option. Sets the maximum socket send buffer + * in bytes. + * + * This method throws `ERR_SOCKET_BUFFER_SIZE` if called on an unbound socket. + */ + setSendBufferSize(size: number) { + bufferSize(this, size, SEND_BUFFER); + } + + /** + * Sets the `IP_TTL` socket option. While TTL generally stands for "Time to Live", + * in this context it specifies the number of IP hops that a packet is allowed to + * travel through. Each router or gateway that forwards a packet decrements the + * TTL. If the TTL is decremented to 0 by a router, it will not be forwarded. + * Changing TTL values is typically done for network probes or when multicasting. + * + * The `ttl` argument may be between between 1 and 255\. The default on most systems + * is 64. + * + * This method throws `EBADF` if called on an unbound socket. + */ + setTTL(ttl: number): typeof ttl { + validateNumber(ttl, "ttl"); + + const err = this[kStateSymbol].handle!.setTTL(ttl); + + if (err) { + throw errnoException(err, "setTTL"); + } + + return ttl; + } + + /** + * By default, binding a socket will cause it to block the Node.js process from + * exiting as long as the socket is open. The `socket.unref()` method can be used + * to exclude the socket from the reference counting that keeps the Node.js + * process active, allowing the process to exit even if the socket is still + * listening. + * + * Calling `socket.unref()` multiple times will have no addition effect. + * + * The `socket.unref()` method returns a reference to the socket so calls can be + * chained. + */ + unref(): this { + const handle = this[kStateSymbol].handle; + + if (handle) { + handle.unref(); + } + + return this; + } +} + +/** + * Creates a `dgram.Socket` object. Once the socket is created, calling + * `socket.bind()` will instruct the socket to begin listening for datagram + * messages. When `address` and `port` are not passed to `socket.bind()` the + * method will bind the socket to the "all interfaces" address on a random port + * (it does the right thing for both `udp4` and `udp6` sockets). The bound + * address and port can be retrieved using `socket.address().address` and + * `socket.address().port`. + * + * If the `signal` option is enabled, calling `.abort()` on the corresponding + * `AbortController` is similar to calling `.close()` on the socket: + * + * ```js + * const controller = new AbortController(); + * const { signal } = controller; + * const server = dgram.createSocket({ type: 'udp4', signal }); + * server.on('message', (msg, rinfo) => { + * console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`); + * }); + * // Later, when you want to close the server. + * controller.abort(); + * ``` + * + * @param options + * @param callback Attached as a listener for `'message'` events. Optional. + */ +export function createSocket( + type: SocketType, + listener?: (msg: Buffer, rinfo: RemoteInfo) => void, +): Socket; +export function createSocket( + type: SocketOptions, + listener?: (msg: Buffer, rinfo: RemoteInfo) => void, +): Socket; +export function createSocket( + type: SocketType | SocketOptions, + listener?: (msg: Buffer, rinfo: RemoteInfo) => void, +): Socket { + return new Socket(type, listener); +} + +function startListening(socket: Socket) { + const state = socket[kStateSymbol]; + + state.handle!.onmessage = onMessage; + // Todo(@bartlomieju): handle errors + state.handle!.recvStart(); + state.receiving = true; + state.bindState = BIND_STATE_BOUND; + + if (state.recvBufferSize) { + bufferSize(socket, state.recvBufferSize, RECV_BUFFER); + } + + if (state.sendBufferSize) { + bufferSize(socket, state.sendBufferSize, SEND_BUFFER); + } + + socket.emit("listening"); +} + +function replaceHandle(self: Socket, newHandle: UDP) { + const state = self[kStateSymbol]; + const oldHandle = state.handle!; + + // Set up the handle that we got from primary. + newHandle.lookup = oldHandle.lookup; + newHandle.bind = oldHandle.bind; + newHandle.send = oldHandle.send; + newHandle[ownerSymbol] = self; + + // Replace the existing handle by the handle we got from primary. + oldHandle.close(); + state.handle = newHandle; +} + +function bufferSize(self: Socket, size: number, buffer: boolean): number { + if (size >>> 0 !== size) { + throw new ERR_SOCKET_BAD_BUFFER_SIZE(); + } + + const ctx = {}; + const ret = self[kStateSymbol].handle!.bufferSize(size, buffer, ctx); + + if (ret === undefined) { + throw new ERR_SOCKET_BUFFER_SIZE(ctx as NodeSystemErrorCtx); + } + + return ret; +} + +function socketCloseNT(self: Socket) { + self.emit("close"); +} + +function healthCheck(socket: Socket) { + if (!socket[kStateSymbol].handle) { + // Error message from dgram_legacy.js. + throw new ERR_SOCKET_DGRAM_NOT_RUNNING(); + } +} + +function stopReceiving(socket: Socket) { + const state = socket[kStateSymbol]; + + if (!state.receiving) { + return; + } + + state.handle!.recvStop(); + state.receiving = false; +} + +function onMessage( + nread: number, + handle: UDP, + buf?: Buffer, + rinfo?: RemoteInfo, +) { + const self = handle[ownerSymbol] as Socket; + + if (nread < 0) { + self.emit("error", errnoException(nread, "recvmsg")); + + return; + } + + rinfo!.size = buf!.length; // compatibility + + self.emit("message", buf, rinfo); +} + +function sliceBuffer(buffer: MessageType, offset: number, length: number) { + if (typeof buffer === "string") { + buffer = Buffer.from(buffer); + } else if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + "buffer", + ["Buffer", "TypedArray", "DataView", "string"], + buffer, + ); + } + + offset = offset >>> 0; + length = length >>> 0; + + if (offset > buffer.byteLength) { + throw new ERR_BUFFER_OUT_OF_BOUNDS("offset"); + } + + if (offset + length > buffer.byteLength) { + throw new ERR_BUFFER_OUT_OF_BOUNDS("length"); + } + + return Buffer.from(buffer.buffer, buffer.byteOffset + offset, length); +} + +function fixBufferList( + list: ReadonlyArray, +): Array | null { + const newList = new Array(list.length); + + for (let i = 0, l = list.length; i < l; i++) { + const buf = list[i]; + + if (typeof buf === "string") { + newList[i] = Buffer.from(buf); + } else if (!isArrayBufferView(buf)) { + return null; + } else { + newList[i] = Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength); + } + } + + return newList; +} + +function enqueue(self: Socket, toEnqueue: () => void) { + const state = self[kStateSymbol]; + + // If the send queue hasn't been initialized yet, do it, and install an + // event handler that flushes the send queue after binding is done. + if (state.queue === undefined) { + state.queue = []; + + self.once(EventEmitter.errorMonitor, onListenError); + self.once("listening", onListenSuccess); + } + + state.queue.push(toEnqueue); +} + +function onListenSuccess(this: Socket) { + this.removeListener(EventEmitter.errorMonitor, onListenError); + clearQueue.call(this); +} + +function onListenError(this: Socket) { + this.removeListener("listening", onListenSuccess); + this[kStateSymbol].queue = undefined; +} + +function clearQueue(this: Socket) { + const state = this[kStateSymbol]; + const queue = state.queue; + state.queue = undefined; + + // Flush the send queue. + for (const queueEntry of queue!) { + queueEntry(); + } +} + +function _connect( + this: Socket, + port: number, + address: string, + callback: (err?: ErrnoException) => void, +) { + const state = this[kStateSymbol]; + + if (callback) { + this.once("connect", callback); + } + + const afterDns = (ex: ErrnoException | null, ip: string) => { + defaultTriggerAsyncIdScope( + this[asyncIdSymbol], + doConnect, + ex, + this, + ip, + address, + port, + callback, + ); + }; + + state.handle!.lookup(address, afterDns); +} + +function doConnect( + ex: ErrnoException | null, + self: Socket, + ip: string, + address: string, + port: number, + callback: (err?: ErrnoException) => void, +) { + const state = self[kStateSymbol]; + + if (!state.handle) { + return; + } + + if (!ex) { + const err = state.handle.connect(ip, port); + + if (err) { + ex = exceptionWithHostPort(err, "connect", address, port); + } + } + + if (ex) { + state.connectState = CONNECT_STATE_DISCONNECTED; + + return nextTick(() => { + if (callback) { + self.removeListener("connect", callback); + + callback(ex!); + } else { + self.emit("error", ex); + } + }); + } + + state.connectState = CONNECT_STATE_CONNECTED; + + nextTick(() => self.emit("connect")); +} + +function doSend( + ex: ErrnoException | null, + self: Socket, + ip: string, + list: MessageType[], + address: string, + port: number, + callback?: (error: ErrnoException | null, bytes?: number) => void, +) { + const state = self[kStateSymbol]; + + if (ex) { + if (typeof callback === "function") { + nextTick(callback, ex); + + return; + } + + nextTick(() => self.emit("error", ex)); + + return; + } else if (!state.handle) { + return; + } + + const req = new SendWrap(); + req.list = list; // Keep reference alive. + req.address = address; + req.port = port; + + if (callback) { + req.callback = callback; + req.oncomplete = afterSend; + } + + let err; + + if (port) { + err = state.handle.send(req, list, list.length, port, ip, !!callback); + } else { + err = state.handle.send(req, list, list.length, !!callback); + } + + if (err >= 1) { + // Synchronous finish. The return code is msg_length + 1 so that we can + // distinguish between synchronous success and asynchronous success. + if (callback) { + nextTick(callback, null, err - 1); + } + + return; + } + + if (err && callback) { + // Don't emit as error, dgram_legacy.js compatibility + const ex = exceptionWithHostPort(err, "send", address, port); + + nextTick(callback, ex); + } +} + +function afterSend(this: SendWrap, err: number | null, sent?: number) { + let ex: ErrnoException | null; + + if (err) { + ex = exceptionWithHostPort(err, "send", this.address, this.port); + } else { + ex = null; + } + + this.callback(ex, sent); +} + +export type { SocketType }; + +export default { + createSocket, + Socket, +}; diff --git a/ext/node/polyfills/diagnostics_channel.ts b/ext/node/polyfills/diagnostics_channel.ts new file mode 100644 index 0000000000..c0918e4e24 --- /dev/null +++ b/ext/node/polyfills/diagnostics_channel.ts @@ -0,0 +1,89 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { nextTick } from "internal:deno_node/polyfills/process.ts"; + +type Subscriber = (message: unknown, name?: string) => void; + +export class Channel { + _subscribers: Subscriber[]; + name: string; + constructor(name: string) { + this._subscribers = []; + this.name = name; + } + + publish(message: unknown) { + for (const subscriber of this._subscribers) { + try { + subscriber(message, this.name); + } catch (err) { + nextTick(() => { + throw err; + }); + } + } + } + + subscribe(subscription: Subscriber) { + validateFunction(subscription, "subscription"); + + this._subscribers.push(subscription); + } + + unsubscribe(subscription: Subscriber) { + if (!this._subscribers.includes(subscription)) { + return false; + } + + this._subscribers.splice(this._subscribers.indexOf(subscription), 1); + + return true; + } + + get hasSubscribers() { + return this._subscribers.length > 0; + } +} + +const channels: Record = {}; + +export function channel(name: string) { + if (typeof name !== "string" && typeof name !== "symbol") { + throw new ERR_INVALID_ARG_TYPE("channel", ["string", "symbol"], name); + } + + if (!Object.hasOwn(channels, name)) { + channels[name] = new Channel(name); + } + + return channels[name]; +} + +export function hasSubscribers(name: string) { + if (!Object.hasOwn(channels, name)) { + return false; + } + + return channels[name].hasSubscribers; +} + +export function subscribe(name: string, subscription: Subscriber) { + const c = channel(name); + + return c.subscribe(subscription); +} + +export function unsubscribe(name: string, subscription: Subscriber) { + const c = channel(name); + + return c.unsubscribe(subscription); +} + +export default { + channel, + hasSubscribers, + subscribe, + unsubscribe, + Channel, +}; diff --git a/ext/node/polyfills/dns.ts b/ext/node/polyfills/dns.ts new file mode 100644 index 0000000000..7b8752e13d --- /dev/null +++ b/ext/node/polyfills/dns.ts @@ -0,0 +1,1012 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +import { customPromisifyArgs } from "internal:deno_node/polyfills/internal/util.mjs"; +import { + validateBoolean, + validateFunction, + validateNumber, + validateOneOf, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isIP } from "internal:deno_node/polyfills/internal/net.ts"; +import { + emitInvalidHostnameWarning, + getDefaultResolver, + getDefaultVerbatim, + isFamily, + isLookupCallback, + isLookupOptions, + isResolveCallback, + Resolver as CallbackResolver, + setDefaultResolver, + setDefaultResultOrder, + validateHints, +} from "internal:deno_node/polyfills/internal/dns/utils.ts"; +import type { + AnyAaaaRecord, + AnyARecord, + AnyCnameRecord, + AnyMxRecord, + AnyNaptrRecord, + AnyNsRecord, + AnyPtrRecord, + AnyRecord, + AnySoaRecord, + AnySrvRecord, + AnyTxtRecord, + CaaRecord, + LookupAddress, + LookupAllOptions, + LookupOneOptions, + LookupOptions, + MxRecord, + NaptrRecord, + Records, + RecordWithTtl, + ResolveCallback, + ResolveOptions, + ResolverOptions, + ResolveWithTtlOptions, + SoaRecord, + SrvRecord, +} from "internal:deno_node/polyfills/internal/dns/utils.ts"; +import promisesBase from "internal:deno_node/polyfills/internal/dns/promises.ts"; +import type { ErrnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + dnsException, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + AI_ADDRCONFIG as ADDRCONFIG, + AI_ALL as ALL, + AI_V4MAPPED as V4MAPPED, +} from "internal:deno_node/polyfills/internal_binding/ares.ts"; +import { + ChannelWrapQuery, + getaddrinfo, + GetAddrInfoReqWrap, + QueryReqWrap, +} from "internal:deno_node/polyfills/internal_binding/cares_wrap.ts"; +import { toASCII } from "internal:deno_node/polyfills/internal/idna.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +function onlookup( + this: GetAddrInfoReqWrap, + err: number | null, + addresses: string[], +) { + if (err) { + return this.callback(dnsException(err, "getaddrinfo", this.hostname)); + } + + this.callback(null, addresses[0], this.family || isIP(addresses[0])); +} + +function onlookupall( + this: GetAddrInfoReqWrap, + err: number | null, + addresses: string[], +) { + if (err) { + return this.callback(dnsException(err, "getaddrinfo", this.hostname)); + } + + const family = this.family; + const parsedAddresses = []; + + for (let i = 0; i < addresses.length; i++) { + const addr = addresses[i]; + parsedAddresses[i] = { + address: addr, + family: family || isIP(addr), + }; + } + + this.callback(null, parsedAddresses); +} + +type LookupCallback = ( + err: ErrnoException | null, + addressOrAddresses?: string | LookupAddress[] | null, + family?: number, +) => void; + +const validFamilies = [0, 4, 6]; + +// Easy DNS A/AAAA look up +// lookup(hostname, [options,] callback) +export function lookup( + hostname: string, + family: number, + callback: ( + err: ErrnoException | null, + address: string, + family: number, + ) => void, +): GetAddrInfoReqWrap | Record; +export function lookup( + hostname: string, + options: LookupOneOptions, + callback: ( + err: ErrnoException | null, + address: string, + family: number, + ) => void, +): GetAddrInfoReqWrap | Record; +export function lookup( + hostname: string, + options: LookupAllOptions, + callback: (err: ErrnoException | null, addresses: LookupAddress[]) => void, +): GetAddrInfoReqWrap | Record; +export function lookup( + hostname: string, + options: LookupOptions, + callback: ( + err: ErrnoException | null, + address: string | LookupAddress[], + family: number, + ) => void, +): GetAddrInfoReqWrap | Record; +export function lookup( + hostname: string, + callback: ( + err: ErrnoException | null, + address: string, + family: number, + ) => void, +): GetAddrInfoReqWrap | Record; +export function lookup( + hostname: string, + options: unknown, + callback?: unknown, +): GetAddrInfoReqWrap | Record { + let hints = 0; + let family = 0; + let all = false; + let verbatim = getDefaultVerbatim(); + + // Parse arguments + if (hostname) { + validateString(hostname, "hostname"); + } + + if (isLookupCallback(options)) { + callback = options; + family = 0; + } else if (isFamily(options)) { + validateFunction(callback, "callback"); + + validateOneOf(options, "family", validFamilies); + family = options; + } else if (!isLookupOptions(options)) { + validateFunction(arguments.length === 2 ? options : callback, "callback"); + + throw new ERR_INVALID_ARG_TYPE("options", ["integer", "object"], options); + } else { + validateFunction(callback, "callback"); + + if (options?.hints != null) { + validateNumber(options.hints, "options.hints"); + hints = options.hints >>> 0; + validateHints(hints); + } + + if (options?.family != null) { + validateOneOf(options.family, "options.family", validFamilies); + family = options.family; + } + + if (options?.all != null) { + validateBoolean(options.all, "options.all"); + all = options.all; + } + + if (options?.verbatim != null) { + validateBoolean(options.verbatim, "options.verbatim"); + verbatim = options.verbatim; + } + } + + if (!hostname) { + emitInvalidHostnameWarning(hostname); + + if (all) { + nextTick(callback as LookupCallback, null, []); + } else { + nextTick(callback as LookupCallback, null, null, family === 6 ? 6 : 4); + } + + return {}; + } + + const matchedFamily = isIP(hostname); + + if (matchedFamily) { + if (all) { + nextTick(callback as LookupCallback, null, [ + { address: hostname, family: matchedFamily }, + ]); + } else { + nextTick(callback as LookupCallback, null, hostname, matchedFamily); + } + + return {}; + } + + const req = new GetAddrInfoReqWrap(); + req.callback = callback as LookupCallback; + req.family = family; + req.hostname = hostname; + req.oncomplete = all ? onlookupall : onlookup; + + const err = getaddrinfo(req, toASCII(hostname), family, hints, verbatim); + + if (err) { + nextTick( + callback as LookupCallback, + dnsException(err, "getaddrinfo", hostname), + ); + + return {}; + } + + return req; +} + +Object.defineProperty(lookup, customPromisifyArgs, { + value: ["address", "family"], + enumerable: false, +}); + +function onresolve( + this: QueryReqWrap, + err: number, + records: Records, + ttls?: number[], +) { + if (err) { + this.callback(dnsException(err, this.bindingName, this.hostname)); + + return; + } + + const parsedRecords = ttls && this.ttl + ? (records as string[]).map((address: string, index: number) => ({ + address, + ttl: ttls[index], + })) + : records; + + this.callback(null, parsedRecords); +} + +function resolver(bindingName: keyof ChannelWrapQuery) { + function query( + this: Resolver, + name: string, + options: unknown, + callback?: unknown, + ): QueryReqWrap { + if (isResolveCallback(options)) { + callback = options; + options = {}; + } + + validateString(name, "name"); + validateFunction(callback, "callback"); + + const req = new QueryReqWrap(); + req.bindingName = bindingName; + req.callback = callback as ResolveCallback; + req.hostname = name; + req.oncomplete = onresolve; + + if (options && (options as ResolveOptions).ttl) { + notImplemented("dns.resolve* with ttl option"); + } + + req.ttl = !!(options && (options as ResolveOptions).ttl); + + const err = this._handle[bindingName](req, toASCII(name)); + + if (err) { + throw dnsException(err, bindingName, name); + } + + return req; + } + + Object.defineProperty(query, "name", { value: bindingName }); + + return query; +} + +const resolveMap = Object.create(null); + +export class Resolver extends CallbackResolver { + constructor(options?: ResolverOptions) { + super(options); + } + + // deno-lint-ignore no-explicit-any + [resolveMethod: string]: any; +} + +Resolver.prototype.resolveAny = resolveMap.ANY = resolver("queryAny"); +Resolver.prototype.resolve4 = resolveMap.A = resolver("queryA"); +Resolver.prototype.resolve6 = resolveMap.AAAA = resolver("queryAaaa"); +Resolver.prototype.resolveCaa = resolveMap.CAA = resolver("queryCaa"); +Resolver.prototype.resolveCname = resolveMap.CNAME = resolver("queryCname"); +Resolver.prototype.resolveMx = resolveMap.MX = resolver("queryMx"); +Resolver.prototype.resolveNs = resolveMap.NS = resolver("queryNs"); +Resolver.prototype.resolveTxt = resolveMap.TXT = resolver("queryTxt"); +Resolver.prototype.resolveSrv = resolveMap.SRV = resolver("querySrv"); +Resolver.prototype.resolvePtr = resolveMap.PTR = resolver("queryPtr"); +Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver("queryNaptr"); +Resolver.prototype.resolveSoa = resolveMap.SOA = resolver("querySoa"); +Resolver.prototype.reverse = resolver("getHostByAddr"); +Resolver.prototype.resolve = _resolve; + +function _resolve( + this: Resolver, + hostname: string, + rrtype: unknown, + callback?: unknown, +): QueryReqWrap { + let resolver: Resolver; + + if (typeof hostname !== "string") { + throw new ERR_INVALID_ARG_TYPE("name", "string", hostname); + } + + if (typeof rrtype === "string") { + resolver = resolveMap[rrtype]; + } else if (typeof rrtype === "function") { + resolver = resolveMap.A; + callback = rrtype; + } else { + throw new ERR_INVALID_ARG_TYPE("rrtype", "string", rrtype); + } + + if (typeof resolver === "function") { + return Reflect.apply(resolver, this, [hostname, callback]); + } + + throw new ERR_INVALID_ARG_VALUE("rrtype", rrtype); +} + +/** + * Sets the IP address and port of servers to be used when performing DNS + * resolution. The `servers` argument is an array of [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6) formatted + * addresses. If the port is the IANA default DNS port (53) it can be omitted. + * + * ```js + * dns.setServers([ + * '4.4.4.4', + * '[2001:4860:4860::8888]', + * '4.4.4.4:1053', + * '[2001:4860:4860::8888]:1053', + * ]); + * ``` + * + * An error will be thrown if an invalid address is provided. + * + * The `dns.setServers()` method must not be called while a DNS query is in + * progress. + * + * The `setServers` method affects only `resolve`,`dns.resolve*()` and `reverse` (and specifically _not_ `lookup`). + * + * This method works much like [resolve.conf](https://man7.org/linux/man-pages/man5/resolv.conf.5.html). + * That is, if attempting to resolve with the first server provided results in a + * `NOTFOUND` error, the `resolve()` method will _not_ attempt to resolve with + * subsequent servers provided. Fallback DNS servers will only be used if the + * earlier ones time out or result in some other error. + * + * @param servers array of `RFC 5952` formatted addresses + */ +export function setServers(servers: ReadonlyArray) { + const resolver = new Resolver(); + + resolver.setServers(servers); + setDefaultResolver(resolver); +} + +// The Node implementation uses `bindDefaultResolver` to set the follow methods +// on `module.exports` bound to the current `defaultResolver`. We don't have +// the same ability in ESM but can simulate this (at some cost) by explicitly +// exporting these methods which dynamically bind to the default resolver when +// called. + +/** + * Returns an array of IP address strings, formatted according to [RFC 5952](https://tools.ietf.org/html/rfc5952#section-6), + * that are currently configured for DNS resolution. A string will include a port + * section if a custom port is used. + * + * ```js + * [ + * '4.4.4.4', + * '2001:4860:4860::8888', + * '4.4.4.4:1053', + * '[2001:4860:4860::8888]:1053', + * ] + * ``` + */ +export function getServers(): string[] { + return Resolver.prototype.getServers.bind(getDefaultResolver())(); +} + +/** + * Uses the DNS protocol to resolve all records (also known as `ANY` or `*` query). + * The `ret` argument passed to the `callback` function will be an array containing + * various types of records. Each object has a property `type` that indicates the + * type of the current record. And depending on the `type`, additional properties + * will be present on the object. + * + * Here is an example of the `ret` object passed to the callback: + * + * ```js + * [ { type: 'A', address: '127.0.0.1', ttl: 299 }, + * { type: 'CNAME', value: 'example.com' }, + * { type: 'MX', exchange: 'alt4.aspmx.l.example.com', priority: 50 }, + * { type: 'NS', value: 'ns1.example.com' }, + * { type: 'TXT', entries: [ 'v=spf1 include:_spf.example.com ~all' ] }, + * { type: 'SOA', + * nsname: 'ns1.example.com', + * hostmaster: 'admin.example.com', + * serial: 156696742, + * refresh: 900, + * retry: 900, + * expire: 1800, + * minttl: 60 } ] + * ``` + * + * DNS server operators may choose not to respond to `ANY` queries. It may be + * better to call individual methods like `resolve4`, `resolveMx`, and so on. + * For more details, see [RFC 8482](https://tools.ietf.org/html/rfc8482). + */ +export function resolveAny( + hostname: string, + callback: (err: ErrnoException | null, addresses: AnyRecord[]) => void, +): QueryReqWrap; +export function resolveAny(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveAny.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve a IPv4 addresses (`A` records) for the + * `hostname`. The `addresses` argument passed to the `callback` function will + * contain an array of IPv4 addresses (e.g. `['74.125.79.104', '74.125.79.105','74.125.79.106']`). + * + * @param hostname Host name to resolve. + */ +export function resolve4( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, +): void; +export function resolve4( + hostname: string, + options: ResolveWithTtlOptions, + callback: (err: ErrnoException | null, addresses: RecordWithTtl[]) => void, +): void; +export function resolve4( + hostname: string, + options: ResolveOptions, + callback: ( + err: ErrnoException | null, + addresses: string[] | RecordWithTtl[], + ) => void, +): void; +export function resolve4( + hostname: string, + options: unknown, + callback?: unknown, +) { + return Resolver.prototype.resolve4.bind(getDefaultResolver() as Resolver)( + hostname, + options, + callback, + ); +} + +/** + * Uses the DNS protocol to resolve a IPv6 addresses (`AAAA` records) for the + * `hostname`. The `addresses` argument passed to the `callback` function + * will contain an array of IPv6 addresses. + * + * @param hostname Host name to resolve. + */ +export function resolve6( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, +): void; +export function resolve6( + hostname: string, + options: ResolveWithTtlOptions, + callback: (err: ErrnoException | null, addresses: RecordWithTtl[]) => void, +): void; +export function resolve6( + hostname: string, + options: ResolveOptions, + callback: ( + err: ErrnoException | null, + addresses: string[] | RecordWithTtl[], + ) => void, +): void; +export function resolve6( + hostname: string, + options: unknown, + callback?: unknown, +) { + return Resolver.prototype.resolve6.bind(getDefaultResolver() as Resolver)( + hostname, + options, + callback, + ); +} + +/** + * Uses the DNS protocol to resolve `CAA` records for the `hostname`. The + * `addresses` argument passed to the `callback` function will contain an array + * of certification authority authorization records available for the + * `hostname` (e.g. `[{critical: 0, iodef: 'mailto:pki@example.com'}, {critical: 128, issue: 'pki.example.com'}]`). + */ +export function resolveCaa( + hostname: string, + callback: (err: ErrnoException | null, records: CaaRecord[]) => void, +): QueryReqWrap; +export function resolveCaa(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveCaa.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve `CNAME` records for the `hostname`. The + * `addresses` argument passed to the `callback` function will contain an array + * of canonical name records available for the `hostname`(e.g. `['bar.example.com']`). + */ +export function resolveCname( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolveCname(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveCname.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve mail exchange records (`MX` records) for the + * `hostname`. The `addresses` argument passed to the `callback` function will + * contain an array of objects containing both a `priority` and `exchange` + * property (e.g. `[{priority: 10, exchange: 'mx.example.com'}, ...]`). + */ +export function resolveMx( + hostname: string, + callback: (err: ErrnoException | null, addresses: MxRecord[]) => void, +): QueryReqWrap; +export function resolveMx(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveMx.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve name server records (`NS` records) for the + * `hostname`. The `addresses` argument passed to the `callback` function will + * contain an array of name server records available for `hostname` + * (e.g. `['ns1.example.com', 'ns2.example.com']`). + */ +export function resolveNs( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolveNs(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveNs.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve text queries (`TXT` records) for the + * `hostname`. The `records` argument passed to the `callback` function is a + * two-dimensional array of the text records available for `hostname` + * (e.g.`[ ['v=spf1 ip4:0.0.0.0 ', '~all' ] ]`). Each sub-array contains TXT + * chunks of one record. Depending on the use case, these could be either + * joined together or treated separately. + */ +export function resolveTxt( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[][]) => void, +): QueryReqWrap; +export function resolveTxt(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveTxt.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve service records (`SRV` records) for the + * `hostname`. The `addresses` argument passed to the `callback` function will + * be an array of objects with the following properties: + * + * - `priority` + * - `weight` + * - `port` + * - `name` + * + * ```js + * { + * priority: 10, + * weight: 5, + * port: 21223, + * name: 'service.example.com' + * } + * ``` + */ +export function resolveSrv( + hostname: string, + callback: (err: ErrnoException | null, addresses: SrvRecord[]) => void, +): QueryReqWrap; +export function resolveSrv(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveSrv.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve pointer records (`PTR` records) for the + * `hostname`. The `addresses` argument passed to the `callback` function will + * be an array of strings containing the reply records. + */ +export function resolvePtr( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolvePtr(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolvePtr.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve regular expression based records (`NAPTR` + * records) for the `hostname`. The `addresses` argument passed to the + * `callback` function will contain an array of objects with the following + * properties: + * + * - `flags` + * - `service` + * - `regexp` + * - `replacement` + * - `order` + * - `preference` + * + * ```js + * { + * flags: 's', + * service: 'SIP+D2U', + * regexp: '', + * replacement: '_sip._udp.example.com', + * order: 30, + * preference: 100 + * } + * ``` + */ +export function resolveNaptr( + hostname: string, + callback: (err: ErrnoException | null, addresses: NaptrRecord[]) => void, +): QueryReqWrap; +export function resolveNaptr(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveNaptr.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve a start of authority record (`SOA` record) for + * the `hostname`. The `address` argument passed to the `callback` function will + * be an object with the following properties: + * + * - `nsname` + * - `hostmaster` + * - `serial` + * - `refresh` + * - `retry` + * - `expire` + * - `minttl` + * + * ```js + * { + * nsname: 'ns.example.com', + * hostmaster: 'root.example.com', + * serial: 2013101809, + * refresh: 10000, + * retry: 2400, + * expire: 604800, + * minttl: 3600 + * } + * ``` + */ +export function resolveSoa( + hostname: string, + callback: (err: ErrnoException | null, address: SoaRecord) => void, +): QueryReqWrap; +export function resolveSoa(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.resolveSoa.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Performs a reverse DNS query that resolves an IPv4 or IPv6 address to an + * array of host names. + * + * On error, `err` is an `Error` object, where `err.code` is + * one of the `DNS error codes`. + */ +export function reverse( + ip: string, + callback: (err: ErrnoException | null, hostnames: string[]) => void, +): QueryReqWrap; +export function reverse(...args: unknown[]): QueryReqWrap { + return Resolver.prototype.reverse.bind(getDefaultResolver() as Resolver)( + ...args, + ); +} + +/** + * Uses the DNS protocol to resolve a host name (e.g. `'nodejs.org'`) into an array + * of the resource records. The `callback` function has arguments`(err, records)`.] + * When successful, `records` will be an array of resource + * records. The type and structure of individual results varies based on `rrtype`. + * + * On error, `err` is an `Error` object, where `err.code` is one of the DNS error codes. + * + * @param hostname Host name to resolve. + * @param [rrtype='A'] Resource record type. + */ +export function resolve( + hostname: string, + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "A", + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "AAAA", + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "ANY", + callback: (err: ErrnoException | null, addresses: AnyRecord[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "CNAME", + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "MX", + callback: (err: ErrnoException | null, addresses: MxRecord[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "NAPTR", + callback: (err: ErrnoException | null, addresses: NaptrRecord[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "NS", + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "PTR", + callback: (err: ErrnoException | null, addresses: string[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "SOA", + callback: (err: ErrnoException | null, addresses: SoaRecord) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "SRV", + callback: (err: ErrnoException | null, addresses: SrvRecord[]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: "TXT", + callback: (err: ErrnoException | null, addresses: string[][]) => void, +): QueryReqWrap; +export function resolve( + hostname: string, + rrtype: string, + callback: ( + err: ErrnoException | null, + addresses: + | string[] + | MxRecord[] + | NaptrRecord[] + | SoaRecord + | SrvRecord[] + | string[][] + | AnyRecord[], + ) => void, +): QueryReqWrap; +export function resolve(hostname: string, rrtype: unknown, callback?: unknown) { + return Resolver.prototype.resolve.bind(getDefaultResolver() as Resolver)( + hostname, + rrtype, + callback, + ); +} + +// ERROR CODES +export const NODATA = "ENODATA"; +export const FORMERR = "EFORMERR"; +export const SERVFAIL = "ESERVFAIL"; +export const NOTFOUND = "ENOTFOUND"; +export const NOTIMP = "ENOTIMP"; +export const REFUSED = "EREFUSED"; +export const BADQUERY = "EBADQUERY"; +export const BADNAME = "EBADNAME"; +export const BADFAMILY = "EBADFAMILY"; +export const BADRESP = "EBADRESP"; +export const CONNREFUSED = "ECONNREFUSED"; +export const TIMEOUT = "ETIMEOUT"; +export const EOF = "EOF"; +export const FILE = "EFILE"; +export const NOMEM = "ENOMEM"; +export const DESTRUCTION = "EDESTRUCTION"; +export const BADSTR = "EBADSTR"; +export const BADFLAGS = "EBADFLAGS"; +export const NONAME = "ENONAME"; +export const BADHINTS = "EBADHINTS"; +export const NOTINITIALIZED = "ENOTINITIALIZED"; +export const LOADIPHLPAPI = "ELOADIPHLPAPI"; +export const ADDRGETNETWORKPARAMS = "EADDRGETNETWORKPARAMS"; +export const CANCELLED = "ECANCELLED"; + +const promises = { + ...promisesBase, + setDefaultResultOrder, + setServers, + + // ERROR CODES + NODATA, + FORMERR, + SERVFAIL, + NOTFOUND, + NOTIMP, + REFUSED, + BADQUERY, + BADNAME, + BADFAMILY, + BADRESP, + CONNREFUSED, + TIMEOUT, + EOF, + FILE, + NOMEM, + DESTRUCTION, + BADSTR, + BADFLAGS, + NONAME, + BADHINTS, + NOTINITIALIZED, + LOADIPHLPAPI, + ADDRGETNETWORKPARAMS, + CANCELLED, +}; + +export { ADDRCONFIG, ALL, promises, setDefaultResultOrder, V4MAPPED }; + +export type { + AnyAaaaRecord, + AnyARecord, + AnyCnameRecord, + AnyMxRecord, + AnyNaptrRecord, + AnyNsRecord, + AnyPtrRecord, + AnyRecord, + AnySoaRecord, + AnySrvRecord, + AnyTxtRecord, + CaaRecord, + LookupAddress, + LookupAllOptions, + LookupOneOptions, + LookupOptions, + MxRecord, + NaptrRecord, + Records, + RecordWithTtl, + ResolveCallback, + ResolveOptions, + ResolverOptions, + ResolveWithTtlOptions, + SoaRecord, + SrvRecord, +}; + +export default { + ADDRCONFIG, + ALL, + V4MAPPED, + lookup, + getServers, + resolveAny, + resolve4, + resolve6, + resolveCaa, + resolveCname, + resolveMx, + resolveNs, + resolveTxt, + resolveSrv, + resolvePtr, + resolveNaptr, + resolveSoa, + resolve, + Resolver, + reverse, + setServers, + setDefaultResultOrder, + promises, + NODATA, + FORMERR, + SERVFAIL, + NOTFOUND, + NOTIMP, + REFUSED, + BADQUERY, + BADNAME, + BADFAMILY, + BADRESP, + CONNREFUSED, + TIMEOUT, + EOF, + FILE, + NOMEM, + DESTRUCTION, + BADSTR, + BADFLAGS, + NONAME, + BADHINTS, + NOTINITIALIZED, + LOADIPHLPAPI, + ADDRGETNETWORKPARAMS, + CANCELLED, +}; diff --git a/ext/node/polyfills/dns/promises.ts b/ext/node/polyfills/dns/promises.ts new file mode 100644 index 0000000000..0de6a488bf --- /dev/null +++ b/ext/node/polyfills/dns/promises.ts @@ -0,0 +1,70 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. +import { promises } from "internal:deno_node/polyfills/dns.ts"; +export const { + getServers, + lookup, + resolve, + resolve4, + resolve6, + resolveAny, + resolveCaa, + resolveCname, + resolveMx, + resolveNaptr, + resolveNs, + resolvePtr, + Resolver, + resolveSoa, + resolveSrv, + resolveTxt, + reverse, + setDefaultResultOrder, + setServers, + + // ERROR CODES + NODATA, + FORMERR, + SERVFAIL, + NOTFOUND, + NOTIMP, + REFUSED, + BADQUERY, + BADNAME, + BADFAMILY, + BADRESP, + CONNREFUSED, + TIMEOUT, + EOF, + FILE, + NOMEM, + DESTRUCTION, + BADSTR, + BADFLAGS, + NONAME, + BADHINTS, + NOTINITIALIZED, + LOADIPHLPAPI, + ADDRGETNETWORKPARAMS, + CANCELLED, +} = promises; +export default promises; diff --git a/ext/node/polyfills/domain.ts b/ext/node/polyfills/domain.ts new file mode 100644 index 0000000000..ea0aed728d --- /dev/null +++ b/ext/node/polyfills/domain.ts @@ -0,0 +1,17 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export function create() { + notImplemented("domain.create"); +} +export class Domain { + constructor() { + notImplemented("domain.Domain.prototype.constructor"); + } +} +export default { + create, + Domain, +}; diff --git a/ext/node/polyfills/events.ts b/ext/node/polyfills/events.ts new file mode 100644 index 0000000000..5b43faa101 --- /dev/null +++ b/ext/node/polyfills/events.ts @@ -0,0 +1,14 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// @deno-types="./_events.d.ts" +export { + captureRejectionSymbol, + default, + defaultMaxListeners, + errorMonitor, + EventEmitter, + getEventListeners, + listenerCount, + on, + once, + setMaxListeners, +} from "internal:deno_node/polyfills/_events.mjs"; diff --git a/ext/node/polyfills/fs.ts b/ext/node/polyfills/fs.ts new file mode 100644 index 0000000000..b67d2facdf --- /dev/null +++ b/ext/node/polyfills/fs.ts @@ -0,0 +1,431 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + access, + accessPromise, + accessSync, +} from "internal:deno_node/polyfills/_fs/_fs_access.ts"; +import { + appendFile, + appendFilePromise, + appendFileSync, +} from "internal:deno_node/polyfills/_fs/_fs_appendFile.ts"; +import { + chmod, + chmodPromise, + chmodSync, +} from "internal:deno_node/polyfills/_fs/_fs_chmod.ts"; +import { + chown, + chownPromise, + chownSync, +} from "internal:deno_node/polyfills/_fs/_fs_chown.ts"; +import { + close, + closeSync, +} from "internal:deno_node/polyfills/_fs/_fs_close.ts"; +import * as constants from "internal:deno_node/polyfills/_fs/_fs_constants.ts"; +import { + copyFile, + copyFilePromise, + copyFileSync, +} from "internal:deno_node/polyfills/_fs/_fs_copy.ts"; +import Dir from "internal:deno_node/polyfills/_fs/_fs_dir.ts"; +import Dirent from "internal:deno_node/polyfills/_fs/_fs_dirent.ts"; +import { + exists, + existsSync, +} from "internal:deno_node/polyfills/_fs/_fs_exists.ts"; +import { + fdatasync, + fdatasyncSync, +} from "internal:deno_node/polyfills/_fs/_fs_fdatasync.ts"; +import { + fstat, + fstatSync, +} from "internal:deno_node/polyfills/_fs/_fs_fstat.ts"; +import { + fsync, + fsyncSync, +} from "internal:deno_node/polyfills/_fs/_fs_fsync.ts"; +import { + ftruncate, + ftruncateSync, +} from "internal:deno_node/polyfills/_fs/_fs_ftruncate.ts"; +import { + futimes, + futimesSync, +} from "internal:deno_node/polyfills/_fs/_fs_futimes.ts"; +import { + link, + linkPromise, + linkSync, +} from "internal:deno_node/polyfills/_fs/_fs_link.ts"; +import { + lstat, + lstatPromise, + lstatSync, +} from "internal:deno_node/polyfills/_fs/_fs_lstat.ts"; +import { + mkdir, + mkdirPromise, + mkdirSync, +} from "internal:deno_node/polyfills/_fs/_fs_mkdir.ts"; +import { + mkdtemp, + mkdtempPromise, + mkdtempSync, +} from "internal:deno_node/polyfills/_fs/_fs_mkdtemp.ts"; +import { + open, + openPromise, + openSync, +} from "internal:deno_node/polyfills/_fs/_fs_open.ts"; +import { + opendir, + opendirPromise, + opendirSync, +} from "internal:deno_node/polyfills/_fs/_fs_opendir.ts"; +import { read, readSync } from "internal:deno_node/polyfills/_fs/_fs_read.ts"; +import { + readdir, + readdirPromise, + readdirSync, +} from "internal:deno_node/polyfills/_fs/_fs_readdir.ts"; +import { + readFile, + readFilePromise, + readFileSync, +} from "internal:deno_node/polyfills/_fs/_fs_readFile.ts"; +import { + readlink, + readlinkPromise, + readlinkSync, +} from "internal:deno_node/polyfills/_fs/_fs_readlink.ts"; +import { + realpath, + realpathPromise, + realpathSync, +} from "internal:deno_node/polyfills/_fs/_fs_realpath.ts"; +import { + rename, + renamePromise, + renameSync, +} from "internal:deno_node/polyfills/_fs/_fs_rename.ts"; +import { + rmdir, + rmdirPromise, + rmdirSync, +} from "internal:deno_node/polyfills/_fs/_fs_rmdir.ts"; +import { + rm, + rmPromise, + rmSync, +} from "internal:deno_node/polyfills/_fs/_fs_rm.ts"; +import { + stat, + statPromise, + statSync, +} from "internal:deno_node/polyfills/_fs/_fs_stat.ts"; +import { + symlink, + symlinkPromise, + symlinkSync, +} from "internal:deno_node/polyfills/_fs/_fs_symlink.ts"; +import { + truncate, + truncatePromise, + truncateSync, +} from "internal:deno_node/polyfills/_fs/_fs_truncate.ts"; +import { + unlink, + unlinkPromise, + unlinkSync, +} from "internal:deno_node/polyfills/_fs/_fs_unlink.ts"; +import { + utimes, + utimesPromise, + utimesSync, +} from "internal:deno_node/polyfills/_fs/_fs_utimes.ts"; +import { + unwatchFile, + watch, + watchFile, + watchPromise, +} from "internal:deno_node/polyfills/_fs/_fs_watch.ts"; +// @deno-types="./_fs/_fs_write.d.ts" +import { + write, + writeSync, +} from "internal:deno_node/polyfills/_fs/_fs_write.mjs"; +// @deno-types="./_fs/_fs_writev.d.ts" +import { + writev, + writevSync, +} from "internal:deno_node/polyfills/_fs/_fs_writev.mjs"; +import { + writeFile, + writeFilePromise, + writeFileSync, +} from "internal:deno_node/polyfills/_fs/_fs_writeFile.ts"; +import { Stats } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +// @deno-types="./internal/fs/streams.d.ts" +import { + createReadStream, + createWriteStream, + ReadStream, + WriteStream, +} from "internal:deno_node/polyfills/internal/fs/streams.mjs"; + +const { + F_OK, + R_OK, + W_OK, + X_OK, + O_RDONLY, + O_WRONLY, + O_RDWR, + O_NOCTTY, + O_TRUNC, + O_APPEND, + O_DIRECTORY, + O_NOFOLLOW, + O_SYNC, + O_DSYNC, + O_SYMLINK, + O_NONBLOCK, + O_CREAT, + O_EXCL, +} = constants; + +const promises = { + access: accessPromise, + copyFile: copyFilePromise, + open: openPromise, + opendir: opendirPromise, + rename: renamePromise, + truncate: truncatePromise, + rm: rmPromise, + rmdir: rmdirPromise, + mkdir: mkdirPromise, + readdir: readdirPromise, + readlink: readlinkPromise, + symlink: symlinkPromise, + lstat: lstatPromise, + stat: statPromise, + link: linkPromise, + unlink: unlinkPromise, + chmod: chmodPromise, + // lchmod: promisify(lchmod), + // lchown: promisify(lchown), + chown: chownPromise, + utimes: utimesPromise, + // lutimes = promisify(lutimes), + realpath: realpathPromise, + mkdtemp: mkdtempPromise, + writeFile: writeFilePromise, + appendFile: appendFilePromise, + readFile: readFilePromise, + watch: watchPromise, +}; + +export default { + access, + accessSync, + appendFile, + appendFileSync, + chmod, + chmodSync, + chown, + chownSync, + close, + closeSync, + constants, + copyFile, + copyFileSync, + createReadStream, + createWriteStream, + Dir, + Dirent, + exists, + existsSync, + F_OK, + fdatasync, + fdatasyncSync, + fstat, + fstatSync, + fsync, + fsyncSync, + ftruncate, + ftruncateSync, + futimes, + futimesSync, + link, + linkSync, + lstat, + lstatSync, + mkdir, + mkdirSync, + mkdtemp, + mkdtempSync, + O_APPEND, + O_CREAT, + O_DIRECTORY, + O_DSYNC, + O_EXCL, + O_NOCTTY, + O_NOFOLLOW, + O_NONBLOCK, + O_RDONLY, + O_RDWR, + O_SYMLINK, + O_SYNC, + O_TRUNC, + O_WRONLY, + open, + openSync, + opendir, + opendirSync, + read, + readSync, + promises, + R_OK, + readdir, + readdirSync, + readFile, + readFileSync, + readlink, + readlinkSync, + ReadStream, + realpath, + realpathSync, + rename, + renameSync, + rmdir, + rmdirSync, + rm, + rmSync, + stat, + Stats, + statSync, + symlink, + symlinkSync, + truncate, + truncateSync, + unlink, + unlinkSync, + unwatchFile, + utimes, + utimesSync, + W_OK, + watch, + watchFile, + write, + writeFile, + writev, + writevSync, + writeFileSync, + WriteStream, + writeSync, + X_OK, +}; + +export { + access, + accessSync, + appendFile, + appendFileSync, + chmod, + chmodSync, + chown, + chownSync, + close, + closeSync, + constants, + copyFile, + copyFileSync, + createReadStream, + createWriteStream, + Dir, + Dirent, + exists, + existsSync, + F_OK, + fdatasync, + fdatasyncSync, + fstat, + fstatSync, + fsync, + fsyncSync, + ftruncate, + ftruncateSync, + futimes, + futimesSync, + link, + linkSync, + lstat, + lstatSync, + mkdir, + mkdirSync, + mkdtemp, + mkdtempSync, + O_APPEND, + O_CREAT, + O_DIRECTORY, + O_DSYNC, + O_EXCL, + O_NOCTTY, + O_NOFOLLOW, + O_NONBLOCK, + O_RDONLY, + O_RDWR, + O_SYMLINK, + O_SYNC, + O_TRUNC, + O_WRONLY, + open, + opendir, + opendirSync, + openSync, + promises, + R_OK, + read, + readdir, + readdirSync, + readFile, + readFileSync, + readlink, + readlinkSync, + ReadStream, + readSync, + realpath, + realpathSync, + rename, + renameSync, + rm, + rmdir, + rmdirSync, + rmSync, + stat, + Stats, + statSync, + symlink, + symlinkSync, + truncate, + truncateSync, + unlink, + unlinkSync, + unwatchFile, + utimes, + utimesSync, + W_OK, + watch, + watchFile, + write, + writeFile, + writeFileSync, + WriteStream, + writeSync, + writev, + writevSync, + X_OK, +}; diff --git a/ext/node/polyfills/fs/promises.ts b/ext/node/polyfills/fs/promises.ts new file mode 100644 index 0000000000..2f4687661d --- /dev/null +++ b/ext/node/polyfills/fs/promises.ts @@ -0,0 +1,33 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { promises as fsPromises } from "internal:deno_node/polyfills/fs.ts"; + +export const access = fsPromises.access; +export const copyFile = fsPromises.copyFile; +export const open = fsPromises.open; +export const opendir = fsPromises.opendir; +export const rename = fsPromises.rename; +export const truncate = fsPromises.truncate; +export const rm = fsPromises.rm; +export const rmdir = fsPromises.rmdir; +export const mkdir = fsPromises.mkdir; +export const readdir = fsPromises.readdir; +export const readlink = fsPromises.readlink; +export const symlink = fsPromises.symlink; +export const lstat = fsPromises.lstat; +export const stat = fsPromises.stat; +export const link = fsPromises.link; +export const unlink = fsPromises.unlink; +export const chmod = fsPromises.chmod; +// export const lchmod = fs.lchmod; +// export const lchown = fs.lchown; +export const chown = fsPromises.chown; +export const utimes = fsPromises.utimes; +// export const lutimes = fs.lutimes; +export const realpath = fsPromises.realpath; +export const mkdtemp = fsPromises.mkdtemp; +export const writeFile = fsPromises.writeFile; +export const appendFile = fsPromises.appendFile; +export const readFile = fsPromises.readFile; +export const watch = fsPromises.watch; + +export default fsPromises; diff --git a/ext/node/polyfills/global.ts b/ext/node/polyfills/global.ts new file mode 100644 index 0000000000..f9cb9186f0 --- /dev/null +++ b/ext/node/polyfills/global.ts @@ -0,0 +1,91 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file no-var +import processModule from "internal:deno_node/polyfills/process.ts"; +import { Buffer as bufferModule } from "internal:deno_node/polyfills/buffer.ts"; +import { + clearInterval, + clearTimeout, + setInterval, + setTimeout, +} from "internal:deno_node/polyfills/timers.ts"; +import timers from "internal:deno_node/polyfills/timers.ts"; + +type GlobalType = { + process: typeof processModule; + Buffer: typeof bufferModule; + setImmediate: typeof timers.setImmediate; + clearImmediate: typeof timers.clearImmediate; + setTimeout: typeof timers.setTimeout; + clearTimeout: typeof timers.clearTimeout; + setInterval: typeof timers.setInterval; + clearInterval: typeof timers.clearInterval; +}; + +declare global { + interface Window { + global: GlobalType; + } + + interface globalThis { + global: GlobalType; + } + + var global: GlobalType; + var process: typeof processModule; + var Buffer: typeof bufferModule; + type Buffer = bufferModule; + var setImmediate: typeof timers.setImmediate; + var clearImmediate: typeof timers.clearImmediate; +} + +Object.defineProperty(globalThis, "global", { + value: new Proxy(globalThis, { + get(target, prop, receiver) { + switch (prop) { + case "setInterval": + return setInterval; + case "setTimeout": + return setTimeout; + case "clearInterval": + return clearInterval; + case "clearTimeout": + return clearTimeout; + default: + return Reflect.get(target, prop, receiver); + } + }, + }), + writable: false, + enumerable: false, + configurable: true, +}); + +Object.defineProperty(globalThis, "process", { + value: processModule, + enumerable: false, + writable: true, + configurable: true, +}); + +Object.defineProperty(globalThis, "Buffer", { + value: bufferModule, + enumerable: false, + writable: true, + configurable: true, +}); + +Object.defineProperty(globalThis, "setImmediate", { + value: timers.setImmediate, + enumerable: true, + writable: true, + configurable: true, +}); + +Object.defineProperty(globalThis, "clearImmediate", { + value: timers.clearImmediate, + enumerable: true, + writable: true, + configurable: true, +}); + +export {}; diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts new file mode 100644 index 0000000000..4bfc1e1d3f --- /dev/null +++ b/ext/node/polyfills/http.ts @@ -0,0 +1,940 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { TextEncoder } from "internal:deno_web/08_text_encoding.js"; +import { + type Deferred, + deferred, +} from "internal:deno_node/polyfills/_util/async.ts"; +import { + _normalizeArgs, + ListenOptions, + Socket, +} from "internal:deno_node/polyfills/net.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { ERR_SERVER_NOT_RUNNING } from "internal:deno_node/polyfills/internal/errors.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +import { validatePort } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + Readable as NodeReadable, + Writable as NodeWritable, +} from "internal:deno_node/polyfills/stream.ts"; +import { OutgoingMessage } from "internal:deno_node/polyfills/_http_outgoing.ts"; +import { Agent } from "internal:deno_node/polyfills/_http_agent.mjs"; +import { chunkExpression as RE_TE_CHUNKED } from "internal:deno_node/polyfills/_http_common.ts"; +import { urlToHttpOptions } from "internal:deno_node/polyfills/internal/url.ts"; +import { + constants, + TCP, +} from "internal:deno_node/polyfills/internal_binding/tcp_wrap.ts"; + +enum STATUS_CODES { + /** RFC 7231, 6.2.1 */ + Continue = 100, + /** RFC 7231, 6.2.2 */ + SwitchingProtocols = 101, + /** RFC 2518, 10.1 */ + Processing = 102, + /** RFC 8297 **/ + EarlyHints = 103, + + /** RFC 7231, 6.3.1 */ + OK = 200, + /** RFC 7231, 6.3.2 */ + Created = 201, + /** RFC 7231, 6.3.3 */ + Accepted = 202, + /** RFC 7231, 6.3.4 */ + NonAuthoritativeInfo = 203, + /** RFC 7231, 6.3.5 */ + NoContent = 204, + /** RFC 7231, 6.3.6 */ + ResetContent = 205, + /** RFC 7233, 4.1 */ + PartialContent = 206, + /** RFC 4918, 11.1 */ + MultiStatus = 207, + /** RFC 5842, 7.1 */ + AlreadyReported = 208, + /** RFC 3229, 10.4.1 */ + IMUsed = 226, + + /** RFC 7231, 6.4.1 */ + MultipleChoices = 300, + /** RFC 7231, 6.4.2 */ + MovedPermanently = 301, + /** RFC 7231, 6.4.3 */ + Found = 302, + /** RFC 7231, 6.4.4 */ + SeeOther = 303, + /** RFC 7232, 4.1 */ + NotModified = 304, + /** RFC 7231, 6.4.5 */ + UseProxy = 305, + /** RFC 7231, 6.4.7 */ + TemporaryRedirect = 307, + /** RFC 7538, 3 */ + PermanentRedirect = 308, + + /** RFC 7231, 6.5.1 */ + BadRequest = 400, + /** RFC 7235, 3.1 */ + Unauthorized = 401, + /** RFC 7231, 6.5.2 */ + PaymentRequired = 402, + /** RFC 7231, 6.5.3 */ + Forbidden = 403, + /** RFC 7231, 6.5.4 */ + NotFound = 404, + /** RFC 7231, 6.5.5 */ + MethodNotAllowed = 405, + /** RFC 7231, 6.5.6 */ + NotAcceptable = 406, + /** RFC 7235, 3.2 */ + ProxyAuthRequired = 407, + /** RFC 7231, 6.5.7 */ + RequestTimeout = 408, + /** RFC 7231, 6.5.8 */ + Conflict = 409, + /** RFC 7231, 6.5.9 */ + Gone = 410, + /** RFC 7231, 6.5.10 */ + LengthRequired = 411, + /** RFC 7232, 4.2 */ + PreconditionFailed = 412, + /** RFC 7231, 6.5.11 */ + RequestEntityTooLarge = 413, + /** RFC 7231, 6.5.12 */ + RequestURITooLong = 414, + /** RFC 7231, 6.5.13 */ + UnsupportedMediaType = 415, + /** RFC 7233, 4.4 */ + RequestedRangeNotSatisfiable = 416, + /** RFC 7231, 6.5.14 */ + ExpectationFailed = 417, + /** RFC 7168, 2.3.3 */ + Teapot = 418, + /** RFC 7540, 9.1.2 */ + MisdirectedRequest = 421, + /** RFC 4918, 11.2 */ + UnprocessableEntity = 422, + /** RFC 4918, 11.3 */ + Locked = 423, + /** RFC 4918, 11.4 */ + FailedDependency = 424, + /** RFC 8470, 5.2 */ + TooEarly = 425, + /** RFC 7231, 6.5.15 */ + UpgradeRequired = 426, + /** RFC 6585, 3 */ + PreconditionRequired = 428, + /** RFC 6585, 4 */ + TooManyRequests = 429, + /** RFC 6585, 5 */ + RequestHeaderFieldsTooLarge = 431, + /** RFC 7725, 3 */ + UnavailableForLegalReasons = 451, + + /** RFC 7231, 6.6.1 */ + InternalServerError = 500, + /** RFC 7231, 6.6.2 */ + NotImplemented = 501, + /** RFC 7231, 6.6.3 */ + BadGateway = 502, + /** RFC 7231, 6.6.4 */ + ServiceUnavailable = 503, + /** RFC 7231, 6.6.5 */ + GatewayTimeout = 504, + /** RFC 7231, 6.6.6 */ + HTTPVersionNotSupported = 505, + /** RFC 2295, 8.1 */ + VariantAlsoNegotiates = 506, + /** RFC 4918, 11.5 */ + InsufficientStorage = 507, + /** RFC 5842, 7.2 */ + LoopDetected = 508, + /** RFC 2774, 7 */ + NotExtended = 510, + /** RFC 6585, 6 */ + NetworkAuthenticationRequired = 511, +} + +const METHODS = [ + "ACL", + "BIND", + "CHECKOUT", + "CONNECT", + "COPY", + "DELETE", + "GET", + "HEAD", + "LINK", + "LOCK", + "M-SEARCH", + "MERGE", + "MKACTIVITY", + "MKCALENDAR", + "MKCOL", + "MOVE", + "NOTIFY", + "OPTIONS", + "PATCH", + "POST", + "PROPFIND", + "PROPPATCH", + "PURGE", + "PUT", + "REBIND", + "REPORT", + "SEARCH", + "SOURCE", + "SUBSCRIBE", + "TRACE", + "UNBIND", + "UNLINK", + "UNLOCK", + "UNSUBSCRIBE", +]; + +type Chunk = string | Buffer | Uint8Array; + +// @ts-ignore Deno[Deno.internal] is used on purpose here +const DenoServe = Deno[Deno.internal]?.nodeUnstable?.serve || Deno.serve; +// @ts-ignore Deno[Deno.internal] is used on purpose here +const DenoUpgradeHttpRaw = Deno[Deno.internal]?.nodeUnstable?.upgradeHttpRaw || + Deno.upgradeHttpRaw; + +const ENCODER = new TextEncoder(); + +export interface RequestOptions { + agent?: Agent; + auth?: string; + createConnection?: () => unknown; + defaultPort?: number; + family?: number; + headers?: Record; + hints?: number; + host?: string; + hostname?: string; + insecureHTTPParser?: boolean; + localAddress?: string; + localPort?: number; + lookup?: () => void; + maxHeaderSize?: number; + method?: string; + path?: string; + port?: number; + protocol?: string; + setHost?: boolean; + socketPath?: string; + timeout?: number; + signal?: AbortSignal; + href?: string; +} + +// TODO(@bartlomieju): Implement ClientRequest methods (e.g. setHeader()) +/** ClientRequest represents the http(s) request from the client */ +class ClientRequest extends NodeWritable { + defaultProtocol = "http:"; + body: null | ReadableStream = null; + controller: ReadableStreamDefaultController | null = null; + constructor( + public opts: RequestOptions, + public cb?: (res: IncomingMessageForClient) => void, + ) { + super(); + } + + // deno-lint-ignore no-explicit-any + override _write(chunk: any, _enc: string, cb: () => void) { + if (this.controller) { + this.controller.enqueue(chunk); + cb(); + return; + } + + this.body = new ReadableStream({ + start: (controller) => { + this.controller = controller; + controller.enqueue(chunk); + cb(); + }, + }); + } + + override async _final() { + if (this.controller) { + this.controller.close(); + } + + const body = await this._createBody(this.body, this.opts); + const client = await this._createCustomClient(); + const opts = { + body, + method: this.opts.method, + client, + headers: this.opts.headers, + }; + const mayResponse = fetch(this._createUrlStrFromOptions(this.opts), opts) + .catch((e) => { + if (e.message.includes("connection closed before message completed")) { + // Node.js seems ignoring this error + } else { + this.emit("error", e); + } + return undefined; + }); + const res = new IncomingMessageForClient( + await mayResponse, + this._createSocket(), + ); + this.emit("response", res); + if (client) { + res.on("end", () => { + client.close(); + }); + } + this.cb?.(res); + } + + abort() { + this.destroy(); + } + + async _createBody( + body: ReadableStream | null, + opts: RequestOptions, + ): Promise { + if (!body) return null; + if (!opts.headers) return body; + + const headers = Object.fromEntries( + Object.entries(opts.headers).map(([k, v]) => [k.toLowerCase(), v]), + ); + + if ( + !RE_TE_CHUNKED.test(headers["transfer-encoding"]) && + !Number.isNaN(Number.parseInt(headers["content-length"], 10)) + ) { + const bufferList: Buffer[] = []; + for await (const chunk of body) { + bufferList.push(chunk); + } + return Buffer.concat(bufferList); + } + + return body; + } + + _createCustomClient(): Promise { + return Promise.resolve(undefined); + } + + _createSocket(): Socket { + // Note: Creates a dummy socket for the compatibility + // Sometimes the libraries check some properties of socket + // e.g. if (!response.socket.authorized) { ... } + return new Socket({}); + } + + _createUrlStrFromOptions(opts: RequestOptions): string { + if (opts.href) { + return opts.href; + } + const protocol = opts.protocol ?? this.defaultProtocol; + const auth = opts.auth; + const host = opts.host ?? opts.hostname ?? "localhost"; + const defaultPort = opts.agent?.defaultPort; + const port = opts.port ?? defaultPort ?? 80; + let path = opts.path ?? "/"; + if (!path.startsWith("/")) { + path = "/" + path; + } + return `${protocol}//${auth ? `${auth}@` : ""}${host}${ + port === 80 ? "" : `:${port}` + }${path}`; + } + + setTimeout() { + console.log("not implemented: ClientRequest.setTimeout"); + } +} + +/** IncomingMessage for http(s) client */ +export class IncomingMessageForClient extends NodeReadable { + reader: ReadableStreamDefaultReader | undefined; + #statusMessage = ""; + constructor(public response: Response | undefined, public socket: Socket) { + super(); + this.reader = response?.body?.getReader(); + } + + override async _read(_size: number) { + if (this.reader === undefined) { + this.push(null); + return; + } + try { + const res = await this.reader.read(); + if (res.done) { + this.push(null); + return; + } + this.push(res.value); + } catch (e) { + // deno-lint-ignore no-explicit-any + this.destroy(e as any); + } + } + + get headers() { + if (this.response) { + return Object.fromEntries(this.response.headers.entries()); + } + return {}; + } + + get trailers() { + return {}; + } + + get statusCode() { + return this.response?.status || 0; + } + + get statusMessage() { + return this.#statusMessage || this.response?.statusText || ""; + } + + set statusMessage(v: string) { + this.#statusMessage = v; + } +} + +export class ServerResponse extends NodeWritable { + statusCode?: number = undefined; + statusMessage?: string = undefined; + #headers = new Headers({}); + #readable: ReadableStream; + override writable = true; + // used by `npm:on-finished` + finished = false; + headersSent = false; + #firstChunk: Chunk | null = null; + // Used if --unstable flag IS NOT present + #reqEvent?: Deno.RequestEvent; + // Used if --unstable flag IS present + #resolve?: (value: Response | PromiseLike) => void; + #isFlashRequest: boolean; + + static #enqueue(controller: ReadableStreamDefaultController, chunk: Chunk) { + // TODO(kt3k): This is a workaround for denoland/deno#17194 + // This if-block should be removed when the above issue is resolved. + if (chunk.length === 0) { + return; + } + if (typeof chunk === "string") { + controller.enqueue(ENCODER.encode(chunk)); + } else { + controller.enqueue(chunk); + } + } + + /** Returns true if the response body should be null with the given + * http status code */ + static #bodyShouldBeNull(status: number) { + return status === 101 || status === 204 || status === 205 || status === 304; + } + + constructor( + reqEvent: undefined | Deno.RequestEvent, + resolve: undefined | ((value: Response | PromiseLike) => void), + ) { + let controller: ReadableByteStreamController; + const readable = new ReadableStream({ + start(c) { + controller = c as ReadableByteStreamController; + }, + }); + super({ + autoDestroy: true, + defaultEncoding: "utf-8", + emitClose: true, + write: (chunk, _encoding, cb) => { + if (!this.headersSent) { + if (this.#firstChunk === null) { + this.#firstChunk = chunk; + return cb(); + } else { + ServerResponse.#enqueue(controller, this.#firstChunk); + this.#firstChunk = null; + this.respond(false); + } + } + ServerResponse.#enqueue(controller, chunk); + return cb(); + }, + final: (cb) => { + if (this.#firstChunk) { + this.respond(true, this.#firstChunk); + } else if (!this.headersSent) { + this.respond(true); + } + controller.close(); + return cb(); + }, + destroy: (err, cb) => { + if (err) { + controller.error(err); + } + return cb(null); + }, + }); + this.#readable = readable; + this.#resolve = resolve; + this.#reqEvent = reqEvent; + this.#isFlashRequest = typeof resolve !== "undefined"; + } + + setHeader(name: string, value: string) { + this.#headers.set(name, value); + return this; + } + + getHeader(name: string) { + return this.#headers.get(name); + } + removeHeader(name: string) { + return this.#headers.delete(name); + } + getHeaderNames() { + return Array.from(this.#headers.keys()); + } + hasHeader(name: string) { + return this.#headers.has(name); + } + + writeHead(status: number, headers: Record) { + this.statusCode = status; + for (const k in headers) { + if (Object.hasOwn(headers, k)) { + this.#headers.set(k, headers[k]); + } + } + return this; + } + + #ensureHeaders(singleChunk?: Chunk) { + if (this.statusCode === undefined) { + this.statusCode = 200; + this.statusMessage = "OK"; + } + // Only taken if --unstable IS NOT present + if ( + !this.#isFlashRequest && typeof singleChunk === "string" && + !this.hasHeader("content-type") + ) { + this.setHeader("content-type", "text/plain;charset=UTF-8"); + } + } + + respond(final: boolean, singleChunk?: Chunk) { + this.headersSent = true; + this.#ensureHeaders(singleChunk); + let body = singleChunk ?? (final ? null : this.#readable); + if (ServerResponse.#bodyShouldBeNull(this.statusCode!)) { + body = null; + } + if (this.#isFlashRequest) { + this.#resolve!( + new Response(body, { + headers: this.#headers, + status: this.statusCode, + statusText: this.statusMessage, + }), + ); + } else { + this.#reqEvent!.respondWith( + new Response(body, { + headers: this.#headers, + status: this.statusCode, + statusText: this.statusMessage, + }), + ).catch(() => { + // ignore this error + }); + } + } + + // deno-lint-ignore no-explicit-any + override end(chunk?: any, encoding?: any, cb?: any): this { + this.finished = true; + if (this.#isFlashRequest) { + // Flash sets both of these headers. + this.#headers.delete("transfer-encoding"); + this.#headers.delete("content-length"); + } else if (!chunk && this.#headers.has("transfer-encoding")) { + // FIXME(bnoordhuis) Node sends a zero length chunked body instead, i.e., + // the trailing "0\r\n", but respondWith() just hangs when I try that. + this.#headers.set("content-length", "0"); + this.#headers.delete("transfer-encoding"); + } + + // @ts-expect-error The signature for cb is stricter than the one implemented here + return super.end(chunk, encoding, cb); + } +} + +// TODO(@AaronO): optimize +export class IncomingMessageForServer extends NodeReadable { + #req: Request; + url: string; + method: string; + + constructor(req: Request) { + // Check if no body (GET/HEAD/OPTIONS/...) + const reader = req.body?.getReader(); + super({ + autoDestroy: true, + emitClose: true, + objectMode: false, + read: async function (_size) { + if (!reader) { + return this.push(null); + } + + try { + const { value } = await reader!.read(); + this.push(value !== undefined ? Buffer.from(value) : null); + } catch (err) { + this.destroy(err as Error); + } + }, + destroy: (err, cb) => { + reader?.cancel().finally(() => cb(err)); + }, + }); + // TODO(@bartlomieju): consider more robust path extraction, e.g: + // url: (new URL(request.url).pathname), + this.url = req.url?.slice(req.url.indexOf("/", 8)); + this.method = req.method; + this.#req = req; + } + + get aborted() { + return false; + } + + get httpVersion() { + return "1.1"; + } + + get headers() { + return Object.fromEntries(this.#req.headers.entries()); + } + + get upgrade(): boolean { + return Boolean( + this.#req.headers.get("connection")?.toLowerCase().includes("upgrade") && + this.#req.headers.get("upgrade"), + ); + } +} + +type ServerHandler = ( + req: IncomingMessageForServer, + res: ServerResponse, +) => void; + +export function Server(handler?: ServerHandler): ServerImpl { + return new ServerImpl(handler); +} + +class ServerImpl extends EventEmitter { + #isFlashServer: boolean; + + #httpConnections: Set = new Set(); + #listener?: Deno.Listener; + + #addr?: Deno.NetAddr; + #hasClosed = false; + #ac?: AbortController; + #servePromise?: Deferred; + listening = false; + + constructor(handler?: ServerHandler) { + super(); + // @ts-ignore Might be undefined without `--unstable` flag + this.#isFlashServer = typeof DenoServe == "function"; + if (this.#isFlashServer) { + this.#servePromise = deferred(); + this.#servePromise.then(() => this.emit("close")); + } + if (handler !== undefined) { + this.on("request", handler); + } + } + + listen(...args: unknown[]): this { + // TODO(bnoordhuis) Delegate to net.Server#listen(). + const normalized = _normalizeArgs(args); + const options = normalized[0] as Partial; + const cb = normalized[1]; + + if (cb !== null) { + // @ts-ignore change EventEmitter's sig to use CallableFunction + this.once("listening", cb); + } + + let port = 0; + if (typeof options.port === "number" || typeof options.port === "string") { + validatePort(options.port, "options.port"); + port = options.port | 0; + } + + // TODO(bnoordhuis) Node prefers [::] when host is omitted, + // we on the other hand default to 0.0.0.0. + if (this.#isFlashServer) { + const hostname = options.host ?? "0.0.0.0"; + this.#addr = { + hostname, + port, + } as Deno.NetAddr; + this.listening = true; + nextTick(() => this.#serve()); + } else { + this.listening = true; + const hostname = options.host ?? ""; + this.#listener = Deno.listen({ port, hostname }); + nextTick(() => this.#listenLoop()); + } + + return this; + } + + async #listenLoop() { + const go = async (httpConn: Deno.HttpConn) => { + try { + for (;;) { + let reqEvent = null; + try { + // Note: httpConn.nextRequest() calls httpConn.close() on error. + reqEvent = await httpConn.nextRequest(); + } catch { + // Connection closed. + // TODO(bnoordhuis) Emit "clientError" event on the http.Server + // instance? Node emits it when request parsing fails and expects + // the listener to send a raw 4xx HTTP response on the underlying + // net.Socket but we don't have one to pass to the listener. + } + if (reqEvent === null) { + break; + } + const req = new IncomingMessageForServer(reqEvent.request); + const res = new ServerResponse(reqEvent, undefined); + this.emit("request", req, res); + } + } finally { + this.#httpConnections.delete(httpConn); + } + }; + + const listener = this.#listener; + + if (listener !== undefined) { + this.emit("listening"); + + for await (const conn of listener) { + let httpConn: Deno.HttpConn; + try { + httpConn = Deno.serveHttp(conn); + } catch { + continue; /// Connection closed. + } + + this.#httpConnections.add(httpConn); + go(httpConn); + } + } + } + + #serve() { + const ac = new AbortController(); + const handler = (request: Request) => { + const req = new IncomingMessageForServer(request); + if (req.upgrade && this.listenerCount("upgrade") > 0) { + const [conn, head] = DenoUpgradeHttpRaw(request) as [ + Deno.Conn, + Uint8Array, + ]; + const socket = new Socket({ + handle: new TCP(constants.SERVER, conn), + }); + this.emit("upgrade", req, socket, Buffer.from(head)); + } else { + return new Promise((resolve): void => { + const res = new ServerResponse(undefined, resolve); + this.emit("request", req, res); + }); + } + }; + + if (this.#hasClosed) { + return; + } + this.#ac = ac; + DenoServe( + { + handler: handler as Deno.ServeHandler, + ...this.#addr, + signal: ac.signal, + // @ts-ignore Might be any without `--unstable` flag + onListen: ({ port }) => { + this.#addr!.port = port; + this.emit("listening"); + }, + }, + ).then(() => this.#servePromise!.resolve()); + } + + setTimeout() { + console.error("Not implemented: Server.setTimeout()"); + } + + close(cb?: (err?: Error) => void): this { + const listening = this.listening; + this.listening = false; + + this.#hasClosed = true; + if (typeof cb === "function") { + if (listening) { + this.once("close", cb); + } else { + this.once("close", function close() { + cb(new ERR_SERVER_NOT_RUNNING()); + }); + } + } + + if (this.#isFlashServer) { + if (listening && this.#ac) { + this.#ac.abort(); + this.#ac = undefined; + } else { + this.#servePromise!.resolve(); + } + } else { + nextTick(() => this.emit("close")); + + if (listening) { + this.#listener!.close(); + this.#listener = undefined; + + for (const httpConn of this.#httpConnections) { + try { + httpConn.close(); + } catch { + // Already closed. + } + } + + this.#httpConnections.clear(); + } + } + + return this; + } + + address() { + let addr; + if (this.#isFlashServer) { + addr = this.#addr!; + } else { + addr = this.#listener!.addr as Deno.NetAddr; + } + return { + port: addr.port, + address: addr.hostname, + }; + } +} + +Server.prototype = ServerImpl.prototype; + +export function createServer(handler?: ServerHandler) { + return Server(handler); +} + +/** Makes an HTTP request. */ +export function request( + url: string | URL, + cb?: (res: IncomingMessageForClient) => void, +): ClientRequest; +export function request( + opts: RequestOptions, + cb?: (res: IncomingMessageForClient) => void, +): ClientRequest; +export function request( + url: string | URL, + opts: RequestOptions, + cb?: (res: IncomingMessageForClient) => void, +): ClientRequest; +// deno-lint-ignore no-explicit-any +export function request(...args: any[]) { + let options = {}; + if (typeof args[0] === "string") { + options = urlToHttpOptions(new URL(args.shift())); + } else if (args[0] instanceof URL) { + options = urlToHttpOptions(args.shift()); + } + if (args[0] && typeof args[0] !== "function") { + Object.assign(options, args.shift()); + } + args.unshift(options); + return new ClientRequest(args[0], args[1]); +} + +/** Makes a `GET` HTTP request. */ +export function get( + url: string | URL, + cb?: (res: IncomingMessageForClient) => void, +): ClientRequest; +export function get( + opts: RequestOptions, + cb?: (res: IncomingMessageForClient) => void, +): ClientRequest; +export function get( + url: string | URL, + opts: RequestOptions, + cb?: (res: IncomingMessageForClient) => void, +): ClientRequest; +// deno-lint-ignore no-explicit-any +export function get(...args: any[]) { + const req = request(args[0], args[1], args[2]); + req.end(); + return req; +} + +export { + Agent, + ClientRequest, + IncomingMessageForServer as IncomingMessage, + METHODS, + OutgoingMessage, + STATUS_CODES, +}; +export default { + Agent, + ClientRequest, + STATUS_CODES, + METHODS, + createServer, + Server, + IncomingMessage: IncomingMessageForServer, + IncomingMessageForClient, + IncomingMessageForServer, + OutgoingMessage, + ServerResponse, + request, + get, +}; diff --git a/ext/node/polyfills/http2.ts b/ext/node/polyfills/http2.ts new file mode 100644 index 0000000000..e5eb1725a1 --- /dev/null +++ b/ext/node/polyfills/http2.ts @@ -0,0 +1,83 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export class Http2Session { + constructor() { + notImplemented("Http2Session.prototype.constructor"); + } +} +export class ServerHttp2Session { + constructor() { + notImplemented("ServerHttp2Session"); + } +} +export class ClientHttp2Session { + constructor() { + notImplemented("ClientHttp2Session"); + } +} +export class Http2Stream { + constructor() { + notImplemented("Http2Stream"); + } +} +export class ClientHttp2Stream { + constructor() { + notImplemented("ClientHttp2Stream"); + } +} +export class ServerHttp2Stream { + constructor() { + notImplemented("ServerHttp2Stream"); + } +} +export class Http2Server { + constructor() { + notImplemented("Http2Server"); + } +} +export class Http2SecureServer { + constructor() { + notImplemented("Http2SecureServer"); + } +} +export function createServer() {} +export function createSecureServer() {} +export function connect() {} +export const constants = {}; +export function getDefaultSettings() {} +export function getPackedSettings() {} +export function getUnpackedSettings() {} +export const sensitiveHeaders = Symbol("nodejs.http2.sensitiveHeaders"); +export class Http2ServerRequest { + constructor() { + notImplemented("Http2ServerRequest"); + } +} +export class Http2ServerResponse { + constructor() { + notImplemented("Http2ServerResponse"); + } +} +export default { + Http2Session, + ServerHttp2Session, + ClientHttp2Session, + Http2Stream, + ClientHttp2Stream, + ServerHttp2Stream, + Http2Server, + Http2SecureServer, + createServer, + createSecureServer, + connect, + constants, + getDefaultSettings, + getPackedSettings, + getUnpackedSettings, + sensitiveHeaders, + Http2ServerRequest, + Http2ServerResponse, +}; diff --git a/ext/node/polyfills/https.ts b/ext/node/polyfills/https.ts new file mode 100644 index 0000000000..da77b46e24 --- /dev/null +++ b/ext/node/polyfills/https.ts @@ -0,0 +1,128 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { urlToHttpOptions } from "internal:deno_node/polyfills/internal/url.ts"; +import { + Agent as HttpAgent, + ClientRequest, + IncomingMessageForClient as IncomingMessage, + type RequestOptions, +} from "internal:deno_node/polyfills/http.ts"; +import type { Socket } from "internal:deno_node/polyfills/net.ts"; + +export class Agent extends HttpAgent { +} + +export class Server { + constructor() { + notImplemented("https.Server.prototype.constructor"); + } +} +export function createServer() { + notImplemented("https.createServer"); +} + +interface HttpsRequestOptions extends RequestOptions { + _: unknown; +} + +// Store additional root CAs. +// undefined means NODE_EXTRA_CA_CERTS is not checked yet. +// null means there's no additional root CAs. +let caCerts: string[] | undefined | null; + +/** Makes a request to an https server. */ +export function get( + url: string | URL, + cb?: (res: IncomingMessage) => void, +): HttpsClientRequest; +export function get( + opts: HttpsRequestOptions, + cb?: (res: IncomingMessage) => void, +): HttpsClientRequest; +export function get( + url: string | URL, + opts: HttpsRequestOptions, + cb?: (res: IncomingMessage) => void, +): HttpsClientRequest; +// deno-lint-ignore no-explicit-any +export function get(...args: any[]) { + const req = request(args[0], args[1], args[2]); + req.end(); + return req; +} + +export const globalAgent = undefined; +/** HttpsClientRequest class loosely follows http.ClientRequest class API. */ +class HttpsClientRequest extends ClientRequest { + override defaultProtocol = "https:"; + override async _createCustomClient(): Promise< + Deno.HttpClient | undefined + > { + if (caCerts === null) { + return undefined; + } + if (caCerts !== undefined) { + return Deno.createHttpClient({ caCerts }); + } + const status = await Deno.permissions.query({ + name: "env", + variable: "NODE_EXTRA_CA_CERTS", + }); + if (status.state !== "granted") { + caCerts = null; + return undefined; + } + const certFilename = Deno.env.get("NODE_EXTRA_CA_CERTS"); + if (!certFilename) { + caCerts = null; + return undefined; + } + const caCert = await Deno.readTextFile(certFilename); + caCerts = [caCert]; + return Deno.createHttpClient({ caCerts }); + } + + override _createSocket(): Socket { + // deno-lint-ignore no-explicit-any + return { authorized: true } as any; + } +} + +/** Makes a request to an https server. */ +export function request( + url: string | URL, + cb?: (res: IncomingMessage) => void, +): HttpsClientRequest; +export function request( + opts: HttpsRequestOptions, + cb?: (res: IncomingMessage) => void, +): HttpsClientRequest; +export function request( + url: string | URL, + opts: HttpsRequestOptions, + cb?: (res: IncomingMessage) => void, +): HttpsClientRequest; +// deno-lint-ignore no-explicit-any +export function request(...args: any[]) { + let options = {}; + if (typeof args[0] === "string") { + options = urlToHttpOptions(new URL(args.shift())); + } else if (args[0] instanceof URL) { + options = urlToHttpOptions(args.shift()); + } + if (args[0] && typeof args[0] !== "function") { + Object.assign(options, args.shift()); + } + args.unshift(options); + return new HttpsClientRequest(args[0], args[1]); +} +export default { + Agent, + Server, + createServer, + get, + globalAgent, + request, +}; diff --git a/ext/node/polyfills/inspector.ts b/ext/node/polyfills/inspector.ts new file mode 100644 index 0000000000..e946d7605e --- /dev/null +++ b/ext/node/polyfills/inspector.ts @@ -0,0 +1,91 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +const connectionSymbol = Symbol("connectionProperty"); +const messageCallbacksSymbol = Symbol("messageCallbacks"); +const nextIdSymbol = Symbol("nextId"); +const onMessageSymbol = Symbol("onMessage"); + +class Session extends EventEmitter { + [connectionSymbol]: null; + [nextIdSymbol]: number; + [messageCallbacksSymbol]: Map void>; + + constructor() { + super(); + notImplemented("inspector.Session.prototype.constructor"); + } + + /** Connects the session to the inspector back-end. */ + connect() { + notImplemented("inspector.Session.prototype.connect"); + } + + /** Connects the session to the main thread + * inspector back-end. */ + connectToMainThread() { + notImplemented("inspector.Session.prototype.connectToMainThread"); + } + + [onMessageSymbol](_message: string) { + notImplemented("inspector.Session.prototype[Symbol('onMessage')]"); + } + + /** Posts a message to the inspector back-end. */ + post( + _method: string, + _params?: Record, + _callback?: (...args: unknown[]) => void, + ) { + notImplemented("inspector.Session.prototype.post"); + } + + /** Immediately closes the session, all pending + * message callbacks will be called with an + * error. + */ + disconnect() { + notImplemented("inspector.Session.prototype.disconnect"); + } +} + +/** Activates inspector on host and port. + * See https://nodejs.org/api/inspector.html#inspectoropenport-host-wait */ +function open(_port?: number, _host?: string, _wait?: boolean) { + notImplemented("inspector.Session.prototype.open"); +} + +/** Deactivate the inspector. Blocks until there are no active connections. + * See https://nodejs.org/api/inspector.html#inspectorclose */ +function close() { + notImplemented("inspector.Session.prototype.close"); +} + +/** Return the URL of the active inspector, or undefined if there is none. + * See https://nodejs.org/api/inspector.html#inspectorurl */ +function url() { + // TODO(kt3k): returns undefined for now, which means the inspector is not activated. + return undefined; +} + +/** Blocks until a client (existing or connected later) has sent Runtime.runIfWaitingForDebugger command. + * See https://nodejs.org/api/inspector.html#inspectorwaitfordebugger */ +function waitForDebugger() { + notImplemented("inspector.wairForDebugger"); +} + +const console = globalThis.console; + +export { close, console, open, Session, url, waitForDebugger }; + +export default { + close, + console, + open, + Session, + url, + waitForDebugger, +}; diff --git a/ext/node/polyfills/internal/assert.mjs b/ext/node/polyfills/internal/assert.mjs new file mode 100644 index 0000000000..fcdb32a056 --- /dev/null +++ b/ext/node/polyfills/internal/assert.mjs @@ -0,0 +1,16 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { ERR_INTERNAL_ASSERTION } from "internal:deno_node/polyfills/internal/errors.ts"; + +function assert(value, message) { + if (!value) { + throw new ERR_INTERNAL_ASSERTION(message); + } +} + +function fail(message) { + throw new ERR_INTERNAL_ASSERTION(message); +} + +assert.fail = fail; + +export default assert; diff --git a/ext/node/polyfills/internal/async_hooks.ts b/ext/node/polyfills/internal/async_hooks.ts new file mode 100644 index 0000000000..8bf513e468 --- /dev/null +++ b/ext/node/polyfills/internal/async_hooks.ts @@ -0,0 +1,420 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +// deno-lint-ignore camelcase +import * as async_wrap from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; +import { ERR_ASYNC_CALLBACK } from "internal:deno_node/polyfills/internal/errors.ts"; +export { + asyncIdSymbol, + ownerSymbol, +} from "internal:deno_node/polyfills/internal_binding/symbols.ts"; + +interface ActiveHooks { + array: AsyncHook[]; + // deno-lint-ignore camelcase + call_depth: number; + // deno-lint-ignore camelcase + tmp_array: AsyncHook[] | null; + // deno-lint-ignore camelcase + tmp_fields: number[] | null; +} + +// Properties in active_hooks are used to keep track of the set of hooks being +// executed in case another hook is enabled/disabled. The new set of hooks is +// then restored once the active set of hooks is finished executing. +// deno-lint-ignore camelcase +const active_hooks: ActiveHooks = { + // Array of all AsyncHooks that will be iterated whenever an async event + // fires. Using var instead of (preferably const) in order to assign + // active_hooks.tmp_array if a hook is enabled/disabled during hook + // execution. + array: [], + // Use a counter to track nested calls of async hook callbacks and make sure + // the active_hooks.array isn't altered mid execution. + // deno-lint-ignore camelcase + call_depth: 0, + // Use to temporarily store and updated active_hooks.array if the user + // enables or disables a hook while hooks are being processed. If a hook is + // enabled() or disabled() during hook execution then the current set of + // active hooks is duplicated and set equal to active_hooks.tmp_array. Any + // subsequent changes are on the duplicated array. When all hooks have + // completed executing active_hooks.tmp_array is assigned to + // active_hooks.array. + // deno-lint-ignore camelcase + tmp_array: null, + // Keep track of the field counts held in active_hooks.tmp_array. Because the + // async_hook_fields can't be reassigned, store each uint32 in an array that + // is written back to async_hook_fields when active_hooks.array is restored. + // deno-lint-ignore camelcase + tmp_fields: null, +}; + +export const registerDestroyHook = async_wrap.registerDestroyHook; +const { + // deno-lint-ignore camelcase + async_hook_fields, + // deno-lint-ignore camelcase + asyncIdFields: async_id_fields, + newAsyncId, + constants, +} = async_wrap; +export { newAsyncId }; +const { + kInit, + kBefore, + kAfter, + kDestroy, + kPromiseResolve, + kTotals, + kCheck, + kDefaultTriggerAsyncId, + kStackLength, +} = constants; + +// deno-lint-ignore camelcase +const resource_symbol = Symbol("resource"); +// deno-lint-ignore camelcase +export const async_id_symbol = Symbol("trigger_async_id"); +// deno-lint-ignore camelcase +export const trigger_async_id_symbol = Symbol("trigger_async_id"); +// deno-lint-ignore camelcase +export const init_symbol = Symbol("init"); +// deno-lint-ignore camelcase +export const before_symbol = Symbol("before"); +// deno-lint-ignore camelcase +export const after_symbol = Symbol("after"); +// deno-lint-ignore camelcase +export const destroy_symbol = Symbol("destroy"); +// deno-lint-ignore camelcase +export const promise_resolve_symbol = Symbol("promiseResolve"); + +export const symbols = { + // deno-lint-ignore camelcase + async_id_symbol, + // deno-lint-ignore camelcase + trigger_async_id_symbol, + // deno-lint-ignore camelcase + init_symbol, + // deno-lint-ignore camelcase + before_symbol, + // deno-lint-ignore camelcase + after_symbol, + // deno-lint-ignore camelcase + destroy_symbol, + // deno-lint-ignore camelcase + promise_resolve_symbol, +}; + +// deno-lint-ignore no-explicit-any +function lookupPublicResource(resource: any) { + if (typeof resource !== "object" || resource === null) return resource; + // TODO(addaleax): Merge this with owner_symbol and use it across all + // AsyncWrap instances. + const publicResource = resource[resource_symbol]; + if (publicResource !== undefined) { + return publicResource; + } + return resource; +} + +// Used by C++ to call all init() callbacks. Because some state can be setup +// from C++ there's no need to perform all the same operations as in +// emitInitScript. +function emitInitNative( + asyncId: number, + // deno-lint-ignore no-explicit-any + type: any, + triggerAsyncId: number, + // deno-lint-ignore no-explicit-any + resource: any, +) { + active_hooks.call_depth += 1; + resource = lookupPublicResource(resource); + // Use a single try/catch for all hooks to avoid setting up one per iteration. + try { + for (let i = 0; i < active_hooks.array.length; i++) { + if (typeof active_hooks.array[i][init_symbol] === "function") { + active_hooks.array[i][init_symbol]( + asyncId, + type, + triggerAsyncId, + resource, + ); + } + } + } catch (e) { + throw e; + } finally { + active_hooks.call_depth -= 1; + } + + // Hooks can only be restored if there have been no recursive hook calls. + // Also the active hooks do not need to be restored if enable()/disable() + // weren't called during hook execution, in which case active_hooks.tmp_array + // will be null. + if (active_hooks.call_depth === 0 && active_hooks.tmp_array !== null) { + restoreActiveHooks(); + } +} + +function getHookArrays(): [AsyncHook[], number[] | Uint32Array] { + if (active_hooks.call_depth === 0) { + return [active_hooks.array, async_hook_fields]; + } + // If this hook is being enabled while in the middle of processing the array + // of currently active hooks then duplicate the current set of active hooks + // and store this there. This shouldn't fire until the next time hooks are + // processed. + if (active_hooks.tmp_array === null) { + storeActiveHooks(); + } + return [active_hooks.tmp_array!, active_hooks.tmp_fields!]; +} + +function storeActiveHooks() { + active_hooks.tmp_array = active_hooks.array.slice(); + // Don't want to make the assumption that kInit to kDestroy are indexes 0 to + // 4. So do this the long way. + active_hooks.tmp_fields = []; + copyHooks(active_hooks.tmp_fields, async_hook_fields); +} + +function copyHooks( + destination: number[] | Uint32Array, + source: number[] | Uint32Array, +) { + destination[kInit] = source[kInit]; + destination[kBefore] = source[kBefore]; + destination[kAfter] = source[kAfter]; + destination[kDestroy] = source[kDestroy]; + destination[kPromiseResolve] = source[kPromiseResolve]; +} + +// Then restore the correct hooks array in case any hooks were added/removed +// during hook callback execution. +function restoreActiveHooks() { + active_hooks.array = active_hooks.tmp_array!; + copyHooks(async_hook_fields, active_hooks.tmp_fields!); + + active_hooks.tmp_array = null; + active_hooks.tmp_fields = null; +} + +// deno-lint-ignore no-unused-vars +let wantPromiseHook = false; +function enableHooks() { + async_hook_fields[kCheck] += 1; + + // TODO(kt3k): Uncomment this + // setCallbackTrampoline(callbackTrampoline); +} + +function disableHooks() { + async_hook_fields[kCheck] -= 1; + + wantPromiseHook = false; + + // TODO(kt3k): Uncomment the below + // setCallbackTrampoline(); + + // Delay the call to `disablePromiseHook()` because we might currently be + // between the `before` and `after` calls of a Promise. + // TODO(kt3k): Uncomment the below + // enqueueMicrotask(disablePromiseHookIfNecessary); +} + +// Return the triggerAsyncId meant for the constructor calling it. It's up to +// the user to safeguard this call and make sure it's zero'd out when the +// constructor is complete. +export function getDefaultTriggerAsyncId() { + const defaultTriggerAsyncId = + async_id_fields[async_wrap.UidFields.kDefaultTriggerAsyncId]; + // If defaultTriggerAsyncId isn't set, use the executionAsyncId + if (defaultTriggerAsyncId < 0) { + return async_id_fields[async_wrap.UidFields.kExecutionAsyncId]; + } + return defaultTriggerAsyncId; +} + +export function defaultTriggerAsyncIdScope( + triggerAsyncId: number | undefined, + // deno-lint-ignore no-explicit-any + block: (...arg: any[]) => void, + ...args: unknown[] +) { + if (triggerAsyncId === undefined) { + return block.apply(null, args); + } + // CHECK(NumberIsSafeInteger(triggerAsyncId)) + // CHECK(triggerAsyncId > 0) + const oldDefaultTriggerAsyncId = async_id_fields[kDefaultTriggerAsyncId]; + async_id_fields[kDefaultTriggerAsyncId] = triggerAsyncId; + + try { + return block.apply(null, args); + } finally { + async_id_fields[kDefaultTriggerAsyncId] = oldDefaultTriggerAsyncId; + } +} + +function hasHooks(key: number) { + return async_hook_fields[key] > 0; +} + +export function enabledHooksExist() { + return hasHooks(kCheck); +} + +export function initHooksExist() { + return hasHooks(kInit); +} + +export function afterHooksExist() { + return hasHooks(kAfter); +} + +export function destroyHooksExist() { + return hasHooks(kDestroy); +} + +export function promiseResolveHooksExist() { + return hasHooks(kPromiseResolve); +} + +function emitInitScript( + asyncId: number, + // deno-lint-ignore no-explicit-any + type: any, + triggerAsyncId: number, + // deno-lint-ignore no-explicit-any + resource: any, +) { + // Short circuit all checks for the common case. Which is that no hooks have + // been set. Do this to remove performance impact for embedders (and core). + if (!hasHooks(kInit)) { + return; + } + + if (triggerAsyncId === null) { + triggerAsyncId = getDefaultTriggerAsyncId(); + } + + emitInitNative(asyncId, type, triggerAsyncId, resource); +} +export { emitInitScript as emitInit }; + +export function hasAsyncIdStack() { + return hasHooks(kStackLength); +} + +export { constants }; + +type Fn = (...args: unknown[]) => unknown; + +export class AsyncHook { + [init_symbol]: Fn; + [before_symbol]: Fn; + [after_symbol]: Fn; + [destroy_symbol]: Fn; + [promise_resolve_symbol]: Fn; + + constructor({ + init, + before, + after, + destroy, + promiseResolve, + }: { + init: Fn; + before: Fn; + after: Fn; + destroy: Fn; + promiseResolve: Fn; + }) { + if (init !== undefined && typeof init !== "function") { + throw new ERR_ASYNC_CALLBACK("hook.init"); + } + if (before !== undefined && typeof before !== "function") { + throw new ERR_ASYNC_CALLBACK("hook.before"); + } + if (after !== undefined && typeof after !== "function") { + throw new ERR_ASYNC_CALLBACK("hook.after"); + } + if (destroy !== undefined && typeof destroy !== "function") { + throw new ERR_ASYNC_CALLBACK("hook.destroy"); + } + if (promiseResolve !== undefined && typeof promiseResolve !== "function") { + throw new ERR_ASYNC_CALLBACK("hook.promiseResolve"); + } + + this[init_symbol] = init; + this[before_symbol] = before; + this[after_symbol] = after; + this[destroy_symbol] = destroy; + this[promise_resolve_symbol] = promiseResolve; + } + + enable() { + // The set of callbacks for a hook should be the same regardless of whether + // enable()/disable() are run during their execution. The following + // references are reassigned to the tmp arrays if a hook is currently being + // processed. + // deno-lint-ignore camelcase + const { 0: hooks_array, 1: hook_fields } = getHookArrays(); + + // Each hook is only allowed to be added once. + if (hooks_array.includes(this)) { + return this; + } + + // deno-lint-ignore camelcase + const prev_kTotals = hook_fields[kTotals]; + + // createHook() has already enforced that the callbacks are all functions, + // so here simply increment the count of whether each callbacks exists or + // not. + hook_fields[kTotals] = hook_fields[kInit] += +!!this[init_symbol]; + hook_fields[kTotals] += hook_fields[kBefore] += +!!this[before_symbol]; + hook_fields[kTotals] += hook_fields[kAfter] += +!!this[after_symbol]; + hook_fields[kTotals] += hook_fields[kDestroy] += +!!this[destroy_symbol]; + hook_fields[kTotals] += hook_fields[kPromiseResolve] += + +!!this[promise_resolve_symbol]; + hooks_array.push(this); + + if (prev_kTotals === 0 && hook_fields[kTotals] > 0) { + enableHooks(); + } + + // TODO(kt3k): Uncomment the below + // updatePromiseHookMode(); + + return this; + } + + disable() { + // deno-lint-ignore camelcase + const { 0: hooks_array, 1: hook_fields } = getHookArrays(); + + const index = hooks_array.indexOf(this); + if (index === -1) { + return this; + } + + // deno-lint-ignore camelcase + const prev_kTotals = hook_fields[kTotals]; + + hook_fields[kTotals] = hook_fields[kInit] -= +!!this[init_symbol]; + hook_fields[kTotals] += hook_fields[kBefore] -= +!!this[before_symbol]; + hook_fields[kTotals] += hook_fields[kAfter] -= +!!this[after_symbol]; + hook_fields[kTotals] += hook_fields[kDestroy] -= +!!this[destroy_symbol]; + hook_fields[kTotals] += hook_fields[kPromiseResolve] -= + +!!this[promise_resolve_symbol]; + hooks_array.splice(index, 1); + + if (prev_kTotals > 0 && hook_fields[kTotals] === 0) { + disableHooks(); + } + + return this; + } +} diff --git a/ext/node/polyfills/internal/blob.mjs b/ext/node/polyfills/internal/blob.mjs new file mode 100644 index 0000000000..1b685fad40 --- /dev/null +++ b/ext/node/polyfills/internal/blob.mjs @@ -0,0 +1,7 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Node's implementation checks for a symbol they put in the blob prototype +// Since the implementation of Blob is Deno's, the only option is to check the +// objects constructor +export function isBlob(object) { + return object instanceof Blob; +} diff --git a/ext/node/polyfills/internal/buffer.d.ts b/ext/node/polyfills/internal/buffer.d.ts new file mode 100644 index 0000000000..638674467a --- /dev/null +++ b/ext/node/polyfills/internal/buffer.d.ts @@ -0,0 +1,2074 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright DefinitelyTyped contributors. All rights reserved. MIT license. + +/** + * One of many allowed encodings for the buffer content + * - ascii + * - base64 + * - base64url + * - binary + * - hex + * - latin1 + * - ucs-2 + * - ucs2 + * - utf-8 + * - utf16le + * - utf8 + */ +type Encoding = unknown; + +type WithImplicitCoercion = + | T + | { + valueOf(): T; + }; + +/** + * `Buffer` objects are used to represent a fixed-length sequence of bytes. Many + * Node.js APIs support `Buffer`s. + * + * The `Buffer` class is a subclass of JavaScript's [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) class and + * extends it with methods that cover additional use cases. Node.js APIs accept + * plain [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) s wherever `Buffer`s are supported as well. + * + * While the `Buffer` class is available within the global scope, it is still + * recommended to explicitly reference it via an import or require statement. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Creates a zero-filled Buffer of length 10. + * const buf1 = Buffer.alloc(10); + * + * // Creates a Buffer of length 10, + * // filled with bytes which all have the value `1`. + * const buf2 = Buffer.alloc(10, 1); + * + * // Creates an uninitialized buffer of length 10. + * // This is faster than calling Buffer.alloc() but the returned + * // Buffer instance might contain old data that needs to be + * // overwritten using fill(), write(), or other functions that fill the Buffer's + * // contents. + * const buf3 = Buffer.allocUnsafe(10); + * + * // Creates a Buffer containing the bytes [1, 2, 3]. + * const buf4 = Buffer.from([1, 2, 3]); + * + * // Creates a Buffer containing the bytes [1, 1, 1, 1] – the entries + * // are all truncated using `(value & 255)` to fit into the range 0–255. + * const buf5 = Buffer.from([257, 257.5, -255, '1']); + * + * // Creates a Buffer containing the UTF-8-encoded bytes for the string 'tést': + * // [0x74, 0xc3, 0xa9, 0x73, 0x74] (in hexadecimal notation) + * // [116, 195, 169, 115, 116] (in decimal notation) + * const buf6 = Buffer.from('tést'); + * + * // Creates a Buffer containing the Latin-1 bytes [0x74, 0xe9, 0x73, 0x74]. + * const buf7 = Buffer.from('tést', 'latin1'); + * ``` + * @see [source](https://github.com/nodejs/node/blob/v16.9.0/lib/buffer.js) + */ +export class Buffer extends Uint8Array { + /** + * Allocates a new buffer containing the given {str}. + * + * @param str String to store in buffer. + * @param encoding encoding to use, optional. Default is 'utf8' + * @deprecated since v10.0.0 - Use `Buffer.from(string[, encoding])` instead. + */ + constructor(str: string, encoding?: Encoding); + /** + * Allocates a new buffer of {size} octets. + * + * @param size count of octets to allocate. + * @deprecated since v10.0.0 - Use `Buffer.alloc()` instead (also see `Buffer.allocUnsafe()`). + */ + constructor(size: number); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + * @deprecated since v10.0.0 - Use `Buffer.from(array)` instead. + */ + constructor(array: Uint8Array); + /** + * Produces a Buffer backed by the same allocated memory as + * the given {ArrayBuffer}/{SharedArrayBuffer}. + * + * @param arrayBuffer The ArrayBuffer with which to share memory. + * @deprecated since v10.0.0 - Use `Buffer.from(arrayBuffer[, byteOffset[, length]])` instead. + */ + constructor(arrayBuffer: ArrayBuffer | SharedArrayBuffer); + /** + * Allocates a new buffer containing the given {array} of octets. + * + * @param array The octets to store. + * @deprecated since v10.0.0 - Use `Buffer.from(array)` instead. + */ + constructor(array: ReadonlyArray); + /** + * Copies the passed {buffer} data onto a new {Buffer} instance. + * + * @param buffer The buffer to copy. + * @deprecated since v10.0.0 - Use `Buffer.from(buffer)` instead. + */ + constructor(buffer: Buffer); + /** + * Allocates a new `Buffer` using an `array` of bytes in the range `0` – `255`. + * Array entries outside that range will be truncated to fit into it. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Creates a new Buffer containing the UTF-8 bytes of the string 'buffer'. + * const buf = Buffer.from([0x62, 0x75, 0x66, 0x66, 0x65, 0x72]); + * ``` + * + * A `TypeError` will be thrown if `array` is not an `Array` or another type + * appropriate for `Buffer.from()` variants. + * + * `Buffer.from(array)` and `Buffer.from(string)` may also use the internal`Buffer` pool like `Buffer.allocUnsafe()` does. + * @since v5.10.0 + */ + static from( + arrayBuffer: WithImplicitCoercion, + byteOffset?: number, + length?: number, + ): Buffer; + /** + * Creates a new Buffer using the passed {data} + * @param data data to create a new Buffer + */ + static from(data: Uint8Array | ReadonlyArray): Buffer; + static from( + data: WithImplicitCoercion | string>, + ): Buffer; + /** + * Creates a new Buffer containing the given JavaScript string {str}. + * If provided, the {encoding} parameter identifies the character encoding. + * If not provided, {encoding} defaults to 'utf8'. + */ + static from( + str: + | WithImplicitCoercion + | { + [Symbol.toPrimitive](hint: "string"): string; + }, + encoding?: Encoding, + ): Buffer; + /** + * Creates a new Buffer using the passed {data} + * @param values to create a new Buffer + */ + static of(...items: number[]): Buffer; + /** + * Returns `true` if `obj` is a `Buffer`, `false` otherwise. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * Buffer.isBuffer(Buffer.alloc(10)); // true + * Buffer.isBuffer(Buffer.from('foo')); // true + * Buffer.isBuffer('a string'); // false + * Buffer.isBuffer([]); // false + * Buffer.isBuffer(new Uint8Array(1024)); // false + * ``` + * @since v0.1.101 + */ + static isBuffer(obj: unknown): obj is Buffer; + /** + * Returns `true` if `encoding` is the name of a supported character encoding, + * or `false` otherwise. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * console.log(Buffer.isEncoding('utf8')); + * // Prints: true + * + * console.log(Buffer.isEncoding('hex')); + * // Prints: true + * + * console.log(Buffer.isEncoding('utf/8')); + * // Prints: false + * + * console.log(Buffer.isEncoding('')); + * // Prints: false + * ``` + * @since v0.9.1 + * @param encoding A character encoding name to check. + */ + static isEncoding(encoding: string): boolean; + /** + * Returns the byte length of a string when encoded using `encoding`. + * This is not the same as [`String.prototype.length`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/length), which does not account + * for the encoding that is used to convert the string into bytes. + * + * For `'base64'`, `'base64url'`, and `'hex'`, this function assumes valid input. + * For strings that contain non-base64/hex-encoded data (e.g. whitespace), the + * return value might be greater than the length of a `Buffer` created from the + * string. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const str = '\u00bd + \u00bc = \u00be'; + * + * console.log(`${str}: ${str.length} characters, ` + + * `${Buffer.byteLength(str, 'utf8')} bytes`); + * // Prints: ½ + ¼ = ¾: 9 characters, 12 bytes + * ``` + * + * When `string` is a + * `Buffer`/[`DataView`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView)/[`TypedArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/- + * Reference/Global_Objects/TypedArray)/[`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)/[`SharedArrayBuffer`](https://develop- + * er.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer), the byte length as reported by `.byteLength`is returned. + * @since v0.1.90 + * @param string A value to calculate the length of. + * @param [encoding='utf8'] If `string` is a string, this is its encoding. + * @return The number of bytes contained within `string`. + */ + static byteLength( + string: + | string + | ArrayBufferView + | ArrayBuffer + | SharedArrayBuffer, + encoding?: Encoding, + ): number; + /** + * Returns a new `Buffer` which is the result of concatenating all the `Buffer`instances in the `list` together. + * + * If the list has no items, or if the `totalLength` is 0, then a new zero-length`Buffer` is returned. + * + * If `totalLength` is not provided, it is calculated from the `Buffer` instances + * in `list` by adding their lengths. + * + * If `totalLength` is provided, it is coerced to an unsigned integer. If the + * combined length of the `Buffer`s in `list` exceeds `totalLength`, the result is + * truncated to `totalLength`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Create a single `Buffer` from a list of three `Buffer` instances. + * + * const buf1 = Buffer.alloc(10); + * const buf2 = Buffer.alloc(14); + * const buf3 = Buffer.alloc(18); + * const totalLength = buf1.length + buf2.length + buf3.length; + * + * console.log(totalLength); + * // Prints: 42 + * + * const bufA = Buffer.concat([buf1, buf2, buf3], totalLength); + * + * console.log(bufA); + * // Prints: + * console.log(bufA.length); + * // Prints: 42 + * ``` + * + * `Buffer.concat()` may also use the internal `Buffer` pool like `Buffer.allocUnsafe()` does. + * @since v0.7.11 + * @param list List of `Buffer` or {@link Uint8Array} instances to concatenate. + * @param totalLength Total length of the `Buffer` instances in `list` when concatenated. + */ + static concat( + list: ReadonlyArray, + totalLength?: number, + ): Buffer; + /** + * Compares `buf1` to `buf2`, typically for the purpose of sorting arrays of`Buffer` instances. This is equivalent to calling `buf1.compare(buf2)`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.from('1234'); + * const buf2 = Buffer.from('0123'); + * const arr = [buf1, buf2]; + * + * console.log(arr.sort(Buffer.compare)); + * // Prints: [ , ] + * // (This result is equal to: [buf2, buf1].) + * ``` + * @since v0.11.13 + * @return Either `-1`, `0`, or `1`, depending on the result of the comparison. See `compare` for details. + */ + static compare(buf1: Uint8Array, buf2: Uint8Array): number; + /** + * Allocates a new `Buffer` of `size` bytes. If `fill` is `undefined`, the`Buffer` will be zero-filled. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.alloc(5); + * + * console.log(buf); + * // Prints: + * ``` + * + * If `size` is larger than {@link constants.MAX_LENGTH} or smaller than 0, `ERR_INVALID_ARG_VALUE` is thrown. + * + * If `fill` is specified, the allocated `Buffer` will be initialized by calling `buf.fill(fill)`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.alloc(5, 'a'); + * + * console.log(buf); + * // Prints: + * ``` + * + * If both `fill` and `encoding` are specified, the allocated `Buffer` will be + * initialized by calling `buf.fill(fill, encoding)`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.alloc(11, 'aGVsbG8gd29ybGQ=', 'base64'); + * + * console.log(buf); + * // Prints: + * ``` + * + * Calling `Buffer.alloc()` can be measurably slower than the alternative `Buffer.allocUnsafe()` but ensures that the newly created `Buffer` instance + * contents will never contain sensitive data from previous allocations, including + * data that might not have been allocated for `Buffer`s. + * + * A `TypeError` will be thrown if `size` is not a number. + * @since v5.10.0 + * @param size The desired length of the new `Buffer`. + * @param [fill=0] A value to pre-fill the new `Buffer` with. + * @param [encoding='utf8'] If `fill` is a string, this is its encoding. + */ + static alloc( + size: number, + fill?: string | Uint8Array | number, + encoding?: Encoding, + ): Buffer; + /** + * Allocates a new `Buffer` of `size` bytes. If `size` is larger than {@link constants.MAX_LENGTH} or smaller than 0, `ERR_INVALID_ARG_VALUE` is thrown. + * + * The underlying memory for `Buffer` instances created in this way is _not_ + * _initialized_. The contents of the newly created `Buffer` are unknown and_may contain sensitive data_. Use `Buffer.alloc()` instead to initialize`Buffer` instances with zeroes. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(10); + * + * console.log(buf); + * // Prints (contents may vary): + * + * buf.fill(0); + * + * console.log(buf); + * // Prints: + * ``` + * + * A `TypeError` will be thrown if `size` is not a number. + * + * The `Buffer` module pre-allocates an internal `Buffer` instance of + * size `Buffer.poolSize` that is used as a pool for the fast allocation of new`Buffer` instances created using `Buffer.allocUnsafe()`,`Buffer.from(array)`, `Buffer.concat()`, and the + * deprecated`new Buffer(size)` constructor only when `size` is less than or equal + * to `Buffer.poolSize >> 1` (floor of `Buffer.poolSize` divided by two). + * + * Use of this pre-allocated internal memory pool is a key difference between + * calling `Buffer.alloc(size, fill)` vs. `Buffer.allocUnsafe(size).fill(fill)`. + * Specifically, `Buffer.alloc(size, fill)` will _never_ use the internal `Buffer`pool, while `Buffer.allocUnsafe(size).fill(fill)`_will_ use the internal`Buffer` pool if `size` is less + * than or equal to half `Buffer.poolSize`. The + * difference is subtle but can be important when an application requires the + * additional performance that `Buffer.allocUnsafe()` provides. + * @since v5.10.0 + * @param size The desired length of the new `Buffer`. + */ + static allocUnsafe(size: number): Buffer; + /** + * Allocates a new `Buffer` of `size` bytes. If `size` is larger than {@link constants.MAX_LENGTH} or smaller than 0, `ERR_INVALID_ARG_VALUE` is thrown. A zero-length `Buffer` is created + * if `size` is 0. + * + * The underlying memory for `Buffer` instances created in this way is _not_ + * _initialized_. The contents of the newly created `Buffer` are unknown and_may contain sensitive data_. Use `buf.fill(0)` to initialize + * such `Buffer` instances with zeroes. + * + * When using `Buffer.allocUnsafe()` to allocate new `Buffer` instances, + * allocations under 4 KB are sliced from a single pre-allocated `Buffer`. This + * allows applications to avoid the garbage collection overhead of creating many + * individually allocated `Buffer` instances. This approach improves both + * performance and memory usage by eliminating the need to track and clean up as + * many individual `ArrayBuffer` objects. + * + * However, in the case where a developer may need to retain a small chunk of + * memory from a pool for an indeterminate amount of time, it may be appropriate + * to create an un-pooled `Buffer` instance using `Buffer.allocUnsafeSlow()` and + * then copying out the relevant bits. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Need to keep around a few small chunks of memory. + * const store = []; + * + * socket.on('readable', () => { + * let data; + * while (null !== (data = readable.read())) { + * // Allocate for retained data. + * const sb = Buffer.allocUnsafeSlow(10); + * + * // Copy the data into the new allocation. + * data.copy(sb, 0, 0, 10); + * + * store.push(sb); + * } + * }); + * ``` + * + * A `TypeError` will be thrown if `size` is not a number. + * @since v5.12.0 + * @param size The desired length of the new `Buffer`. + */ + static allocUnsafeSlow(size: number): Buffer; + // /** + // * This is the size (in bytes) of pre-allocated internal `Buffer` instances used + // * for pooling. This value may be modified. + // * @since v0.11.3 + // */ + // static poolSize: number; + + /** + * Writes `string` to `buf` at `offset` according to the character encoding in`encoding`. The `length` parameter is the number of bytes to write. If `buf` did + * not contain enough space to fit the entire string, only part of `string` will be + * written. However, partially encoded characters will not be written. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.alloc(256); + * + * const len = buf.write('\u00bd + \u00bc = \u00be', 0); + * + * console.log(`${len} bytes: ${buf.toString('utf8', 0, len)}`); + * // Prints: 12 bytes: ½ + ¼ = ¾ + * + * const buffer = Buffer.alloc(10); + * + * const length = buffer.write('abcd', 8); + * + * console.log(`${length} bytes: ${buffer.toString('utf8', 8, 10)}`); + * // Prints: 2 bytes : ab + * ``` + * @since v0.1.90 + * @param string String to write to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write `string`. + * @param [length=buf.length - offset] Maximum number of bytes to write (written bytes will not exceed `buf.length - offset`). + * @param [encoding='utf8'] The character encoding of `string`. + * @return Number of bytes written. + */ + write(string: string, encoding?: Encoding): number; + write(string: string, offset: number, encoding?: Encoding): number; + write( + string: string, + offset: number, + length: number, + encoding?: Encoding, + ): number; + /** + * Decodes `buf` to a string according to the specified character encoding in`encoding`. `start` and `end` may be passed to decode only a subset of `buf`. + * + * If `encoding` is `'utf8'` and a byte sequence in the input is not valid UTF-8, + * then each invalid byte is replaced with the replacement character `U+FFFD`. + * + * The maximum length of a string instance (in UTF-16 code units) is available + * as {@link constants.MAX_STRING_LENGTH}. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.allocUnsafe(26); + * + * for (let i = 0; i < 26; i++) { + * // 97 is the decimal ASCII value for 'a'. + * buf1[i] = i + 97; + * } + * + * console.log(buf1.toString('utf8')); + * // Prints: abcdefghijklmnopqrstuvwxyz + * console.log(buf1.toString('utf8', 0, 5)); + * // Prints: abcde + * + * const buf2 = Buffer.from('tést'); + * + * console.log(buf2.toString('hex')); + * // Prints: 74c3a97374 + * console.log(buf2.toString('utf8', 0, 3)); + * // Prints: té + * console.log(buf2.toString(undefined, 0, 3)); + * // Prints: té + * ``` + * @since v0.1.90 + * @param [encoding='utf8'] The character encoding to use. + * @param [start=0] The byte offset to start decoding at. + * @param [end=buf.length] The byte offset to stop decoding at (not inclusive). + */ + toString(encoding?: Encoding, start?: number, end?: number): string; + /** + * Returns a JSON representation of `buf`. [`JSON.stringify()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) implicitly calls + * this function when stringifying a `Buffer` instance. + * + * `Buffer.from()` accepts objects in the format returned from this method. + * In particular, `Buffer.from(buf.toJSON())` works like `Buffer.from(buf)`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]); + * const json = JSON.stringify(buf); + * + * console.log(json); + * // Prints: {"type":"Buffer","data":[1,2,3,4,5]} + * + * const copy = JSON.parse(json, (key, value) => { + * return value && value.type === 'Buffer' ? + * Buffer.from(value) : + * value; + * }); + * + * console.log(copy); + * // Prints: + * ``` + * @since v0.9.2 + */ + toJSON(): { + type: "Buffer"; + data: number[]; + }; + /** + * Returns `true` if both `buf` and `otherBuffer` have exactly the same bytes,`false` otherwise. Equivalent to `buf.compare(otherBuffer) === 0`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.from('ABC'); + * const buf2 = Buffer.from('414243', 'hex'); + * const buf3 = Buffer.from('ABCD'); + * + * console.log(buf1.equals(buf2)); + * // Prints: true + * console.log(buf1.equals(buf3)); + * // Prints: false + * ``` + * @since v0.11.13 + * @param otherBuffer A `Buffer` or {@link Uint8Array} with which to compare `buf`. + */ + equals(otherBuffer: Uint8Array): boolean; + /** + * Compares `buf` with `target` and returns a number indicating whether `buf`comes before, after, or is the same as `target` in sort order. + * Comparison is based on the actual sequence of bytes in each `Buffer`. + * + * * `0` is returned if `target` is the same as `buf` + * * `1` is returned if `target` should come _before_`buf` when sorted. + * * `-1` is returned if `target` should come _after_`buf` when sorted. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.from('ABC'); + * const buf2 = Buffer.from('BCD'); + * const buf3 = Buffer.from('ABCD'); + * + * console.log(buf1.compare(buf1)); + * // Prints: 0 + * console.log(buf1.compare(buf2)); + * // Prints: -1 + * console.log(buf1.compare(buf3)); + * // Prints: -1 + * console.log(buf2.compare(buf1)); + * // Prints: 1 + * console.log(buf2.compare(buf3)); + * // Prints: 1 + * console.log([buf1, buf2, buf3].sort(Buffer.compare)); + * // Prints: [ , , ] + * // (This result is equal to: [buf1, buf3, buf2].) + * ``` + * + * The optional `targetStart`, `targetEnd`, `sourceStart`, and `sourceEnd`arguments can be used to limit the comparison to specific ranges within `target`and `buf` respectively. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8, 9]); + * const buf2 = Buffer.from([5, 6, 7, 8, 9, 1, 2, 3, 4]); + * + * console.log(buf1.compare(buf2, 5, 9, 0, 4)); + * // Prints: 0 + * console.log(buf1.compare(buf2, 0, 6, 4)); + * // Prints: -1 + * console.log(buf1.compare(buf2, 5, 6, 5)); + * // Prints: 1 + * ``` + * + * `ERR_OUT_OF_RANGE` is thrown if `targetStart < 0`, `sourceStart < 0`,`targetEnd > target.byteLength`, or `sourceEnd > source.byteLength`. + * @since v0.11.13 + * @param target A `Buffer` or {@link Uint8Array} with which to compare `buf`. + * @param [targetStart=0] The offset within `target` at which to begin comparison. + * @param [targetEnd=target.length] The offset within `target` at which to end comparison (not inclusive). + * @param [sourceStart=0] The offset within `buf` at which to begin comparison. + * @param [sourceEnd=buf.length] The offset within `buf` at which to end comparison (not inclusive). + */ + compare( + target: Uint8Array, + targetStart?: number, + targetEnd?: number, + sourceStart?: number, + sourceEnd?: number, + ): number; + /** + * Copies data from a region of `buf` to a region in `target`, even if the `target`memory region overlaps with `buf`. + * + * [`TypedArray.prototype.set()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/set) performs the same operation, and is available + * for all TypedArrays, including Node.js `Buffer`s, although it takes + * different function arguments. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Create two `Buffer` instances. + * const buf1 = Buffer.allocUnsafe(26); + * const buf2 = Buffer.allocUnsafe(26).fill('!'); + * + * for (let i = 0; i < 26; i++) { + * // 97 is the decimal ASCII value for 'a'. + * buf1[i] = i + 97; + * } + * + * // Copy `buf1` bytes 16 through 19 into `buf2` starting at byte 8 of `buf2`. + * buf1.copy(buf2, 8, 16, 20); + * // This is equivalent to: + * // buf2.set(buf1.subarray(16, 20), 8); + * + * console.log(buf2.toString('ascii', 0, 25)); + * // Prints: !!!!!!!!qrst!!!!!!!!!!!!! + * ``` + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Create a `Buffer` and copy data from one region to an overlapping region + * // within the same `Buffer`. + * + * const buf = Buffer.allocUnsafe(26); + * + * for (let i = 0; i < 26; i++) { + * // 97 is the decimal ASCII value for 'a'. + * buf[i] = i + 97; + * } + * + * buf.copy(buf, 0, 4, 10); + * + * console.log(buf.toString()); + * // Prints: efghijghijklmnopqrstuvwxyz + * ``` + * @since v0.1.90 + * @param target A `Buffer` or {@link Uint8Array} to copy into. + * @param [targetStart=0] The offset within `target` at which to begin writing. + * @param [sourceStart=0] The offset within `buf` from which to begin copying. + * @param [sourceEnd=buf.length] The offset within `buf` at which to stop copying (not inclusive). + * @return The number of bytes copied. + */ + copy( + target: Uint8Array, + targetStart?: number, + sourceStart?: number, + sourceEnd?: number, + ): number; + /** + * Returns a new `Buffer` that references the same memory as the original, but + * offset and cropped by the `start` and `end` indices. + * + * This is the same behavior as `buf.subarray()`. + * + * This method is not compatible with the `Uint8Array.prototype.slice()`, + * which is a superclass of `Buffer`. To copy the slice, use`Uint8Array.prototype.slice()`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('buffer'); + * + * const copiedBuf = Uint8Array.prototype.slice.call(buf); + * copiedBuf[0]++; + * console.log(copiedBuf.toString()); + * // Prints: cuffer + * + * console.log(buf.toString()); + * // Prints: buffer + * ``` + * @since v0.3.0 + * @param [start=0] Where the new `Buffer` will start. + * @param [end=buf.length] Where the new `Buffer` will end (not inclusive). + */ + slice(start?: number, end?: number): Buffer; + /** + * Returns a new `Buffer` that references the same memory as the original, but + * offset and cropped by the `start` and `end` indices. + * + * Specifying `end` greater than `buf.length` will return the same result as + * that of `end` equal to `buf.length`. + * + * This method is inherited from [`TypedArray.prototype.subarray()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray). + * + * Modifying the new `Buffer` slice will modify the memory in the original `Buffer`because the allocated memory of the two objects overlap. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Create a `Buffer` with the ASCII alphabet, take a slice, and modify one byte + * // from the original `Buffer`. + * + * const buf1 = Buffer.allocUnsafe(26); + * + * for (let i = 0; i < 26; i++) { + * // 97 is the decimal ASCII value for 'a'. + * buf1[i] = i + 97; + * } + * + * const buf2 = buf1.subarray(0, 3); + * + * console.log(buf2.toString('ascii', 0, buf2.length)); + * // Prints: abc + * + * buf1[0] = 33; + * + * console.log(buf2.toString('ascii', 0, buf2.length)); + * // Prints: !bc + * ``` + * + * Specifying negative indexes causes the slice to be generated relative to the + * end of `buf` rather than the beginning. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('buffer'); + * + * console.log(buf.subarray(-6, -1).toString()); + * // Prints: buffe + * // (Equivalent to buf.subarray(0, 5).) + * + * console.log(buf.subarray(-6, -2).toString()); + * // Prints: buff + * // (Equivalent to buf.subarray(0, 4).) + * + * console.log(buf.subarray(-5, -2).toString()); + * // Prints: uff + * // (Equivalent to buf.subarray(1, 4).) + * ``` + * @since v3.0.0 + * @param [start=0] Where the new `Buffer` will start. + * @param [end=buf.length] Where the new `Buffer` will end (not inclusive). + */ + subarray(start?: number, end?: number): Buffer; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. + * + * `value` is interpreted and written as a two's complement signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(8); + * + * buf.writeBigInt64BE(0x0102030405060708n, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v12.0.0, v10.20.0 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 8`. + * @return `offset` plus the number of bytes written. + */ + writeBigInt64BE(value: bigint, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian. + * + * `value` is interpreted and written as a two's complement signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(8); + * + * buf.writeBigInt64LE(0x0102030405060708n, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v12.0.0, v10.20.0 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 8`. + * @return `offset` plus the number of bytes written. + */ + writeBigInt64LE(value: bigint, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. + * + * This function is also available under the `writeBigUint64BE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(8); + * + * buf.writeBigUInt64BE(0xdecafafecacefaden, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v12.0.0, v10.20.0 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 8`. + * @return `offset` plus the number of bytes written. + */ + writeBigUInt64BE(value: bigint, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(8); + * + * buf.writeBigUInt64LE(0xdecafafecacefaden, 0); + * + * console.log(buf); + * // Prints: + * ``` + * + * This function is also available under the `writeBigUint64LE` alias. + * @since v12.0.0, v10.20.0 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy: `0 <= offset <= buf.length - 8`. + * @return `offset` plus the number of bytes written. + */ + writeBigUInt64LE(value: bigint, offset?: number): number; + /** + * Writes `byteLength` bytes of `value` to `buf` at the specified `offset`as little-endian. Supports up to 48 bits of accuracy. Behavior is undefined + * when `value` is anything other than an unsigned integer. + * + * This function is also available under the `writeUintLE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(6); + * + * buf.writeUIntLE(0x1234567890ab, 0, 6); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param offset Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to write. Must satisfy `0 < byteLength <= 6`. + * @return `offset` plus the number of bytes written. + */ + writeUIntLE(value: number, offset: number, byteLength: number): number; + /** + * Writes `byteLength` bytes of `value` to `buf` at the specified `offset`as big-endian. Supports up to 48 bits of accuracy. Behavior is undefined + * when `value` is anything other than an unsigned integer. + * + * This function is also available under the `writeUintBE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(6); + * + * buf.writeUIntBE(0x1234567890ab, 0, 6); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param offset Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to write. Must satisfy `0 < byteLength <= 6`. + * @return `offset` plus the number of bytes written. + */ + writeUIntBE(value: number, offset: number, byteLength: number): number; + /** + * Writes `byteLength` bytes of `value` to `buf` at the specified `offset`as little-endian. Supports up to 48 bits of accuracy. Behavior is undefined + * when `value` is anything other than a signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(6); + * + * buf.writeIntLE(0x1234567890ab, 0, 6); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.11.15 + * @param value Number to be written to `buf`. + * @param offset Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to write. Must satisfy `0 < byteLength <= 6`. + * @return `offset` plus the number of bytes written. + */ + writeIntLE(value: number, offset: number, byteLength: number): number; + /** + * Writes `byteLength` bytes of `value` to `buf` at the specified `offset`as big-endian. Supports up to 48 bits of accuracy. Behavior is undefined when`value` is anything other than a + * signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(6); + * + * buf.writeIntBE(0x1234567890ab, 0, 6); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.11.15 + * @param value Number to be written to `buf`. + * @param offset Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to write. Must satisfy `0 < byteLength <= 6`. + * @return `offset` plus the number of bytes written. + */ + writeIntBE(value: number, offset: number, byteLength: number): number; + /** + * Reads an unsigned, big-endian 64-bit integer from `buf` at the specified`offset`. + * + * This function is also available under the `readBigUint64BE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]); + * + * console.log(buf.readBigUInt64BE(0)); + * // Prints: 4294967295n + * ``` + * @since v12.0.0, v10.20.0 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 8`. + */ + readBigUInt64BE(offset?: number): bigint; + /** + * Reads an unsigned, little-endian 64-bit integer from `buf` at the specified`offset`. + * + * This function is also available under the `readBigUint64LE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff]); + * + * console.log(buf.readBigUInt64LE(0)); + * // Prints: 18446744069414584320n + * ``` + * @since v12.0.0, v10.20.0 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 8`. + */ + readBigUInt64LE(offset?: number): bigint; + /** + * Reads a signed, big-endian 64-bit integer from `buf` at the specified `offset`. + * + * Integers read from a `Buffer` are interpreted as two's complement signed + * values. + * @since v12.0.0, v10.20.0 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 8`. + */ + readBigInt64BE(offset?: number): bigint; + /** + * Reads a signed, little-endian 64-bit integer from `buf` at the specified`offset`. + * + * Integers read from a `Buffer` are interpreted as two's complement signed + * values. + * @since v12.0.0, v10.20.0 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy: `0 <= offset <= buf.length - 8`. + */ + readBigInt64LE(offset?: number): bigint; + /** + * Reads `byteLength` number of bytes from `buf` at the specified `offset`and interprets the result as an unsigned, little-endian integer supporting + * up to 48 bits of accuracy. + * + * This function is also available under the `readUintLE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); + * + * console.log(buf.readUIntLE(0, 6).toString(16)); + * // Prints: ab9078563412 + * ``` + * @since v0.11.15 + * @param offset Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to read. Must satisfy `0 < byteLength <= 6`. + */ + readUIntLE(offset: number, byteLength: number): number; + /** + * Reads `byteLength` number of bytes from `buf` at the specified `offset`and interprets the result as an unsigned big-endian integer supporting + * up to 48 bits of accuracy. + * + * This function is also available under the `readUintBE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); + * + * console.log(buf.readUIntBE(0, 6).toString(16)); + * // Prints: 1234567890ab + * console.log(buf.readUIntBE(1, 6).toString(16)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.11.15 + * @param offset Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to read. Must satisfy `0 < byteLength <= 6`. + */ + readUIntBE(offset: number, byteLength: number): number; + /** + * Reads `byteLength` number of bytes from `buf` at the specified `offset`and interprets the result as a little-endian, two's complement signed value + * supporting up to 48 bits of accuracy. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); + * + * console.log(buf.readIntLE(0, 6).toString(16)); + * // Prints: -546f87a9cbee + * ``` + * @since v0.11.15 + * @param offset Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to read. Must satisfy `0 < byteLength <= 6`. + */ + readIntLE(offset: number, byteLength: number): number; + /** + * Reads `byteLength` number of bytes from `buf` at the specified `offset`and interprets the result as a big-endian, two's complement signed value + * supporting up to 48 bits of accuracy. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56, 0x78, 0x90, 0xab]); + * + * console.log(buf.readIntBE(0, 6).toString(16)); + * // Prints: 1234567890ab + * console.log(buf.readIntBE(1, 6).toString(16)); + * // Throws ERR_OUT_OF_RANGE. + * console.log(buf.readIntBE(1, 0).toString(16)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.11.15 + * @param offset Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - byteLength`. + * @param byteLength Number of bytes to read. Must satisfy `0 < byteLength <= 6`. + */ + readIntBE(offset: number, byteLength: number): number; + /** + * Reads an unsigned 8-bit integer from `buf` at the specified `offset`. + * + * This function is also available under the `readUint8` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([1, -2]); + * + * console.log(buf.readUInt8(0)); + * // Prints: 1 + * console.log(buf.readUInt8(1)); + * // Prints: 254 + * console.log(buf.readUInt8(2)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.5.0 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 1`. + */ + readUInt8(offset?: number): number; + /** + * Reads an unsigned, little-endian 16-bit integer from `buf` at the specified`offset`. + * + * This function is also available under the `readUint16LE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56]); + * + * console.log(buf.readUInt16LE(0).toString(16)); + * // Prints: 3412 + * console.log(buf.readUInt16LE(1).toString(16)); + * // Prints: 5634 + * console.log(buf.readUInt16LE(2).toString(16)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 2`. + */ + readUInt16LE(offset?: number): number; + /** + * Reads an unsigned, big-endian 16-bit integer from `buf` at the specified`offset`. + * + * This function is also available under the `readUint16BE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56]); + * + * console.log(buf.readUInt16BE(0).toString(16)); + * // Prints: 1234 + * console.log(buf.readUInt16BE(1).toString(16)); + * // Prints: 3456 + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 2`. + */ + readUInt16BE(offset?: number): number; + /** + * Reads an unsigned, little-endian 32-bit integer from `buf` at the specified`offset`. + * + * This function is also available under the `readUint32LE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56, 0x78]); + * + * console.log(buf.readUInt32LE(0).toString(16)); + * // Prints: 78563412 + * console.log(buf.readUInt32LE(1).toString(16)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 4`. + */ + readUInt32LE(offset?: number): number; + /** + * Reads an unsigned, big-endian 32-bit integer from `buf` at the specified`offset`. + * + * This function is also available under the `readUint32BE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0x12, 0x34, 0x56, 0x78]); + * + * console.log(buf.readUInt32BE(0).toString(16)); + * // Prints: 12345678 + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 4`. + */ + readUInt32BE(offset?: number): number; + /** + * Reads a signed 8-bit integer from `buf` at the specified `offset`. + * + * Integers read from a `Buffer` are interpreted as two's complement signed values. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([-1, 5]); + * + * console.log(buf.readInt8(0)); + * // Prints: -1 + * console.log(buf.readInt8(1)); + * // Prints: 5 + * console.log(buf.readInt8(2)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.5.0 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 1`. + */ + readInt8(offset?: number): number; + /** + * Reads a signed, little-endian 16-bit integer from `buf` at the specified`offset`. + * + * Integers read from a `Buffer` are interpreted as two's complement signed values. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0, 5]); + * + * console.log(buf.readInt16LE(0)); + * // Prints: 1280 + * console.log(buf.readInt16LE(1)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 2`. + */ + readInt16LE(offset?: number): number; + /** + * Reads a signed, big-endian 16-bit integer from `buf` at the specified `offset`. + * + * Integers read from a `Buffer` are interpreted as two's complement signed values. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0, 5]); + * + * console.log(buf.readInt16BE(0)); + * // Prints: 5 + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 2`. + */ + readInt16BE(offset?: number): number; + /** + * Reads a signed, little-endian 32-bit integer from `buf` at the specified`offset`. + * + * Integers read from a `Buffer` are interpreted as two's complement signed values. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0, 0, 0, 5]); + * + * console.log(buf.readInt32LE(0)); + * // Prints: 83886080 + * console.log(buf.readInt32LE(1)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 4`. + */ + readInt32LE(offset?: number): number; + /** + * Reads a signed, big-endian 32-bit integer from `buf` at the specified `offset`. + * + * Integers read from a `Buffer` are interpreted as two's complement signed values. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([0, 0, 0, 5]); + * + * console.log(buf.readInt32BE(0)); + * // Prints: 5 + * ``` + * @since v0.5.5 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 4`. + */ + readInt32BE(offset?: number): number; + /** + * Reads a 32-bit, little-endian float from `buf` at the specified `offset`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([1, 2, 3, 4]); + * + * console.log(buf.readFloatLE(0)); + * // Prints: 1.539989614439558e-36 + * console.log(buf.readFloatLE(1)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.11.15 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 4`. + */ + readFloatLE(offset?: number): number; + /** + * Reads a 32-bit, big-endian float from `buf` at the specified `offset`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([1, 2, 3, 4]); + * + * console.log(buf.readFloatBE(0)); + * // Prints: 2.387939260590663e-38 + * ``` + * @since v0.11.15 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 4`. + */ + readFloatBE(offset?: number): number; + /** + * Reads a 64-bit, little-endian double from `buf` at the specified `offset`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); + * + * console.log(buf.readDoubleLE(0)); + * // Prints: 5.447603722011605e-270 + * console.log(buf.readDoubleLE(1)); + * // Throws ERR_OUT_OF_RANGE. + * ``` + * @since v0.11.15 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 8`. + */ + readDoubleLE(offset?: number): number; + /** + * Reads a 64-bit, big-endian double from `buf` at the specified `offset`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]); + * + * console.log(buf.readDoubleBE(0)); + * // Prints: 8.20788039913184e-304 + * ``` + * @since v0.11.15 + * @param [offset=0] Number of bytes to skip before starting to read. Must satisfy `0 <= offset <= buf.length - 8`. + */ + readDoubleBE(offset?: number): number; + reverse(): this; + /** + * Interprets `buf` as an array of unsigned 16-bit integers and swaps the + * byte order _in-place_. Throws `ERR_INVALID_BUFFER_SIZE` if `buf.length` is not a multiple of 2. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); + * + * console.log(buf1); + * // Prints: + * + * buf1.swap16(); + * + * console.log(buf1); + * // Prints: + * + * const buf2 = Buffer.from([0x1, 0x2, 0x3]); + * + * buf2.swap16(); + * // Throws ERR_INVALID_BUFFER_SIZE. + * ``` + * + * One convenient use of `buf.swap16()` is to perform a fast in-place conversion + * between UTF-16 little-endian and UTF-16 big-endian: + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('This is little-endian UTF-16', 'utf16le'); + * buf.swap16(); // Convert to big-endian UTF-16 text. + * ``` + * @since v5.10.0 + * @return A reference to `buf`. + */ + swap16(): Buffer; + /** + * Interprets `buf` as an array of unsigned 32-bit integers and swaps the + * byte order _in-place_. Throws `ERR_INVALID_BUFFER_SIZE` if `buf.length` is not a multiple of 4. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); + * + * console.log(buf1); + * // Prints: + * + * buf1.swap32(); + * + * console.log(buf1); + * // Prints: + * + * const buf2 = Buffer.from([0x1, 0x2, 0x3]); + * + * buf2.swap32(); + * // Throws ERR_INVALID_BUFFER_SIZE. + * ``` + * @since v5.10.0 + * @return A reference to `buf`. + */ + swap32(): Buffer; + /** + * Interprets `buf` as an array of 64-bit numbers and swaps byte order _in-place_. + * Throws `ERR_INVALID_BUFFER_SIZE` if `buf.length` is not a multiple of 8. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf1 = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8]); + * + * console.log(buf1); + * // Prints: + * + * buf1.swap64(); + * + * console.log(buf1); + * // Prints: + * + * const buf2 = Buffer.from([0x1, 0x2, 0x3]); + * + * buf2.swap64(); + * // Throws ERR_INVALID_BUFFER_SIZE. + * ``` + * @since v6.3.0 + * @return A reference to `buf`. + */ + swap64(): Buffer; + /** + * Writes `value` to `buf` at the specified `offset`. `value` must be a + * valid unsigned 8-bit integer. Behavior is undefined when `value` is anything + * other than an unsigned 8-bit integer. + * + * This function is also available under the `writeUint8` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeUInt8(0x3, 0); + * buf.writeUInt8(0x4, 1); + * buf.writeUInt8(0x23, 2); + * buf.writeUInt8(0x42, 3); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.0 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 1`. + * @return `offset` plus the number of bytes written. + */ + writeUInt8(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian. The `value`must be a valid unsigned 16-bit integer. Behavior is undefined when `value` is + * anything other than an unsigned 16-bit integer. + * + * This function is also available under the `writeUint16LE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeUInt16LE(0xdead, 0); + * buf.writeUInt16LE(0xbeef, 2); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 2`. + * @return `offset` plus the number of bytes written. + */ + writeUInt16LE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. The `value`must be a valid unsigned 16-bit integer. Behavior is undefined when `value`is anything other than an + * unsigned 16-bit integer. + * + * This function is also available under the `writeUint16BE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeUInt16BE(0xdead, 0); + * buf.writeUInt16BE(0xbeef, 2); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 2`. + * @return `offset` plus the number of bytes written. + */ + writeUInt16BE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian. The `value`must be a valid unsigned 32-bit integer. Behavior is undefined when `value` is + * anything other than an unsigned 32-bit integer. + * + * This function is also available under the `writeUint32LE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeUInt32LE(0xfeedface, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 4`. + * @return `offset` plus the number of bytes written. + */ + writeUInt32LE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. The `value`must be a valid unsigned 32-bit integer. Behavior is undefined when `value`is anything other than an + * unsigned 32-bit integer. + * + * This function is also available under the `writeUint32BE` alias. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeUInt32BE(0xfeedface, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 4`. + * @return `offset` plus the number of bytes written. + */ + writeUInt32BE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset`. `value` must be a valid + * signed 8-bit integer. Behavior is undefined when `value` is anything other than + * a signed 8-bit integer. + * + * `value` is interpreted and written as a two's complement signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(2); + * + * buf.writeInt8(2, 0); + * buf.writeInt8(-2, 1); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.0 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 1`. + * @return `offset` plus the number of bytes written. + */ + writeInt8(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian. The `value`must be a valid signed 16-bit integer. Behavior is undefined when `value` is + * anything other than a signed 16-bit integer. + * + * The `value` is interpreted and written as a two's complement signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(2); + * + * buf.writeInt16LE(0x0304, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 2`. + * @return `offset` plus the number of bytes written. + */ + writeInt16LE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. The `value`must be a valid signed 16-bit integer. Behavior is undefined when `value` is + * anything other than a signed 16-bit integer. + * + * The `value` is interpreted and written as a two's complement signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(2); + * + * buf.writeInt16BE(0x0102, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 2`. + * @return `offset` plus the number of bytes written. + */ + writeInt16BE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian. The `value`must be a valid signed 32-bit integer. Behavior is undefined when `value` is + * anything other than a signed 32-bit integer. + * + * The `value` is interpreted and written as a two's complement signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeInt32LE(0x05060708, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 4`. + * @return `offset` plus the number of bytes written. + */ + writeInt32LE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. The `value`must be a valid signed 32-bit integer. Behavior is undefined when `value` is + * anything other than a signed 32-bit integer. + * + * The `value` is interpreted and written as a two's complement signed integer. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeInt32BE(0x01020304, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.5.5 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 4`. + * @return `offset` plus the number of bytes written. + */ + writeInt32BE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian. Behavior is + * undefined when `value` is anything other than a JavaScript number. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeFloatLE(0xcafebabe, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.11.15 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 4`. + * @return `offset` plus the number of bytes written. + */ + writeFloatLE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. Behavior is + * undefined when `value` is anything other than a JavaScript number. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(4); + * + * buf.writeFloatBE(0xcafebabe, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.11.15 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 4`. + * @return `offset` plus the number of bytes written. + */ + writeFloatBE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as little-endian. The `value`must be a JavaScript number. Behavior is undefined when `value` is anything + * other than a JavaScript number. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(8); + * + * buf.writeDoubleLE(123.456, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.11.15 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 8`. + * @return `offset` plus the number of bytes written. + */ + writeDoubleLE(value: number, offset?: number): number; + /** + * Writes `value` to `buf` at the specified `offset` as big-endian. The `value`must be a JavaScript number. Behavior is undefined when `value` is anything + * other than a JavaScript number. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(8); + * + * buf.writeDoubleBE(123.456, 0); + * + * console.log(buf); + * // Prints: + * ``` + * @since v0.11.15 + * @param value Number to be written to `buf`. + * @param [offset=0] Number of bytes to skip before starting to write. Must satisfy `0 <= offset <= buf.length - 8`. + * @return `offset` plus the number of bytes written. + */ + writeDoubleBE(value: number, offset?: number): number; + /** + * Fills `buf` with the specified `value`. If the `offset` and `end` are not given, + * the entire `buf` will be filled: + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Fill a `Buffer` with the ASCII character 'h'. + * + * const b = Buffer.allocUnsafe(50).fill('h'); + * + * console.log(b.toString()); + * // Prints: hhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhhh + * ``` + * + * `value` is coerced to a `uint32` value if it is not a string, `Buffer`, or + * integer. If the resulting integer is greater than `255` (decimal), `buf` will be + * filled with `value & 255`. + * + * If the final write of a `fill()` operation falls on a multi-byte character, + * then only the bytes of that character that fit into `buf` are written: + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Fill a `Buffer` with character that takes up two bytes in UTF-8. + * + * console.log(Buffer.allocUnsafe(5).fill('\u0222')); + * // Prints: + * ``` + * + * If `value` contains invalid characters, it is truncated; if no valid + * fill data remains, an exception is thrown: + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.allocUnsafe(5); + * + * console.log(buf.fill('a')); + * // Prints: + * console.log(buf.fill('aazz', 'hex')); + * // Prints: + * console.log(buf.fill('zz', 'hex')); + * // Throws an exception. + * ``` + * @since v0.5.0 + * @param value The value with which to fill `buf`. + * @param [offset=0] Number of bytes to skip before starting to fill `buf`. + * @param [end=buf.length] Where to stop filling `buf` (not inclusive). + * @param [encoding='utf8'] The encoding for `value` if `value` is a string. + * @return A reference to `buf`. + */ + fill( + value: string | Uint8Array | number, + offset?: number, + end?: number, + encoding?: Encoding, + ): this; + /** + * If `value` is: + * + * * a string, `value` is interpreted according to the character encoding in`encoding`. + * * a `Buffer` or [`Uint8Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array), `value` will be used in its entirety. + * To compare a partial `Buffer`, use `buf.slice()`. + * * a number, `value` will be interpreted as an unsigned 8-bit integer + * value between `0` and `255`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('this is a buffer'); + * + * console.log(buf.indexOf('this')); + * // Prints: 0 + * console.log(buf.indexOf('is')); + * // Prints: 2 + * console.log(buf.indexOf(Buffer.from('a buffer'))); + * // Prints: 8 + * console.log(buf.indexOf(97)); + * // Prints: 8 (97 is the decimal ASCII value for 'a') + * console.log(buf.indexOf(Buffer.from('a buffer example'))); + * // Prints: -1 + * console.log(buf.indexOf(Buffer.from('a buffer example').slice(0, 8))); + * // Prints: 8 + * + * const utf16Buffer = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'utf16le'); + * + * console.log(utf16Buffer.indexOf('\u03a3', 0, 'utf16le')); + * // Prints: 4 + * console.log(utf16Buffer.indexOf('\u03a3', -4, 'utf16le')); + * // Prints: 6 + * ``` + * + * If `value` is not a string, number, or `Buffer`, this method will throw a`TypeError`. If `value` is a number, it will be coerced to a valid byte value, + * an integer between 0 and 255. + * + * If `byteOffset` is not a number, it will be coerced to a number. If the result + * of coercion is `NaN` or `0`, then the entire buffer will be searched. This + * behavior matches [`String.prototype.indexOf()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf). + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const b = Buffer.from('abcdef'); + * + * // Passing a value that's a number, but not a valid byte. + * // Prints: 2, equivalent to searching for 99 or 'c'. + * console.log(b.indexOf(99.9)); + * console.log(b.indexOf(256 + 99)); + * + * // Passing a byteOffset that coerces to NaN or 0. + * // Prints: 1, searching the whole buffer. + * console.log(b.indexOf('b', undefined)); + * console.log(b.indexOf('b', {})); + * console.log(b.indexOf('b', null)); + * console.log(b.indexOf('b', [])); + * ``` + * + * If `value` is an empty string or empty `Buffer` and `byteOffset` is less + * than `buf.length`, `byteOffset` will be returned. If `value` is empty and`byteOffset` is at least `buf.length`, `buf.length` will be returned. + * @since v1.5.0 + * @param value What to search for. + * @param [byteOffset=0] Where to begin searching in `buf`. If negative, then offset is calculated from the end of `buf`. + * @param [encoding='utf8'] If `value` is a string, this is the encoding used to determine the binary representation of the string that will be searched for in `buf`. + * @return The index of the first occurrence of `value` in `buf`, or `-1` if `buf` does not contain `value`. + */ + indexOf( + value: string | number | Uint8Array, + byteOffset?: number, + encoding?: Encoding, + ): number; + /** + * Identical to `buf.indexOf()`, except the last occurrence of `value` is found + * rather than the first occurrence. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('this buffer is a buffer'); + * + * console.log(buf.lastIndexOf('this')); + * // Prints: 0 + * console.log(buf.lastIndexOf('buffer')); + * // Prints: 17 + * console.log(buf.lastIndexOf(Buffer.from('buffer'))); + * // Prints: 17 + * console.log(buf.lastIndexOf(97)); + * // Prints: 15 (97 is the decimal ASCII value for 'a') + * console.log(buf.lastIndexOf(Buffer.from('yolo'))); + * // Prints: -1 + * console.log(buf.lastIndexOf('buffer', 5)); + * // Prints: 5 + * console.log(buf.lastIndexOf('buffer', 4)); + * // Prints: -1 + * + * const utf16Buffer = Buffer.from('\u039a\u0391\u03a3\u03a3\u0395', 'utf16le'); + * + * console.log(utf16Buffer.lastIndexOf('\u03a3', undefined, 'utf16le')); + * // Prints: 6 + * console.log(utf16Buffer.lastIndexOf('\u03a3', -5, 'utf16le')); + * // Prints: 4 + * ``` + * + * If `value` is not a string, number, or `Buffer`, this method will throw a`TypeError`. If `value` is a number, it will be coerced to a valid byte value, + * an integer between 0 and 255. + * + * If `byteOffset` is not a number, it will be coerced to a number. Any arguments + * that coerce to `NaN`, like `{}` or `undefined`, will search the whole buffer. + * This behavior matches [`String.prototype.lastIndexOf()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf). + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const b = Buffer.from('abcdef'); + * + * // Passing a value that's a number, but not a valid byte. + * // Prints: 2, equivalent to searching for 99 or 'c'. + * console.log(b.lastIndexOf(99.9)); + * console.log(b.lastIndexOf(256 + 99)); + * + * // Passing a byteOffset that coerces to NaN. + * // Prints: 1, searching the whole buffer. + * console.log(b.lastIndexOf('b', undefined)); + * console.log(b.lastIndexOf('b', {})); + * + * // Passing a byteOffset that coerces to 0. + * // Prints: -1, equivalent to passing 0. + * console.log(b.lastIndexOf('b', null)); + * console.log(b.lastIndexOf('b', [])); + * ``` + * + * If `value` is an empty string or empty `Buffer`, `byteOffset` will be returned. + * @since v6.0.0 + * @param value What to search for. + * @param [byteOffset=buf.length - 1] Where to begin searching in `buf`. If negative, then offset is calculated from the end of `buf`. + * @param [encoding='utf8'] If `value` is a string, this is the encoding used to determine the binary representation of the string that will be searched for in `buf`. + * @return The index of the last occurrence of `value` in `buf`, or `-1` if `buf` does not contain `value`. + */ + lastIndexOf( + value: string | number | Uint8Array, + byteOffset?: number, + encoding?: Encoding, + ): number; + /** + * Creates and returns an [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) of `[index, byte]` pairs from the contents + * of `buf`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * // Log the entire contents of a `Buffer`. + * + * const buf = Buffer.from('buffer'); + * + * for (const pair of buf.entries()) { + * console.log(pair); + * } + * // Prints: + * // [0, 98] + * // [1, 117] + * // [2, 102] + * // [3, 102] + * // [4, 101] + * // [5, 114] + * ``` + * @since v1.1.0 + */ + entries(): IterableIterator<[number, number]>; + /** + * Equivalent to `buf.indexOf() !== -1`. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('this is a buffer'); + * + * console.log(buf.includes('this')); + * // Prints: true + * console.log(buf.includes('is')); + * // Prints: true + * console.log(buf.includes(Buffer.from('a buffer'))); + * // Prints: true + * console.log(buf.includes(97)); + * // Prints: true (97 is the decimal ASCII value for 'a') + * console.log(buf.includes(Buffer.from('a buffer example'))); + * // Prints: false + * console.log(buf.includes(Buffer.from('a buffer example').slice(0, 8))); + * // Prints: true + * console.log(buf.includes('this', 4)); + * // Prints: false + * ``` + * @since v5.3.0 + * @param value What to search for. + * @param [byteOffset=0] Where to begin searching in `buf`. If negative, then offset is calculated from the end of `buf`. + * @param [encoding='utf8'] If `value` is a string, this is its encoding. + * @return `true` if `value` was found in `buf`, `false` otherwise. + */ + includes( + value: string | number | Buffer, + byteOffset?: number, + encoding?: Encoding, + ): boolean; + /** + * Creates and returns an [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) of `buf` keys (indices). + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('buffer'); + * + * for (const key of buf.keys()) { + * console.log(key); + * } + * // Prints: + * // 0 + * // 1 + * // 2 + * // 3 + * // 4 + * // 5 + * ``` + * @since v1.1.0 + */ + keys(): IterableIterator; + /** + * Creates and returns an [iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols) for `buf` values (bytes). This function is + * called automatically when a `Buffer` is used in a `for..of` statement. + * + * ```js + * import { Buffer } from "internal:deno_node/polyfills/internal/buffer"; + * + * const buf = Buffer.from('buffer'); + * + * for (const value of buf.values()) { + * console.log(value); + * } + * // Prints: + * // 98 + * // 117 + * // 102 + * // 102 + * // 101 + * // 114 + * + * for (const value of buf) { + * console.log(value); + * } + * // Prints: + * // 98 + * // 117 + * // 102 + * // 102 + * // 101 + * // 114 + * ``` + * @since v1.1.0 + */ + values(): IterableIterator; +} + +export const SlowBuffer: { + /** @deprecated since v6.0.0, use `Buffer.allocUnsafeSlow()` */ + new (size: number): Buffer; + prototype: Buffer; +}; + +export const atob: typeof globalThis.atob; +export const Blob: Blob; +export const btoa: typeof globalThis.btoa; +export const constants: { + MAX_LENGTH: number; + MAX_STRING_LENGTH: number; +}; +export const kMaxLength: number; +export const kStringMaxLength: number; + +declare const exports: { + atob: typeof atob; + Blob: Blob; + btoa: typeof btoa; + Buffer: Buffer; + constants: typeof constants; + kMaxLength: typeof kMaxLength; + kStringMaxLength: typeof kStringMaxLength; + SlowBuffer: typeof SlowBuffer; +}; + +export default exports; diff --git a/ext/node/polyfills/internal/buffer.mjs b/ext/node/polyfills/internal/buffer.mjs new file mode 100644 index 0000000000..506f905079 --- /dev/null +++ b/ext/node/polyfills/internal/buffer.mjs @@ -0,0 +1,2607 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// Copyright Feross Aboukhadijeh, and other contributors. All rights reserved. MIT license. + +import { TextDecoder, TextEncoder } from "internal:deno_web/08_text_encoding.js"; +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; +import { encodings } from "internal:deno_node/polyfills/internal_binding/string_decoder.ts"; +import { indexOfBuffer, indexOfNumber } from "internal:deno_node/polyfills/internal_binding/buffer.ts"; +import { + asciiToBytes, + base64ToBytes, + base64UrlToBytes, + bytesToAscii, + bytesToUtf16le, + hexToBytes, + utf16leToBytes, +} from "internal:deno_node/polyfills/internal_binding/_utils.ts"; +import { isAnyArrayBuffer, isArrayBufferView } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { normalizeEncoding } from "internal:deno_node/polyfills/internal/util.mjs"; +import { validateBuffer } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isUint8Array } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { forgivingBase64Encode, forgivingBase64UrlEncode } from "internal:deno_web/00_infra.js"; + +const utf8Encoder = new TextEncoder(); + +// Temporary buffers to convert numbers. +const float32Array = new Float32Array(1); +const uInt8Float32Array = new Uint8Array(float32Array.buffer); +const float64Array = new Float64Array(1); +const uInt8Float64Array = new Uint8Array(float64Array.buffer); + +// Check endianness. +float32Array[0] = -1; // 0xBF800000 +// Either it is [0, 0, 128, 191] or [191, 128, 0, 0]. It is not possible to +// check this with `os.endianness()` because that is determined at compile time. +export const bigEndian = uInt8Float32Array[3] === 0; + +export const kMaxLength = 2147483647; +export const kStringMaxLength = 536870888; +const MAX_UINT32 = 2 ** 32; + +const customInspectSymbol = + typeof Symbol === "function" && typeof Symbol["for"] === "function" + ? Symbol["for"]("nodejs.util.inspect.custom") + : null; + +const INSPECT_MAX_BYTES = 50; + +export const constants = { + MAX_LENGTH: kMaxLength, + MAX_STRING_LENGTH: kStringMaxLength, +}; + +Object.defineProperty(Buffer.prototype, "parent", { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) { + return void 0; + } + return this.buffer; + }, +}); + +Object.defineProperty(Buffer.prototype, "offset", { + enumerable: true, + get: function () { + if (!Buffer.isBuffer(this)) { + return void 0; + } + return this.byteOffset; + }, +}); + +function createBuffer(length) { + if (length > kMaxLength) { + throw new RangeError( + 'The value "' + length + '" is invalid for option "size"', + ); + } + const buf = new Uint8Array(length); + Object.setPrototypeOf(buf, Buffer.prototype); + return buf; +} + +export function Buffer(arg, encodingOrOffset, length) { + if (typeof arg === "number") { + if (typeof encodingOrOffset === "string") { + throw new codes.ERR_INVALID_ARG_TYPE( + "string", + "string", + arg, + ); + } + return _allocUnsafe(arg); + } + return _from(arg, encodingOrOffset, length); +} + +Buffer.poolSize = 8192; + +function _from(value, encodingOrOffset, length) { + if (typeof value === "string") { + return fromString(value, encodingOrOffset); + } + + if (typeof value === "object" && value !== null) { + if (isAnyArrayBuffer(value)) { + return fromArrayBuffer(value, encodingOrOffset, length); + } + + const valueOf = value.valueOf && value.valueOf(); + if ( + valueOf != null && + valueOf !== value && + (typeof valueOf === "string" || typeof valueOf === "object") + ) { + return _from(valueOf, encodingOrOffset, length); + } + + const b = fromObject(value); + if (b) { + return b; + } + + if (typeof value[Symbol.toPrimitive] === "function") { + const primitive = value[Symbol.toPrimitive]("string"); + if (typeof primitive === "string") { + return fromString(primitive, encodingOrOffset); + } + } + } + + throw new codes.ERR_INVALID_ARG_TYPE( + "first argument", + ["string", "Buffer", "ArrayBuffer", "Array", "Array-like Object"], + value, + ); +} + +Buffer.from = function from(value, encodingOrOffset, length) { + return _from(value, encodingOrOffset, length); +}; + +Object.setPrototypeOf(Buffer.prototype, Uint8Array.prototype); + +Object.setPrototypeOf(Buffer, Uint8Array); + +function assertSize(size) { + validateNumber(size, "size"); + if (!(size >= 0 && size <= kMaxLength)) { + throw new codes.ERR_INVALID_ARG_VALUE.RangeError("size", size); + } +} + +function _alloc(size, fill, encoding) { + assertSize(size); + + const buffer = createBuffer(size); + if (fill !== undefined) { + if (encoding !== undefined && typeof encoding !== "string") { + throw new codes.ERR_INVALID_ARG_TYPE( + "encoding", + "string", + encoding, + ); + } + return buffer.fill(fill, encoding); + } + return buffer; +} + +Buffer.alloc = function alloc(size, fill, encoding) { + return _alloc(size, fill, encoding); +}; + +function _allocUnsafe(size) { + assertSize(size); + return createBuffer(size < 0 ? 0 : checked(size) | 0); +} + +Buffer.allocUnsafe = function allocUnsafe(size) { + return _allocUnsafe(size); +}; + +Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) { + return _allocUnsafe(size); +}; + +function fromString(string, encoding) { + if (typeof encoding !== "string" || encoding === "") { + encoding = "utf8"; + } + if (!Buffer.isEncoding(encoding)) { + throw new codes.ERR_UNKNOWN_ENCODING(encoding); + } + const length = byteLength(string, encoding) | 0; + let buf = createBuffer(length); + const actual = buf.write(string, encoding); + if (actual !== length) { + buf = buf.slice(0, actual); + } + return buf; +} + +function fromArrayLike(array) { + const length = array.length < 0 ? 0 : checked(array.length) | 0; + const buf = createBuffer(length); + for (let i = 0; i < length; i += 1) { + buf[i] = array[i] & 255; + } + return buf; +} + +function fromObject(obj) { + if (obj.length !== undefined || isAnyArrayBuffer(obj.buffer)) { + if (typeof obj.length !== "number") { + return createBuffer(0); + } + return fromArrayLike(obj); + } + + if (obj.type === "Buffer" && Array.isArray(obj.data)) { + return fromArrayLike(obj.data); + } +} + +function checked(length) { + if (length >= kMaxLength) { + throw new RangeError( + "Attempt to allocate Buffer larger than maximum size: 0x" + + kMaxLength.toString(16) + " bytes", + ); + } + return length | 0; +} + +export function SlowBuffer(length) { + assertSize(length); + return Buffer.alloc(+length); +} + +Object.setPrototypeOf(SlowBuffer.prototype, Uint8Array.prototype); + +Object.setPrototypeOf(SlowBuffer, Uint8Array); + +Buffer.isBuffer = function isBuffer(b) { + return b != null && b._isBuffer === true && b !== Buffer.prototype; +}; + +Buffer.compare = function compare(a, b) { + if (isInstance(a, Uint8Array)) { + a = Buffer.from(a, a.offset, a.byteLength); + } + if (isInstance(b, Uint8Array)) { + b = Buffer.from(b, b.offset, b.byteLength); + } + if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { + throw new TypeError( + 'The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array', + ); + } + if (a === b) { + return 0; + } + let x = a.length; + let y = b.length; + for (let i = 0, len = Math.min(x, y); i < len; ++i) { + if (a[i] !== b[i]) { + x = a[i]; + y = b[i]; + break; + } + } + if (x < y) { + return -1; + } + if (y < x) { + return 1; + } + return 0; +}; + +Buffer.isEncoding = function isEncoding(encoding) { + return typeof encoding === "string" && encoding.length !== 0 && + normalizeEncoding(encoding) !== undefined; +}; + +Buffer.concat = function concat(list, length) { + if (!Array.isArray(list)) { + throw new codes.ERR_INVALID_ARG_TYPE("list", "Array", list); + } + + if (list.length === 0) { + return Buffer.alloc(0); + } + + if (length === undefined) { + length = 0; + for (let i = 0; i < list.length; i++) { + if (list[i].length) { + length += list[i].length; + } + } + } else { + validateOffset(length, "length"); + } + + const buffer = Buffer.allocUnsafe(length); + let pos = 0; + for (let i = 0; i < list.length; i++) { + const buf = list[i]; + if (!isUint8Array(buf)) { + // TODO(BridgeAR): This should not be of type ERR_INVALID_ARG_TYPE. + // Instead, find the proper error code for this. + throw new codes.ERR_INVALID_ARG_TYPE( + `list[${i}]`, + ["Buffer", "Uint8Array"], + list[i], + ); + } + pos += _copyActual(buf, buffer, pos, 0, buf.length); + } + + // Note: `length` is always equal to `buffer.length` at this point + if (pos < length) { + // Zero-fill the remaining bytes if the specified `length` was more than + // the actual total length, i.e. if we have some remaining allocated bytes + // there were not initialized. + buffer.fill(0, pos, length); + } + + return buffer; +}; + +function byteLength(string, encoding) { + if (typeof string !== "string") { + if (isArrayBufferView(string) || isAnyArrayBuffer(string)) { + return string.byteLength; + } + + throw new codes.ERR_INVALID_ARG_TYPE( + "string", + ["string", "Buffer", "ArrayBuffer"], + string, + ); + } + + const len = string.length; + const mustMatch = arguments.length > 2 && arguments[2] === true; + if (!mustMatch && len === 0) { + return 0; + } + + if (!encoding) { + return (mustMatch ? -1 : byteLengthUtf8(string)); + } + + const ops = getEncodingOps(encoding); + if (ops === undefined) { + return (mustMatch ? -1 : byteLengthUtf8(string)); + } + return ops.byteLength(string); +} + +Buffer.byteLength = byteLength; + +Buffer.prototype._isBuffer = true; + +function swap(b, n, m) { + const i = b[n]; + b[n] = b[m]; + b[m] = i; +} + +Buffer.prototype.swap16 = function swap16() { + const len = this.length; + if (len % 2 !== 0) { + throw new RangeError("Buffer size must be a multiple of 16-bits"); + } + for (let i = 0; i < len; i += 2) { + swap(this, i, i + 1); + } + return this; +}; + +Buffer.prototype.swap32 = function swap32() { + const len = this.length; + if (len % 4 !== 0) { + throw new RangeError("Buffer size must be a multiple of 32-bits"); + } + for (let i = 0; i < len; i += 4) { + swap(this, i, i + 3); + swap(this, i + 1, i + 2); + } + return this; +}; + +Buffer.prototype.swap64 = function swap64() { + const len = this.length; + if (len % 8 !== 0) { + throw new RangeError("Buffer size must be a multiple of 64-bits"); + } + for (let i = 0; i < len; i += 8) { + swap(this, i, i + 7); + swap(this, i + 1, i + 6); + swap(this, i + 2, i + 5); + swap(this, i + 3, i + 4); + } + return this; +}; + +Buffer.prototype.toString = function toString(encoding, start, end) { + if (arguments.length === 0) { + return this.utf8Slice(0, this.length); + } + + const len = this.length; + + if (start <= 0) { + start = 0; + } else if (start >= len) { + return ""; + } else { + start |= 0; + } + + if (end === undefined || end > len) { + end = len; + } else { + end |= 0; + } + + if (end <= start) { + return ""; + } + + if (encoding === undefined) { + return this.utf8Slice(start, end); + } + + const ops = getEncodingOps(encoding); + if (ops === undefined) { + throw new codes.ERR_UNKNOWN_ENCODING(encoding); + } + + return ops.slice(this, start, end); +}; + +Buffer.prototype.toLocaleString = Buffer.prototype.toString; + +Buffer.prototype.equals = function equals(b) { + if (!isUint8Array(b)) { + throw new codes.ERR_INVALID_ARG_TYPE( + "otherBuffer", + ["Buffer", "Uint8Array"], + b, + ); + } + if (this === b) { + return true; + } + return Buffer.compare(this, b) === 0; +}; + +Buffer.prototype.inspect = function inspect() { + let str = ""; + const max = INSPECT_MAX_BYTES; + str = this.toString("hex", 0, max).replace(/(.{2})/g, "$1 ").trim(); + if (this.length > max) { + str += " ... "; + } + return ""; +}; + +if (customInspectSymbol) { + Buffer.prototype[customInspectSymbol] = Buffer.prototype.inspect; +} + +Buffer.prototype.compare = function compare( + target, + start, + end, + thisStart, + thisEnd, +) { + if (isInstance(target, Uint8Array)) { + target = Buffer.from(target, target.offset, target.byteLength); + } + if (!Buffer.isBuffer(target)) { + throw new codes.ERR_INVALID_ARG_TYPE( + "target", + ["Buffer", "Uint8Array"], + target, + ); + } + + if (start === undefined) { + start = 0; + } else { + validateOffset(start, "targetStart", 0, kMaxLength); + } + + if (end === undefined) { + end = target.length; + } else { + validateOffset(end, "targetEnd", 0, target.length); + } + + if (thisStart === undefined) { + thisStart = 0; + } else { + validateOffset(start, "sourceStart", 0, kMaxLength); + } + + if (thisEnd === undefined) { + thisEnd = this.length; + } else { + validateOffset(end, "sourceEnd", 0, this.length); + } + + if ( + start < 0 || end > target.length || thisStart < 0 || + thisEnd > this.length + ) { + throw new codes.ERR_OUT_OF_RANGE("out of range index", "range"); + } + + if (thisStart >= thisEnd && start >= end) { + return 0; + } + if (thisStart >= thisEnd) { + return -1; + } + if (start >= end) { + return 1; + } + start >>>= 0; + end >>>= 0; + thisStart >>>= 0; + thisEnd >>>= 0; + if (this === target) { + return 0; + } + let x = thisEnd - thisStart; + let y = end - start; + const len = Math.min(x, y); + const thisCopy = this.slice(thisStart, thisEnd); + const targetCopy = target.slice(start, end); + for (let i = 0; i < len; ++i) { + if (thisCopy[i] !== targetCopy[i]) { + x = thisCopy[i]; + y = targetCopy[i]; + break; + } + } + if (x < y) { + return -1; + } + if (y < x) { + return 1; + } + return 0; +}; + +function bidirectionalIndexOf(buffer, val, byteOffset, encoding, dir) { + validateBuffer(buffer); + + if (typeof byteOffset === "string") { + encoding = byteOffset; + byteOffset = undefined; + } else if (byteOffset > 0x7fffffff) { + byteOffset = 0x7fffffff; + } else if (byteOffset < -0x80000000) { + byteOffset = -0x80000000; + } + byteOffset = +byteOffset; + if (Number.isNaN(byteOffset)) { + byteOffset = dir ? 0 : (buffer.length || buffer.byteLength); + } + dir = !!dir; + + if (typeof val === "number") { + return indexOfNumber(buffer, val >>> 0, byteOffset, dir); + } + + let ops; + if (encoding === undefined) { + ops = encodingOps.utf8; + } else { + ops = getEncodingOps(encoding); + } + + if (typeof val === "string") { + if (ops === undefined) { + throw new codes.ERR_UNKNOWN_ENCODING(encoding); + } + return ops.indexOf(buffer, val, byteOffset, dir); + } + + if (isUint8Array(val)) { + const encodingVal = ops === undefined ? encodingsMap.utf8 : ops.encodingVal; + return indexOfBuffer(buffer, val, byteOffset, encodingVal, dir); + } + + throw new codes.ERR_INVALID_ARG_TYPE( + "value", + ["number", "string", "Buffer", "Uint8Array"], + val, + ); +} + +Buffer.prototype.includes = function includes(val, byteOffset, encoding) { + return this.indexOf(val, byteOffset, encoding) !== -1; +}; + +Buffer.prototype.indexOf = function indexOf(val, byteOffset, encoding) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, true); +}; + +Buffer.prototype.lastIndexOf = function lastIndexOf( + val, + byteOffset, + encoding, +) { + return bidirectionalIndexOf(this, val, byteOffset, encoding, false); +}; + +Buffer.prototype.asciiSlice = function asciiSlice(offset, length) { + if (offset === 0 && length === this.length) { + return bytesToAscii(this); + } else { + return bytesToAscii(this.slice(offset, length)); + } +}; + +Buffer.prototype.asciiWrite = function asciiWrite(string, offset, length) { + return blitBuffer(asciiToBytes(string), this, offset, length); +}; + +Buffer.prototype.base64Slice = function base64Slice( + offset, + length, +) { + if (offset === 0 && length === this.length) { + return forgivingBase64Encode(this); + } else { + return forgivingBase64Encode(this.slice(offset, length)); + } +}; + +Buffer.prototype.base64Write = function base64Write( + string, + offset, + length, +) { + return blitBuffer(base64ToBytes(string), this, offset, length); +}; + +Buffer.prototype.base64urlSlice = function base64urlSlice( + offset, + length, +) { + if (offset === 0 && length === this.length) { + return forgivingBase64UrlEncode(this); + } else { + return forgivingBase64UrlEncode(this.slice(offset, length)); + } +}; + +Buffer.prototype.base64urlWrite = function base64urlWrite( + string, + offset, + length, +) { + return blitBuffer(base64UrlToBytes(string), this, offset, length); +}; + +Buffer.prototype.hexWrite = function hexWrite(string, offset, length) { + return blitBuffer( + hexToBytes(string, this.length - offset), + this, + offset, + length, + ); +}; + +Buffer.prototype.hexSlice = function hexSlice(string, offset, length) { + return _hexSlice(this, string, offset, length); +}; + +Buffer.prototype.latin1Slice = function latin1Slice( + string, + offset, + length, +) { + return _latin1Slice(this, string, offset, length); +}; + +Buffer.prototype.latin1Write = function latin1Write( + string, + offset, + length, +) { + return blitBuffer(asciiToBytes(string), this, offset, length); +}; + +Buffer.prototype.ucs2Slice = function ucs2Slice(offset, length) { + if (offset === 0 && length === this.length) { + return bytesToUtf16le(this); + } else { + return bytesToUtf16le(this.slice(offset, length)); + } +}; + +Buffer.prototype.ucs2Write = function ucs2Write(string, offset, length) { + return blitBuffer( + utf16leToBytes(string, this.length - offset), + this, + offset, + length, + ); +}; + +Buffer.prototype.utf8Slice = function utf8Slice(string, offset, length) { + return _utf8Slice(this, string, offset, length); +}; + +Buffer.prototype.utf8Write = function utf8Write(string, offset, length) { + return blitBuffer( + utf8ToBytes(string, this.length - offset), + this, + offset, + length, + ); +}; + +Buffer.prototype.write = function write(string, offset, length, encoding) { + // Buffer#write(string); + if (offset === undefined) { + return this.utf8Write(string, 0, this.length); + } + // Buffer#write(string, encoding) + if (length === undefined && typeof offset === "string") { + encoding = offset; + length = this.length; + offset = 0; + + // Buffer#write(string, offset[, length][, encoding]) + } else { + validateOffset(offset, "offset", 0, this.length); + + const remaining = this.length - offset; + + if (length === undefined) { + length = remaining; + } else if (typeof length === "string") { + encoding = length; + length = remaining; + } else { + validateOffset(length, "length", 0, this.length); + if (length > remaining) { + length = remaining; + } + } + } + + if (!encoding) { + return this.utf8Write(string, offset, length); + } + + const ops = getEncodingOps(encoding); + if (ops === undefined) { + throw new codes.ERR_UNKNOWN_ENCODING(encoding); + } + return ops.write(this, string, offset, length); +}; + +Buffer.prototype.toJSON = function toJSON() { + return { + type: "Buffer", + data: Array.prototype.slice.call(this._arr || this, 0), + }; +}; +function fromArrayBuffer(obj, byteOffset, length) { + // Convert byteOffset to integer + if (byteOffset === undefined) { + byteOffset = 0; + } else { + byteOffset = +byteOffset; + if (Number.isNaN(byteOffset)) { + byteOffset = 0; + } + } + + const maxLength = obj.byteLength - byteOffset; + + if (maxLength < 0) { + throw new codes.ERR_BUFFER_OUT_OF_BOUNDS("offset"); + } + + if (length === undefined) { + length = maxLength; + } else { + // Convert length to non-negative integer. + length = +length; + if (length > 0) { + if (length > maxLength) { + throw new codes.ERR_BUFFER_OUT_OF_BOUNDS("length"); + } + } else { + length = 0; + } + } + + const buffer = new Uint8Array(obj, byteOffset, length); + Object.setPrototypeOf(buffer, Buffer.prototype); + return buffer; +} + +function _base64Slice(buf, start, end) { + if (start === 0 && end === buf.length) { + return forgivingBase64Encode(buf); + } else { + return forgivingBase64Encode(buf.slice(start, end)); + } +} + +const decoder = new TextDecoder(); + +function _utf8Slice(buf, start, end) { + return decoder.decode(buf.slice(start, end)); +} + +function _latin1Slice(buf, start, end) { + let ret = ""; + end = Math.min(buf.length, end); + for (let i = start; i < end; ++i) { + ret += String.fromCharCode(buf[i]); + } + return ret; +} + +function _hexSlice(buf, start, end) { + const len = buf.length; + if (!start || start < 0) { + start = 0; + } + if (!end || end < 0 || end > len) { + end = len; + } + let out = ""; + for (let i = start; i < end; ++i) { + out += hexSliceLookupTable[buf[i]]; + } + return out; +} + +Buffer.prototype.slice = function slice(start, end) { + const len = this.length; + start = ~~start; + end = end === void 0 ? len : ~~end; + if (start < 0) { + start += len; + if (start < 0) { + start = 0; + } + } else if (start > len) { + start = len; + } + if (end < 0) { + end += len; + if (end < 0) { + end = 0; + } + } else if (end > len) { + end = len; + } + if (end < start) { + end = start; + } + const newBuf = this.subarray(start, end); + Object.setPrototypeOf(newBuf, Buffer.prototype); + return newBuf; +}; + +Buffer.prototype.readUintLE = Buffer.prototype.readUIntLE = function readUIntLE( + offset, + byteLength, +) { + if (offset === undefined) { + throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + if (byteLength === 6) { + return readUInt48LE(this, offset); + } + if (byteLength === 5) { + return readUInt40LE(this, offset); + } + if (byteLength === 3) { + return readUInt24LE(this, offset); + } + if (byteLength === 4) { + return this.readUInt32LE(offset); + } + if (byteLength === 2) { + return this.readUInt16LE(offset); + } + if (byteLength === 1) { + return this.readUInt8(offset); + } + + boundsError(byteLength, 6, "byteLength"); +}; + +Buffer.prototype.readUintBE = Buffer.prototype.readUIntBE = function readUIntBE( + offset, + byteLength, +) { + if (offset === undefined) { + throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + if (byteLength === 6) { + return readUInt48BE(this, offset); + } + if (byteLength === 5) { + return readUInt40BE(this, offset); + } + if (byteLength === 3) { + return readUInt24BE(this, offset); + } + if (byteLength === 4) { + return this.readUInt32BE(offset); + } + if (byteLength === 2) { + return this.readUInt16BE(offset); + } + if (byteLength === 1) { + return this.readUInt8(offset); + } + + boundsError(byteLength, 6, "byteLength"); +}; + +Buffer.prototype.readUint8 = Buffer.prototype.readUInt8 = function readUInt8( + offset = 0, +) { + validateNumber(offset, "offset"); + const val = this[offset]; + if (val === undefined) { + boundsError(offset, this.length - 1); + } + + return val; +}; + +Buffer.prototype.readUint16BE = Buffer.prototype.readUInt16BE = readUInt16BE; + +Buffer.prototype.readUint16LE = + Buffer.prototype.readUInt16LE = + function readUInt16LE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + return first + last * 2 ** 8; + }; + +Buffer.prototype.readUint32LE = + Buffer.prototype.readUInt32LE = + function readUInt32LE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return first + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + last * 2 ** 24; + }; + +Buffer.prototype.readUint32BE = Buffer.prototype.readUInt32BE = readUInt32BE; + +Buffer.prototype.readBigUint64LE = + Buffer.prototype.readBigUInt64LE = + defineBigIntMethod( + function readBigUInt64LE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const lo = first + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 24; + const hi = this[++offset] + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + last * 2 ** 24; + return BigInt(lo) + (BigInt(hi) << BigInt(32)); + }, + ); + +Buffer.prototype.readBigUint64BE = + Buffer.prototype.readBigUInt64BE = + defineBigIntMethod( + function readBigUInt64BE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const hi = first * 2 ** 24 + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + this[++offset]; + const lo = this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + last; + return (BigInt(hi) << BigInt(32)) + BigInt(lo); + }, + ); + +Buffer.prototype.readIntLE = function readIntLE( + offset, + byteLength, +) { + if (offset === undefined) { + throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + if (byteLength === 6) { + return readInt48LE(this, offset); + } + if (byteLength === 5) { + return readInt40LE(this, offset); + } + if (byteLength === 3) { + return readInt24LE(this, offset); + } + if (byteLength === 4) { + return this.readInt32LE(offset); + } + if (byteLength === 2) { + return this.readInt16LE(offset); + } + if (byteLength === 1) { + return this.readInt8(offset); + } + + boundsError(byteLength, 6, "byteLength"); +}; + +Buffer.prototype.readIntBE = function readIntBE(offset, byteLength) { + if (offset === undefined) { + throw new codes.ERR_INVALID_ARG_TYPE("offset", "number", offset); + } + if (byteLength === 6) { + return readInt48BE(this, offset); + } + if (byteLength === 5) { + return readInt40BE(this, offset); + } + if (byteLength === 3) { + return readInt24BE(this, offset); + } + if (byteLength === 4) { + return this.readInt32BE(offset); + } + if (byteLength === 2) { + return this.readInt16BE(offset); + } + if (byteLength === 1) { + return this.readInt8(offset); + } + + boundsError(byteLength, 6, "byteLength"); +}; + +Buffer.prototype.readInt8 = function readInt8(offset = 0) { + validateNumber(offset, "offset"); + const val = this[offset]; + if (val === undefined) { + boundsError(offset, this.length - 1); + } + + return val | (val & 2 ** 7) * 0x1fffffe; +}; + +Buffer.prototype.readInt16LE = function readInt16LE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + const val = first + last * 2 ** 8; + return val | (val & 2 ** 15) * 0x1fffe; +}; + +Buffer.prototype.readInt16BE = function readInt16BE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + const val = first * 2 ** 8 + last; + return val | (val & 2 ** 15) * 0x1fffe; +}; + +Buffer.prototype.readInt32LE = function readInt32LE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return first + + this[++offset] * 2 ** 8 + + this[++offset] * 2 ** 16 + + (last << 24); // Overflow +}; + +Buffer.prototype.readInt32BE = function readInt32BE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return (first << 24) + // Overflow + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + last; +}; + +Buffer.prototype.readBigInt64LE = defineBigIntMethod( + function readBigInt64LE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const val = this[offset + 4] + this[offset + 5] * 2 ** 8 + + this[offset + 6] * 2 ** 16 + (last << 24); + return (BigInt(val) << BigInt(32)) + + BigInt( + first + this[++offset] * 2 ** 8 + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 24, + ); + }, +); + +Buffer.prototype.readBigInt64BE = defineBigIntMethod( + function readBigInt64BE(offset) { + offset = offset >>> 0; + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 7]; + if (first === void 0 || last === void 0) { + boundsError(offset, this.length - 8); + } + const val = (first << 24) + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + this[++offset]; + return (BigInt(val) << BigInt(32)) + + BigInt( + this[++offset] * 2 ** 24 + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + last, + ); + }, +); + +Buffer.prototype.readFloatLE = function readFloatLE(offset) { + return bigEndian + ? readFloatBackwards(this, offset) + : readFloatForwards(this, offset); +}; + +Buffer.prototype.readFloatBE = function readFloatBE(offset) { + return bigEndian + ? readFloatForwards(this, offset) + : readFloatBackwards(this, offset); +}; + +Buffer.prototype.readDoubleLE = function readDoubleLE(offset) { + return bigEndian + ? readDoubleBackwards(this, offset) + : readDoubleForwards(this, offset); +}; + +Buffer.prototype.readDoubleBE = function readDoubleBE(offset) { + return bigEndian + ? readDoubleForwards(this, offset) + : readDoubleBackwards(this, offset); +}; + +Buffer.prototype.writeUintLE = + Buffer.prototype.writeUIntLE = + function writeUIntLE(value, offset, byteLength) { + if (byteLength === 6) { + return writeU_Int48LE(this, value, offset, 0, 0xffffffffffff); + } + if (byteLength === 5) { + return writeU_Int40LE(this, value, offset, 0, 0xffffffffff); + } + if (byteLength === 3) { + return writeU_Int24LE(this, value, offset, 0, 0xffffff); + } + if (byteLength === 4) { + return writeU_Int32LE(this, value, offset, 0, 0xffffffff); + } + if (byteLength === 2) { + return writeU_Int16LE(this, value, offset, 0, 0xffff); + } + if (byteLength === 1) { + return writeU_Int8(this, value, offset, 0, 0xff); + } + + boundsError(byteLength, 6, "byteLength"); + }; + +Buffer.prototype.writeUintBE = + Buffer.prototype.writeUIntBE = + function writeUIntBE(value, offset, byteLength) { + if (byteLength === 6) { + return writeU_Int48BE(this, value, offset, 0, 0xffffffffffff); + } + if (byteLength === 5) { + return writeU_Int40BE(this, value, offset, 0, 0xffffffffff); + } + if (byteLength === 3) { + return writeU_Int24BE(this, value, offset, 0, 0xffffff); + } + if (byteLength === 4) { + return writeU_Int32BE(this, value, offset, 0, 0xffffffff); + } + if (byteLength === 2) { + return writeU_Int16BE(this, value, offset, 0, 0xffff); + } + if (byteLength === 1) { + return writeU_Int8(this, value, offset, 0, 0xff); + } + + boundsError(byteLength, 6, "byteLength"); + }; + +Buffer.prototype.writeUint8 = Buffer.prototype.writeUInt8 = function writeUInt8( + value, + offset = 0, +) { + return writeU_Int8(this, value, offset, 0, 0xff); +}; + +Buffer.prototype.writeUint16LE = + Buffer.prototype.writeUInt16LE = + function writeUInt16LE(value, offset = 0) { + return writeU_Int16LE(this, value, offset, 0, 0xffff); + }; + +Buffer.prototype.writeUint16BE = + Buffer.prototype.writeUInt16BE = + function writeUInt16BE(value, offset = 0) { + return writeU_Int16BE(this, value, offset, 0, 0xffff); + }; + +Buffer.prototype.writeUint32LE = + Buffer.prototype.writeUInt32LE = + function writeUInt32LE(value, offset = 0) { + return _writeUInt32LE(this, value, offset, 0, 0xffffffff); + }; + +Buffer.prototype.writeUint32BE = + Buffer.prototype.writeUInt32BE = + function writeUInt32BE(value, offset = 0) { + return _writeUInt32BE(this, value, offset, 0, 0xffffffff); + }; + +function wrtBigUInt64LE(buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7); + let lo = Number(value & BigInt(4294967295)); + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + lo = lo >> 8; + buf[offset++] = lo; + let hi = Number(value >> BigInt(32) & BigInt(4294967295)); + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + hi = hi >> 8; + buf[offset++] = hi; + return offset; +} + +function wrtBigUInt64BE(buf, value, offset, min, max) { + checkIntBI(value, min, max, buf, offset, 7); + let lo = Number(value & BigInt(4294967295)); + buf[offset + 7] = lo; + lo = lo >> 8; + buf[offset + 6] = lo; + lo = lo >> 8; + buf[offset + 5] = lo; + lo = lo >> 8; + buf[offset + 4] = lo; + let hi = Number(value >> BigInt(32) & BigInt(4294967295)); + buf[offset + 3] = hi; + hi = hi >> 8; + buf[offset + 2] = hi; + hi = hi >> 8; + buf[offset + 1] = hi; + hi = hi >> 8; + buf[offset] = hi; + return offset + 8; +} + +Buffer.prototype.writeBigUint64LE = + Buffer.prototype.writeBigUInt64LE = + defineBigIntMethod( + function writeBigUInt64LE(value, offset = 0) { + return wrtBigUInt64LE( + this, + value, + offset, + BigInt(0), + BigInt("0xffffffffffffffff"), + ); + }, + ); + +Buffer.prototype.writeBigUint64BE = + Buffer.prototype.writeBigUInt64BE = + defineBigIntMethod( + function writeBigUInt64BE(value, offset = 0) { + return wrtBigUInt64BE( + this, + value, + offset, + BigInt(0), + BigInt("0xffffffffffffffff"), + ); + }, + ); + +Buffer.prototype.writeIntLE = function writeIntLE( + value, + offset, + byteLength, +) { + if (byteLength === 6) { + return writeU_Int48LE( + this, + value, + offset, + -0x800000000000, + 0x7fffffffffff, + ); + } + if (byteLength === 5) { + return writeU_Int40LE(this, value, offset, -0x8000000000, 0x7fffffffff); + } + if (byteLength === 3) { + return writeU_Int24LE(this, value, offset, -0x800000, 0x7fffff); + } + if (byteLength === 4) { + return writeU_Int32LE(this, value, offset, -0x80000000, 0x7fffffff); + } + if (byteLength === 2) { + return writeU_Int16LE(this, value, offset, -0x8000, 0x7fff); + } + if (byteLength === 1) { + return writeU_Int8(this, value, offset, -0x80, 0x7f); + } + + boundsError(byteLength, 6, "byteLength"); +}; + +Buffer.prototype.writeIntBE = function writeIntBE( + value, + offset, + byteLength, +) { + if (byteLength === 6) { + return writeU_Int48BE( + this, + value, + offset, + -0x800000000000, + 0x7fffffffffff, + ); + } + if (byteLength === 5) { + return writeU_Int40BE(this, value, offset, -0x8000000000, 0x7fffffffff); + } + if (byteLength === 3) { + return writeU_Int24BE(this, value, offset, -0x800000, 0x7fffff); + } + if (byteLength === 4) { + return writeU_Int32BE(this, value, offset, -0x80000000, 0x7fffffff); + } + if (byteLength === 2) { + return writeU_Int16BE(this, value, offset, -0x8000, 0x7fff); + } + if (byteLength === 1) { + return writeU_Int8(this, value, offset, -0x80, 0x7f); + } + + boundsError(byteLength, 6, "byteLength"); +}; + +Buffer.prototype.writeInt8 = function writeInt8(value, offset = 0) { + return writeU_Int8(this, value, offset, -0x80, 0x7f); +}; + +Buffer.prototype.writeInt16LE = function writeInt16LE(value, offset = 0) { + return writeU_Int16LE(this, value, offset, -0x8000, 0x7fff); +}; + +Buffer.prototype.writeInt16BE = function writeInt16BE( + value, + offset = 0, +) { + return writeU_Int16BE(this, value, offset, -0x8000, 0x7fff); +}; + +Buffer.prototype.writeInt32LE = function writeInt32LE(value, offset = 0) { + return writeU_Int32LE(this, value, offset, -0x80000000, 0x7fffffff); +}; + +Buffer.prototype.writeInt32BE = function writeInt32BE(value, offset = 0) { + return writeU_Int32BE(this, value, offset, -0x80000000, 0x7fffffff); +}; + +Buffer.prototype.writeBigInt64LE = defineBigIntMethod( + function writeBigInt64LE(value, offset = 0) { + return wrtBigUInt64LE( + this, + value, + offset, + -BigInt("0x8000000000000000"), + BigInt("0x7fffffffffffffff"), + ); + }, +); + +Buffer.prototype.writeBigInt64BE = defineBigIntMethod( + function writeBigInt64BE(value, offset = 0) { + return wrtBigUInt64BE( + this, + value, + offset, + -BigInt("0x8000000000000000"), + BigInt("0x7fffffffffffffff"), + ); + }, +); + +Buffer.prototype.writeFloatLE = function writeFloatLE( + value, + offset, +) { + return bigEndian + ? writeFloatBackwards(this, value, offset) + : writeFloatForwards(this, value, offset); +}; + +Buffer.prototype.writeFloatBE = function writeFloatBE( + value, + offset, +) { + return bigEndian + ? writeFloatForwards(this, value, offset) + : writeFloatBackwards(this, value, offset); +}; + +Buffer.prototype.writeDoubleLE = function writeDoubleLE( + value, + offset, +) { + return bigEndian + ? writeDoubleBackwards(this, value, offset) + : writeDoubleForwards(this, value, offset); +}; + +Buffer.prototype.writeDoubleBE = function writeDoubleBE( + value, + offset, +) { + return bigEndian + ? writeDoubleForwards(this, value, offset) + : writeDoubleBackwards(this, value, offset); +}; + +Buffer.prototype.copy = function copy( + target, + targetStart, + sourceStart, + sourceEnd, +) { + if (!isUint8Array(this)) { + throw new codes.ERR_INVALID_ARG_TYPE( + "source", + ["Buffer", "Uint8Array"], + this, + ); + } + + if (!isUint8Array(target)) { + throw new codes.ERR_INVALID_ARG_TYPE( + "target", + ["Buffer", "Uint8Array"], + target, + ); + } + + if (targetStart === undefined) { + targetStart = 0; + } else { + targetStart = toInteger(targetStart, 0); + if (targetStart < 0) { + throw new codes.ERR_OUT_OF_RANGE("targetStart", ">= 0", targetStart); + } + } + + if (sourceStart === undefined) { + sourceStart = 0; + } else { + sourceStart = toInteger(sourceStart, 0); + if (sourceStart < 0) { + throw new codes.ERR_OUT_OF_RANGE("sourceStart", ">= 0", sourceStart); + } + if (sourceStart >= MAX_UINT32) { + throw new codes.ERR_OUT_OF_RANGE( + "sourceStart", + `< ${MAX_UINT32}`, + sourceStart, + ); + } + } + + if (sourceEnd === undefined) { + sourceEnd = this.length; + } else { + sourceEnd = toInteger(sourceEnd, 0); + if (sourceEnd < 0) { + throw new codes.ERR_OUT_OF_RANGE("sourceEnd", ">= 0", sourceEnd); + } + if (sourceEnd >= MAX_UINT32) { + throw new codes.ERR_OUT_OF_RANGE( + "sourceEnd", + `< ${MAX_UINT32}`, + sourceEnd, + ); + } + } + + if (targetStart >= target.length) { + return 0; + } + + if (sourceEnd > 0 && sourceEnd < sourceStart) { + sourceEnd = sourceStart; + } + if (sourceEnd === sourceStart) { + return 0; + } + if (target.length === 0 || this.length === 0) { + return 0; + } + + if (sourceEnd > this.length) { + sourceEnd = this.length; + } + + if (target.length - targetStart < sourceEnd - sourceStart) { + sourceEnd = target.length - targetStart + sourceStart; + } + + const len = sourceEnd - sourceStart; + if ( + this === target && typeof Uint8Array.prototype.copyWithin === "function" + ) { + this.copyWithin(targetStart, sourceStart, sourceEnd); + } else { + Uint8Array.prototype.set.call( + target, + this.subarray(sourceStart, sourceEnd), + targetStart, + ); + } + return len; +}; + +Buffer.prototype.fill = function fill(val, start, end, encoding) { + if (typeof val === "string") { + if (typeof start === "string") { + encoding = start; + start = 0; + end = this.length; + } else if (typeof end === "string") { + encoding = end; + end = this.length; + } + if (encoding !== void 0 && typeof encoding !== "string") { + throw new TypeError("encoding must be a string"); + } + if (typeof encoding === "string" && !Buffer.isEncoding(encoding)) { + throw new TypeError("Unknown encoding: " + encoding); + } + if (val.length === 1) { + const code = val.charCodeAt(0); + if (encoding === "utf8" && code < 128 || encoding === "latin1") { + val = code; + } + } + } else if (typeof val === "number") { + val = val & 255; + } else if (typeof val === "boolean") { + val = Number(val); + } + if (start < 0 || this.length < start || this.length < end) { + throw new RangeError("Out of range index"); + } + if (end <= start) { + return this; + } + start = start >>> 0; + end = end === void 0 ? this.length : end >>> 0; + if (!val) { + val = 0; + } + let i; + if (typeof val === "number") { + for (i = start; i < end; ++i) { + this[i] = val; + } + } else { + const bytes = Buffer.isBuffer(val) ? val : Buffer.from(val, encoding); + const len = bytes.length; + if (len === 0) { + throw new codes.ERR_INVALID_ARG_VALUE( + "value", + val, + ); + } + for (i = 0; i < end - start; ++i) { + this[i + start] = bytes[i % len]; + } + } + return this; +}; + +function checkBounds(buf, offset, byteLength2) { + validateNumber(offset, "offset"); + if (buf[offset] === void 0 || buf[offset + byteLength2] === void 0) { + boundsError(offset, buf.length - (byteLength2 + 1)); + } +} + +function checkIntBI(value, min, max, buf, offset, byteLength2) { + if (value > max || value < min) { + const n = typeof min === "bigint" ? "n" : ""; + let range; + if (byteLength2 > 3) { + if (min === 0 || min === BigInt(0)) { + range = `>= 0${n} and < 2${n} ** ${(byteLength2 + 1) * 8}${n}`; + } else { + range = `>= -(2${n} ** ${(byteLength2 + 1) * 8 - 1}${n}) and < 2 ** ${ + (byteLength2 + 1) * 8 - 1 + }${n}`; + } + } else { + range = `>= ${min}${n} and <= ${max}${n}`; + } + throw new codes.ERR_OUT_OF_RANGE("value", range, value); + } + checkBounds(buf, offset, byteLength2); +} + +function utf8ToBytes(string, units) { + units = units || Infinity; + let codePoint; + const length = string.length; + let leadSurrogate = null; + const bytes = []; + for (let i = 0; i < length; ++i) { + codePoint = string.charCodeAt(i); + if (codePoint > 55295 && codePoint < 57344) { + if (!leadSurrogate) { + if (codePoint > 56319) { + if ((units -= 3) > -1) { + bytes.push(239, 191, 189); + } + continue; + } else if (i + 1 === length) { + if ((units -= 3) > -1) { + bytes.push(239, 191, 189); + } + continue; + } + leadSurrogate = codePoint; + continue; + } + if (codePoint < 56320) { + if ((units -= 3) > -1) { + bytes.push(239, 191, 189); + } + leadSurrogate = codePoint; + continue; + } + codePoint = (leadSurrogate - 55296 << 10 | codePoint - 56320) + 65536; + } else if (leadSurrogate) { + if ((units -= 3) > -1) { + bytes.push(239, 191, 189); + } + } + leadSurrogate = null; + if (codePoint < 128) { + if ((units -= 1) < 0) { + break; + } + bytes.push(codePoint); + } else if (codePoint < 2048) { + if ((units -= 2) < 0) { + break; + } + bytes.push(codePoint >> 6 | 192, codePoint & 63 | 128); + } else if (codePoint < 65536) { + if ((units -= 3) < 0) { + break; + } + bytes.push( + codePoint >> 12 | 224, + codePoint >> 6 & 63 | 128, + codePoint & 63 | 128, + ); + } else if (codePoint < 1114112) { + if ((units -= 4) < 0) { + break; + } + bytes.push( + codePoint >> 18 | 240, + codePoint >> 12 & 63 | 128, + codePoint >> 6 & 63 | 128, + codePoint & 63 | 128, + ); + } else { + throw new Error("Invalid code point"); + } + } + return bytes; +} + +function blitBuffer(src, dst, offset, byteLength) { + let i; + const length = byteLength === undefined ? src.length : byteLength; + for (i = 0; i < length; ++i) { + if (i + offset >= dst.length || i >= src.length) { + break; + } + dst[i + offset] = src[i]; + } + return i; +} + +function isInstance(obj, type) { + return obj instanceof type || + obj != null && obj.constructor != null && + obj.constructor.name != null && obj.constructor.name === type.name; +} + +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 defineBigIntMethod(fn) { + return typeof BigInt === "undefined" ? BufferBigIntNotDefined : fn; +} + +function BufferBigIntNotDefined() { + throw new Error("BigInt not supported"); +} + +export const atob = globalThis.atob; +export const Blob = globalThis.Blob; +export const btoa = globalThis.btoa; + +export function readUInt48LE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + return first + + buf[++offset] * 2 ** 8 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 24 + + (buf[++offset] + last * 2 ** 8) * 2 ** 32; +} + +export function readUInt40LE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return first + + buf[++offset] * 2 ** 8 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 24 + + last * 2 ** 32; +} + +export function readUInt24LE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + return first + buf[++offset] * 2 ** 8 + last * 2 ** 16; +} + +export function readUInt48BE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + return (first * 2 ** 8 + buf[++offset]) * 2 ** 32 + + buf[++offset] * 2 ** 24 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 8 + + last; +} + +export function readUInt40BE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return first * 2 ** 32 + + buf[++offset] * 2 ** 24 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 8 + + last; +} + +export function readUInt24BE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + return first * 2 ** 16 + buf[++offset] * 2 ** 8 + last; +} + +export function readUInt16BE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 1]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 2); + } + + return first * 2 ** 8 + last; +} + +export function readUInt32BE(offset = 0) { + validateNumber(offset, "offset"); + const first = this[offset]; + const last = this[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, this.length - 4); + } + + return first * 2 ** 24 + + this[++offset] * 2 ** 16 + + this[++offset] * 2 ** 8 + + last; +} + +export function readDoubleBackwards(buffer, offset = 0) { + validateNumber(offset, "offset"); + const first = buffer[offset]; + const last = buffer[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 8); + } + + uInt8Float64Array[7] = first; + uInt8Float64Array[6] = buffer[++offset]; + uInt8Float64Array[5] = buffer[++offset]; + uInt8Float64Array[4] = buffer[++offset]; + uInt8Float64Array[3] = buffer[++offset]; + uInt8Float64Array[2] = buffer[++offset]; + uInt8Float64Array[1] = buffer[++offset]; + uInt8Float64Array[0] = last; + return float64Array[0]; +} + +export function readDoubleForwards(buffer, offset = 0) { + validateNumber(offset, "offset"); + const first = buffer[offset]; + const last = buffer[offset + 7]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 8); + } + + uInt8Float64Array[0] = first; + uInt8Float64Array[1] = buffer[++offset]; + uInt8Float64Array[2] = buffer[++offset]; + uInt8Float64Array[3] = buffer[++offset]; + uInt8Float64Array[4] = buffer[++offset]; + uInt8Float64Array[5] = buffer[++offset]; + uInt8Float64Array[6] = buffer[++offset]; + uInt8Float64Array[7] = last; + return float64Array[0]; +} + +export function writeDoubleForwards(buffer, val, offset = 0) { + val = +val; + checkBounds(buffer, offset, 7); + + float64Array[0] = val; + buffer[offset++] = uInt8Float64Array[0]; + buffer[offset++] = uInt8Float64Array[1]; + buffer[offset++] = uInt8Float64Array[2]; + buffer[offset++] = uInt8Float64Array[3]; + buffer[offset++] = uInt8Float64Array[4]; + buffer[offset++] = uInt8Float64Array[5]; + buffer[offset++] = uInt8Float64Array[6]; + buffer[offset++] = uInt8Float64Array[7]; + return offset; +} + +export function writeDoubleBackwards(buffer, val, offset = 0) { + val = +val; + checkBounds(buffer, offset, 7); + + float64Array[0] = val; + buffer[offset++] = uInt8Float64Array[7]; + buffer[offset++] = uInt8Float64Array[6]; + buffer[offset++] = uInt8Float64Array[5]; + buffer[offset++] = uInt8Float64Array[4]; + buffer[offset++] = uInt8Float64Array[3]; + buffer[offset++] = uInt8Float64Array[2]; + buffer[offset++] = uInt8Float64Array[1]; + buffer[offset++] = uInt8Float64Array[0]; + return offset; +} + +export function readFloatBackwards(buffer, offset = 0) { + validateNumber(offset, "offset"); + const first = buffer[offset]; + const last = buffer[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 4); + } + + uInt8Float32Array[3] = first; + uInt8Float32Array[2] = buffer[++offset]; + uInt8Float32Array[1] = buffer[++offset]; + uInt8Float32Array[0] = last; + return float32Array[0]; +} + +export function readFloatForwards(buffer, offset = 0) { + validateNumber(offset, "offset"); + const first = buffer[offset]; + const last = buffer[offset + 3]; + if (first === undefined || last === undefined) { + boundsError(offset, buffer.length - 4); + } + + uInt8Float32Array[0] = first; + uInt8Float32Array[1] = buffer[++offset]; + uInt8Float32Array[2] = buffer[++offset]; + uInt8Float32Array[3] = last; + return float32Array[0]; +} + +export function writeFloatForwards(buffer, val, offset = 0) { + val = +val; + checkBounds(buffer, offset, 3); + + float32Array[0] = val; + buffer[offset++] = uInt8Float32Array[0]; + buffer[offset++] = uInt8Float32Array[1]; + buffer[offset++] = uInt8Float32Array[2]; + buffer[offset++] = uInt8Float32Array[3]; + return offset; +} + +export function writeFloatBackwards(buffer, val, offset = 0) { + val = +val; + checkBounds(buffer, offset, 3); + + float32Array[0] = val; + buffer[offset++] = uInt8Float32Array[3]; + buffer[offset++] = uInt8Float32Array[2]; + buffer[offset++] = uInt8Float32Array[1]; + buffer[offset++] = uInt8Float32Array[0]; + return offset; +} + +export function readInt24LE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + const val = first + buf[++offset] * 2 ** 8 + last * 2 ** 16; + return val | (val & 2 ** 23) * 0x1fe; +} + +export function readInt40LE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + + first + + buf[++offset] * 2 ** 8 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 24; +} + +export function readInt48LE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + const val = buf[offset + 4] + last * 2 ** 8; + return (val | (val & 2 ** 15) * 0x1fffe) * 2 ** 32 + + first + + buf[++offset] * 2 ** 8 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 24; +} + +export function readInt24BE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 2]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 3); + } + + const val = first * 2 ** 16 + buf[++offset] * 2 ** 8 + last; + return val | (val & 2 ** 23) * 0x1fe; +} + +export function readInt48BE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 5]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 6); + } + + const val = buf[++offset] + first * 2 ** 8; + return (val | (val & 2 ** 15) * 0x1fffe) * 2 ** 32 + + buf[++offset] * 2 ** 24 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 8 + + last; +} + +export function readInt40BE(buf, offset = 0) { + validateNumber(offset, "offset"); + const first = buf[offset]; + const last = buf[offset + 4]; + if (first === undefined || last === undefined) { + boundsError(offset, buf.length - 5); + } + + return (first | (first & 2 ** 7) * 0x1fffffe) * 2 ** 32 + + buf[++offset] * 2 ** 24 + + buf[++offset] * 2 ** 16 + + buf[++offset] * 2 ** 8 + + last; +} + +export function byteLengthUtf8(str) { + return utf8Encoder.encode(str).length; +} + +function base64ByteLength(str, bytes) { + // Handle padding + if (str.charCodeAt(bytes - 1) === 0x3D) { + bytes--; + } + if (bytes > 1 && str.charCodeAt(bytes - 1) === 0x3D) { + bytes--; + } + + // Base64 ratio: 3/4 + return (bytes * 3) >>> 2; +} + +export const encodingsMap = Object.create(null); +for (let i = 0; i < encodings.length; ++i) { + encodingsMap[encodings[i]] = i; +} + +export const encodingOps = { + ascii: { + byteLength: (string) => string.length, + encoding: "ascii", + encodingVal: encodingsMap.ascii, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + asciiToBytes(val), + byteOffset, + encodingsMap.ascii, + dir, + ), + slice: (buf, start, end) => buf.asciiSlice(start, end), + write: (buf, string, offset, len) => buf.asciiWrite(string, offset, len), + }, + base64: { + byteLength: (string) => base64ByteLength(string, string.length), + encoding: "base64", + encodingVal: encodingsMap.base64, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + base64ToBytes(val), + byteOffset, + encodingsMap.base64, + dir, + ), + slice: (buf, start, end) => buf.base64Slice(start, end), + write: (buf, string, offset, len) => buf.base64Write(string, offset, len), + }, + base64url: { + byteLength: (string) => base64ByteLength(string, string.length), + encoding: "base64url", + encodingVal: encodingsMap.base64url, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + base64UrlToBytes(val), + byteOffset, + encodingsMap.base64url, + dir, + ), + slice: (buf, start, end) => buf.base64urlSlice(start, end), + write: (buf, string, offset, len) => + buf.base64urlWrite(string, offset, len), + }, + hex: { + byteLength: (string) => string.length >>> 1, + encoding: "hex", + encodingVal: encodingsMap.hex, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + hexToBytes(val), + byteOffset, + encodingsMap.hex, + dir, + ), + slice: (buf, start, end) => buf.hexSlice(start, end), + write: (buf, string, offset, len) => buf.hexWrite(string, offset, len), + }, + latin1: { + byteLength: (string) => string.length, + encoding: "latin1", + encodingVal: encodingsMap.latin1, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + asciiToBytes(val), + byteOffset, + encodingsMap.latin1, + dir, + ), + slice: (buf, start, end) => buf.latin1Slice(start, end), + write: (buf, string, offset, len) => buf.latin1Write(string, offset, len), + }, + ucs2: { + byteLength: (string) => string.length * 2, + encoding: "ucs2", + encodingVal: encodingsMap.utf16le, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + utf16leToBytes(val), + byteOffset, + encodingsMap.utf16le, + dir, + ), + slice: (buf, start, end) => buf.ucs2Slice(start, end), + write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len), + }, + utf8: { + byteLength: byteLengthUtf8, + encoding: "utf8", + encodingVal: encodingsMap.utf8, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + utf8Encoder.encode(val), + byteOffset, + encodingsMap.utf8, + dir, + ), + slice: (buf, start, end) => buf.utf8Slice(start, end), + write: (buf, string, offset, len) => buf.utf8Write(string, offset, len), + }, + utf16le: { + byteLength: (string) => string.length * 2, + encoding: "utf16le", + encodingVal: encodingsMap.utf16le, + indexOf: (buf, val, byteOffset, dir) => + indexOfBuffer( + buf, + utf16leToBytes(val), + byteOffset, + encodingsMap.utf16le, + dir, + ), + slice: (buf, start, end) => buf.ucs2Slice(start, end), + write: (buf, string, offset, len) => buf.ucs2Write(string, offset, len), + }, +}; + +export function getEncodingOps(encoding) { + encoding = String(encoding).toLowerCase(); + switch (encoding.length) { + case 4: + if (encoding === "utf8") return encodingOps.utf8; + if (encoding === "ucs2") return encodingOps.ucs2; + break; + case 5: + if (encoding === "utf-8") return encodingOps.utf8; + if (encoding === "ascii") return encodingOps.ascii; + if (encoding === "ucs-2") return encodingOps.ucs2; + break; + case 7: + if (encoding === "utf16le") { + return encodingOps.utf16le; + } + break; + case 8: + if (encoding === "utf-16le") { + return encodingOps.utf16le; + } + break; + // deno-lint-ignore no-fallthrough + case 6: + if (encoding === "latin1" || encoding === "binary") { + return encodingOps.latin1; + } + if (encoding === "base64") return encodingOps.base64; + case 3: + if (encoding === "hex") { + return encodingOps.hex; + } + break; + case 9: + if (encoding === "base64url") { + return encodingOps.base64url; + } + break; + } +} + +export function _copyActual( + source, + target, + targetStart, + sourceStart, + sourceEnd, +) { + if (sourceEnd - sourceStart > target.length - targetStart) { + sourceEnd = sourceStart + target.length - targetStart; + } + + let nb = sourceEnd - sourceStart; + const sourceLen = source.length - sourceStart; + if (nb > sourceLen) { + nb = sourceLen; + } + + if (sourceStart !== 0 || sourceEnd < source.length) { + source = new Uint8Array(source.buffer, source.byteOffset + sourceStart, nb); + } + + target.set(source, targetStart); + + return nb; +} + +export function boundsError(value, length, type) { + if (Math.floor(value) !== value) { + validateNumber(value, type); + throw new codes.ERR_OUT_OF_RANGE(type || "offset", "an integer", value); + } + + if (length < 0) { + throw new codes.ERR_BUFFER_OUT_OF_BOUNDS(); + } + + throw new codes.ERR_OUT_OF_RANGE( + type || "offset", + `>= ${type ? 1 : 0} and <= ${length}`, + value, + ); +} + +export function validateNumber(value, name) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } +} + +function checkInt(value, min, max, buf, offset, byteLength) { + if (value > max || value < min) { + const n = typeof min === "bigint" ? "n" : ""; + let range; + if (byteLength > 3) { + if (min === 0 || min === 0n) { + range = `>= 0${n} and < 2${n} ** ${(byteLength + 1) * 8}${n}`; + } else { + range = `>= -(2${n} ** ${(byteLength + 1) * 8 - 1}${n}) and ` + + `< 2${n} ** ${(byteLength + 1) * 8 - 1}${n}`; + } + } else { + range = `>= ${min}${n} and <= ${max}${n}`; + } + throw new codes.ERR_OUT_OF_RANGE("value", range, value); + } + checkBounds(buf, offset, byteLength); +} + +export function toInteger(n, defaultVal) { + n = +n; + if ( + !Number.isNaN(n) && + n >= Number.MIN_SAFE_INTEGER && + n <= Number.MAX_SAFE_INTEGER + ) { + return ((n % 1) === 0 ? n : Math.floor(n)); + } + return defaultVal; +} + +// deno-lint-ignore camelcase +export function writeU_Int8(buf, value, offset, min, max) { + value = +value; + validateNumber(offset, "offset"); + if (value > max || value < min) { + throw new codes.ERR_OUT_OF_RANGE("value", `>= ${min} and <= ${max}`, value); + } + if (buf[offset] === undefined) { + boundsError(offset, buf.length - 1); + } + + buf[offset] = value; + return offset + 1; +} + +// deno-lint-ignore camelcase +export function writeU_Int16BE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 1); + + buf[offset++] = value >>> 8; + buf[offset++] = value; + return offset; +} + +export function _writeUInt32LE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 3); + + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + return offset; +} + +// deno-lint-ignore camelcase +export function writeU_Int16LE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 1); + + buf[offset++] = value; + buf[offset++] = value >>> 8; + return offset; +} + +export function _writeUInt32BE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 3); + + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +// deno-lint-ignore camelcase +export function writeU_Int48BE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 5); + + const newVal = Math.floor(value * 2 ** -32); + buf[offset++] = newVal >>> 8; + buf[offset++] = newVal; + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +// deno-lint-ignore camelcase +export function writeU_Int40BE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 4); + + buf[offset++] = Math.floor(value * 2 ** -32); + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +// deno-lint-ignore camelcase +export function writeU_Int32BE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 3); + + buf[offset + 3] = value; + value = value >>> 8; + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 4; +} + +// deno-lint-ignore camelcase +export function writeU_Int24BE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 2); + + buf[offset + 2] = value; + value = value >>> 8; + buf[offset + 1] = value; + value = value >>> 8; + buf[offset] = value; + return offset + 3; +} + +export function validateOffset( + value, + name, + min = 0, + max = Number.MAX_SAFE_INTEGER, +) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value); + } + if (value < min || value > max) { + throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } +} + +// deno-lint-ignore camelcase +export function writeU_Int48LE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 5); + + const newVal = Math.floor(value * 2 ** -32); + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + buf[offset++] = newVal; + buf[offset++] = newVal >>> 8; + return offset; +} + +// deno-lint-ignore camelcase +export function writeU_Int40LE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 4); + + const newVal = value; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + buf[offset++] = Math.floor(newVal * 2 ** -32); + return offset; +} + +// deno-lint-ignore camelcase +export function writeU_Int32LE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 3); + + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + return offset; +} + +// deno-lint-ignore camelcase +export function writeU_Int24LE(buf, value, offset, min, max) { + value = +value; + checkInt(value, min, max, buf, offset, 2); + + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + value = value >>> 8; + buf[offset++] = value; + return offset; +} + +export default { + atob, + btoa, + Blob, + Buffer, + constants, + kMaxLength, + kStringMaxLength, + SlowBuffer, +}; diff --git a/ext/node/polyfills/internal/child_process.ts b/ext/node/polyfills/internal/child_process.ts new file mode 100644 index 0000000000..92aa8d4fa5 --- /dev/null +++ b/ext/node/polyfills/internal/child_process.ts @@ -0,0 +1,1026 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// This module implements 'child_process' module of Node.JS API. +// ref: https://nodejs.org/api/child_process.html +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { os } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { + notImplemented, + warnNotImplemented, +} from "internal:deno_node/polyfills/_utils.ts"; +import { + Readable, + Stream, + Writable, +} from "internal:deno_node/polyfills/stream.ts"; +import { deferred } from "internal:deno_node/polyfills/_util/async.ts"; +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +import { + AbortError, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_UNKNOWN_SIGNAL, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { errnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { ErrnoException } from "internal:deno_node/polyfills/_global.d.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { + isInt32, + validateBoolean, + validateObject, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + ArrayIsArray, + ArrayPrototypeFilter, + ArrayPrototypeJoin, + ArrayPrototypePush, + ArrayPrototypeSlice, + ArrayPrototypeSort, + ArrayPrototypeUnshift, + ObjectPrototypeHasOwnProperty, + StringPrototypeToUpperCase, +} from "internal:deno_node/polyfills/internal/primordials.mjs"; +import { kEmptyObject } from "internal:deno_node/polyfills/internal/util.mjs"; +import { getValidatedPath } from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import process from "internal:deno_node/polyfills/process.ts"; + +export function mapValues( + record: Readonly>, + transformer: (value: T) => O, +): Record { + const ret: Record = {}; + const entries = Object.entries(record); + + for (const [key, value] of entries) { + const mappedValue = transformer(value); + + ret[key] = mappedValue; + } + + return ret; +} + +type NodeStdio = "pipe" | "overlapped" | "ignore" | "inherit" | "ipc"; +type DenoStdio = "inherit" | "piped" | "null"; + +// @ts-ignore Deno[Deno.internal] is used on purpose here +const DenoCommand = Deno[Deno.internal]?.nodeUnstable?.Command || + Deno.Command; + +export function stdioStringToArray( + stdio: NodeStdio, + channel: NodeStdio | number, +) { + const options: (NodeStdio | number)[] = []; + + switch (stdio) { + case "ignore": + case "overlapped": + case "pipe": + options.push(stdio, stdio, stdio); + break; + case "inherit": + options.push(stdio, stdio, stdio); + break; + default: + throw new ERR_INVALID_ARG_VALUE("stdio", stdio); + } + + if (channel) options.push(channel); + + return options; +} + +export class ChildProcess extends EventEmitter { + /** + * The exit code of the child process. This property will be `null` until the child process exits. + */ + exitCode: number | null = null; + + /** + * This property is set to `true` after `kill()` is called. + */ + killed = false; + + /** + * The PID of this child process. + */ + pid!: number; + + /** + * The signal received by this child process. + */ + signalCode: string | null = null; + + /** + * Command line arguments given to this child process. + */ + spawnargs: string[]; + + /** + * The executable file name of this child process. + */ + spawnfile: string; + + /** + * This property represents the child process's stdin. + */ + stdin: Writable | null = null; + + /** + * This property represents the child process's stdout. + */ + stdout: Readable | null = null; + + /** + * This property represents the child process's stderr. + */ + stderr: Readable | null = null; + + /** + * Pipes to this child process. + */ + stdio: [Writable | null, Readable | null, Readable | null] = [ + null, + null, + null, + ]; + + #process!: Deno.ChildProcess; + #spawned = deferred(); + + constructor( + command: string, + args?: string[], + options?: ChildProcessOptions, + ) { + super(); + + const { + env = {}, + stdio = ["pipe", "pipe", "pipe"], + cwd, + shell = false, + signal, + windowsVerbatimArguments = false, + } = options || {}; + const [ + stdin = "pipe", + stdout = "pipe", + stderr = "pipe", + _channel, // TODO(kt3k): handle this correctly + ] = normalizeStdioOption(stdio); + const [cmd, cmdArgs] = buildCommand( + command, + args || [], + shell, + ); + this.spawnfile = cmd; + this.spawnargs = [cmd, ...cmdArgs]; + + const stringEnv = mapValues(env, (value) => value.toString()); + + try { + this.#process = new DenoCommand(cmd, { + args: cmdArgs, + cwd, + env: stringEnv, + stdin: toDenoStdio(stdin as NodeStdio | number), + stdout: toDenoStdio(stdout as NodeStdio | number), + stderr: toDenoStdio(stderr as NodeStdio | number), + windowsRawArguments: windowsVerbatimArguments, + }).spawn(); + this.pid = this.#process.pid; + + if (stdin === "pipe") { + assert(this.#process.stdin); + this.stdin = Writable.fromWeb(this.#process.stdin); + } + + if (stdout === "pipe") { + assert(this.#process.stdout); + this.stdout = Readable.fromWeb(this.#process.stdout); + } + + if (stderr === "pipe") { + assert(this.#process.stderr); + this.stderr = Readable.fromWeb(this.#process.stderr); + } + + this.stdio[0] = this.stdin; + this.stdio[1] = this.stdout; + this.stdio[2] = this.stderr; + + nextTick(() => { + this.emit("spawn"); + this.#spawned.resolve(); + }); + + if (signal) { + const onAbortListener = () => { + try { + if (this.kill("SIGKILL")) { + this.emit("error", new AbortError()); + } + } catch (err) { + this.emit("error", err); + } + }; + if (signal.aborted) { + nextTick(onAbortListener); + } else { + signal.addEventListener("abort", onAbortListener, { once: true }); + this.addListener( + "exit", + () => signal.removeEventListener("abort", onAbortListener), + ); + } + } + + (async () => { + const status = await this.#process.status; + this.exitCode = status.code; + this.#spawned.then(async () => { + const exitCode = this.signalCode == null ? this.exitCode : null; + const signalCode = this.signalCode == null ? null : this.signalCode; + // The 'exit' and 'close' events must be emitted after the 'spawn' event. + this.emit("exit", exitCode, signalCode); + await this.#_waitForChildStreamsToClose(); + this.#closePipes(); + this.emit("close", exitCode, signalCode); + }); + })(); + } catch (err) { + this.#_handleError(err); + } + } + + /** + * @param signal NOTE: this parameter is not yet implemented. + */ + kill(signal?: number | string): boolean { + if (this.killed) { + return this.killed; + } + + const denoSignal = signal == null ? "SIGTERM" : toDenoSignal(signal); + this.#closePipes(); + try { + this.#process.kill(denoSignal); + } catch (err) { + const alreadyClosed = err instanceof TypeError || + err instanceof Deno.errors.PermissionDenied; + if (!alreadyClosed) { + throw err; + } + } + this.killed = true; + this.signalCode = denoSignal; + return this.killed; + } + + ref() { + this.#process.ref(); + } + + unref() { + this.#process.unref(); + } + + disconnect() { + warnNotImplemented("ChildProcess.prototype.disconnect"); + } + + async #_waitForChildStreamsToClose() { + const promises = [] as Array>; + if (this.stdin && !this.stdin.destroyed) { + assert(this.stdin); + this.stdin.destroy(); + promises.push(waitForStreamToClose(this.stdin)); + } + if (this.stdout && !this.stdout.destroyed) { + promises.push(waitForReadableToClose(this.stdout)); + } + if (this.stderr && !this.stderr.destroyed) { + promises.push(waitForReadableToClose(this.stderr)); + } + await Promise.all(promises); + } + + #_handleError(err: unknown) { + nextTick(() => { + this.emit("error", err); // TODO(uki00a) Convert `err` into nodejs's `SystemError` class. + }); + } + + #closePipes() { + if (this.stdin) { + assert(this.stdin); + this.stdin.destroy(); + } + } +} + +const supportedNodeStdioTypes: NodeStdio[] = ["pipe", "ignore", "inherit"]; +function toDenoStdio( + pipe: NodeStdio | number | Stream | null | undefined, +): DenoStdio { + if ( + !supportedNodeStdioTypes.includes(pipe as NodeStdio) || + typeof pipe === "number" || pipe instanceof Stream + ) { + notImplemented(`toDenoStdio pipe=${typeof pipe} (${pipe})`); + } + switch (pipe) { + case "pipe": + case undefined: + case null: + return "piped"; + case "ignore": + return "null"; + case "inherit": + return "inherit"; + default: + notImplemented(`toDenoStdio pipe=${typeof pipe} (${pipe})`); + } +} + +function toDenoSignal(signal: number | string): Deno.Signal { + if (typeof signal === "number") { + for (const name of keys(os.signals)) { + if (os.signals[name] === signal) { + return name as Deno.Signal; + } + } + throw new ERR_UNKNOWN_SIGNAL(String(signal)); + } + + const denoSignal = signal as Deno.Signal; + if (denoSignal in os.signals) { + return denoSignal; + } + throw new ERR_UNKNOWN_SIGNAL(signal); +} + +function keys>(object: T): Array { + return Object.keys(object); +} + +export interface ChildProcessOptions { + /** + * Current working directory of the child process. + */ + cwd?: string | URL; + + /** + * Environment variables passed to the child process. + */ + env?: Record; + + /** + * This option defines child process's stdio configuration. + * @see https://nodejs.org/api/child_process.html#child_process_options_stdio + */ + stdio?: Array | NodeStdio; + + /** + * NOTE: This option is not yet implemented. + */ + detached?: boolean; + + /** + * NOTE: This option is not yet implemented. + */ + uid?: number; + + /** + * NOTE: This option is not yet implemented. + */ + gid?: number; + + /** + * NOTE: This option is not yet implemented. + */ + argv0?: string; + + /** + * * If this option is `true`, run the command in the shell. + * * If this option is a string, run the command in the specified shell. + */ + shell?: string | boolean; + + /** + * Allows aborting the child process using an AbortSignal. + */ + signal?: AbortSignal; + + /** + * NOTE: This option is not yet implemented. + */ + serialization?: "json" | "advanced"; + + /** No quoting or escaping of arguments is done on Windows. Ignored on Unix. + * Default: false. */ + windowsVerbatimArguments?: boolean; + + /** + * NOTE: This option is not yet implemented. + */ + windowsHide?: boolean; +} + +function copyProcessEnvToEnv( + env: Record, + name: string, + optionEnv?: Record, +) { + if ( + Deno.env.get(name) && + (!optionEnv || + !ObjectPrototypeHasOwnProperty(optionEnv, name)) + ) { + env[name] = Deno.env.get(name); + } +} + +function normalizeStdioOption( + stdio: Array | NodeStdio = [ + "pipe", + "pipe", + "pipe", + ], +) { + if (Array.isArray(stdio)) { + return stdio; + } else { + switch (stdio) { + case "overlapped": + if (isWindows) { + notImplemented("normalizeStdioOption overlapped (on windows)"); + } + // 'overlapped' is same as 'piped' on non Windows system. + return ["pipe", "pipe", "pipe"]; + case "pipe": + return ["pipe", "pipe", "pipe"]; + case "inherit": + return ["inherit", "inherit", "inherit"]; + case "ignore": + return ["ignore", "ignore", "ignore"]; + default: + notImplemented(`normalizeStdioOption stdio=${typeof stdio} (${stdio})`); + } + } +} + +export function normalizeSpawnArguments( + file: string, + args: string[], + options: SpawnOptions & SpawnSyncOptions, +) { + validateString(file, "file"); + + if (file.length === 0) { + throw new ERR_INVALID_ARG_VALUE("file", file, "cannot be empty"); + } + + if (ArrayIsArray(args)) { + args = ArrayPrototypeSlice(args); + } else if (args == null) { + args = []; + } else if (typeof args !== "object") { + throw new ERR_INVALID_ARG_TYPE("args", "object", args); + } else { + options = args; + args = []; + } + + if (options === undefined) { + options = kEmptyObject; + } else { + validateObject(options, "options"); + } + + let cwd = options.cwd; + + // Validate the cwd, if present. + if (cwd != null) { + cwd = getValidatedPath(cwd, "options.cwd") as string; + } + + // Validate detached, if present. + if (options.detached != null) { + validateBoolean(options.detached, "options.detached"); + } + + // Validate the uid, if present. + if (options.uid != null && !isInt32(options.uid)) { + throw new ERR_INVALID_ARG_TYPE("options.uid", "int32", options.uid); + } + + // Validate the gid, if present. + if (options.gid != null && !isInt32(options.gid)) { + throw new ERR_INVALID_ARG_TYPE("options.gid", "int32", options.gid); + } + + // Validate the shell, if present. + if ( + options.shell != null && + typeof options.shell !== "boolean" && + typeof options.shell !== "string" + ) { + throw new ERR_INVALID_ARG_TYPE( + "options.shell", + ["boolean", "string"], + options.shell, + ); + } + + // Validate argv0, if present. + if (options.argv0 != null) { + validateString(options.argv0, "options.argv0"); + } + + // Validate windowsHide, if present. + if (options.windowsHide != null) { + validateBoolean(options.windowsHide, "options.windowsHide"); + } + + // Validate windowsVerbatimArguments, if present. + let { windowsVerbatimArguments } = options; + if (windowsVerbatimArguments != null) { + validateBoolean( + windowsVerbatimArguments, + "options.windowsVerbatimArguments", + ); + } + + if (options.shell) { + const command = ArrayPrototypeJoin([file, ...args], " "); + // Set the shell, switches, and commands. + if (process.platform === "win32") { + if (typeof options.shell === "string") { + file = options.shell; + } else { + file = Deno.env.get("comspec") || "cmd.exe"; + } + // '/d /s /c' is used only for cmd.exe. + if (/^(?:.*\\)?cmd(?:\.exe)?$/i.exec(file) !== null) { + args = ["/d", "/s", "/c", `"${command}"`]; + windowsVerbatimArguments = true; + } else { + args = ["-c", command]; + } + } else { + /** TODO: add Android condition */ + if (typeof options.shell === "string") { + file = options.shell; + } else { + file = "/bin/sh"; + } + args = ["-c", command]; + } + } + + if (typeof options.argv0 === "string") { + ArrayPrototypeUnshift(args, options.argv0); + } else { + ArrayPrototypeUnshift(args, file); + } + + const env = options.env || Deno.env.toObject(); + const envPairs: string[][] = []; + + // process.env.NODE_V8_COVERAGE always propagates, making it possible to + // collect coverage for programs that spawn with white-listed environment. + copyProcessEnvToEnv(env, "NODE_V8_COVERAGE", options.env); + + /** TODO: add `isZOS` condition */ + + let envKeys: string[] = []; + // Prototype values are intentionally included. + for (const key in env) { + if (Object.hasOwn(env, key)) { + ArrayPrototypePush(envKeys, key); + } + } + + if (process.platform === "win32") { + // On Windows env keys are case insensitive. Filter out duplicates, + // keeping only the first one (in lexicographic order) + /** TODO: implement SafeSet and makeSafe */ + const sawKey = new Set(); + envKeys = ArrayPrototypeFilter( + ArrayPrototypeSort(envKeys), + (key: string) => { + const uppercaseKey = StringPrototypeToUpperCase(key); + if (sawKey.has(uppercaseKey)) { + return false; + } + sawKey.add(uppercaseKey); + return true; + }, + ); + } + + for (const key of envKeys) { + const value = env[key]; + if (value !== undefined) { + ArrayPrototypePush(envPairs, `${key}=${value}`); + } + } + + return { + // Make a shallow copy so we don't clobber the user's options object. + ...options, + args, + cwd, + detached: !!options.detached, + envPairs, + file, + windowsHide: !!options.windowsHide, + windowsVerbatimArguments: !!windowsVerbatimArguments, + }; +} + +function waitForReadableToClose(readable: Readable) { + readable.resume(); // Ensure buffered data will be consumed. + return waitForStreamToClose(readable as unknown as Stream); +} + +function waitForStreamToClose(stream: Stream) { + const promise = deferred(); + const cleanup = () => { + stream.removeListener("close", onClose); + stream.removeListener("error", onError); + }; + const onClose = () => { + cleanup(); + promise.resolve(); + }; + const onError = (err: Error) => { + cleanup(); + promise.reject(err); + }; + stream.once("close", onClose); + stream.once("error", onError); + return promise; +} + +/** + * This function is based on https://github.com/nodejs/node/blob/fc6426ccc4b4cb73076356fb6dbf46a28953af01/lib/child_process.js#L504-L528. + * Copyright Joyent, Inc. and other Node contributors. All rights reserved. MIT license. + */ +function buildCommand( + file: string, + args: string[], + shell: string | boolean, +): [string, string[]] { + if (file === Deno.execPath()) { + // The user is trying to spawn another Deno process as Node.js. + args = toDenoArgs(args); + } + + if (shell) { + const command = [file, ...args].join(" "); + + // Set the shell, switches, and commands. + if (isWindows) { + if (typeof shell === "string") { + file = shell; + } else { + file = Deno.env.get("comspec") || "cmd.exe"; + } + // '/d /s /c' is used only for cmd.exe. + if (/^(?:.*\\)?cmd(?:\.exe)?$/i.test(file)) { + args = ["/d", "/s", "/c", `"${command}"`]; + } else { + args = ["-c", command]; + } + } else { + if (typeof shell === "string") { + file = shell; + } else { + file = "/bin/sh"; + } + args = ["-c", command]; + } + } + return [file, args]; +} + +function _createSpawnSyncError( + status: string, + command: string, + args: string[] = [], +): ErrnoException { + const error = errnoException( + codeMap.get(status), + "spawnSync " + command, + ); + error.path = command; + error.spawnargs = args; + return error; +} + +export interface SpawnOptions extends ChildProcessOptions { + /** + * NOTE: This option is not yet implemented. + */ + timeout?: number; + /** + * NOTE: This option is not yet implemented. + */ + killSignal?: string; +} + +export interface SpawnSyncOptions extends + Pick< + ChildProcessOptions, + | "cwd" + | "env" + | "argv0" + | "stdio" + | "uid" + | "gid" + | "shell" + | "windowsVerbatimArguments" + | "windowsHide" + > { + input?: string | Buffer | DataView; + timeout?: number; + maxBuffer?: number; + encoding?: string; + /** + * NOTE: This option is not yet implemented. + */ + killSignal?: string; +} + +export interface SpawnSyncResult { + pid?: number; + output?: [string | null, string | Buffer | null, string | Buffer | null]; + stdout?: Buffer | string | null; + stderr?: Buffer | string | null; + status?: number | null; + signal?: string | null; + error?: Error; +} + +function parseSpawnSyncOutputStreams( + output: Deno.CommandOutput, + name: "stdout" | "stderr", +): string | Buffer | null { + // new Deno.Command().outputSync() returns getters for stdout and stderr that throw when set + // to 'inherit'. + try { + return Buffer.from(output[name]) as string | Buffer; + } catch { + return null; + } +} + +export function spawnSync( + command: string, + args: string[], + options: SpawnSyncOptions, +): SpawnSyncResult { + const { + env = Deno.env.toObject(), + stdio = ["pipe", "pipe", "pipe"], + shell = false, + cwd, + encoding, + uid, + gid, + maxBuffer, + windowsVerbatimArguments = false, + } = options; + const normalizedStdio = normalizeStdioOption(stdio); + [command, args] = buildCommand(command, args ?? [], shell); + + const result: SpawnSyncResult = {}; + try { + const output = new DenoCommand(command, { + args, + cwd, + env, + stdout: toDenoStdio(normalizedStdio[1] as NodeStdio | number), + stderr: toDenoStdio(normalizedStdio[2] as NodeStdio | number), + uid, + gid, + windowsRawArguments: windowsVerbatimArguments, + }).outputSync(); + + const status = output.signal ? null : 0; + let stdout = parseSpawnSyncOutputStreams(output, "stdout"); + let stderr = parseSpawnSyncOutputStreams(output, "stderr"); + + if ( + (stdout && stdout.length > maxBuffer!) || + (stderr && stderr.length > maxBuffer!) + ) { + result.error = _createSpawnSyncError("ENOBUFS", command, args); + } + + if (encoding && encoding !== "buffer") { + stdout = stdout && stdout.toString(encoding); + stderr = stderr && stderr.toString(encoding); + } + + result.status = status; + result.signal = output.signal; + result.stdout = stdout; + result.stderr = stderr; + result.output = [output.signal, stdout, stderr]; + } catch (err) { + if (err instanceof Deno.errors.NotFound) { + result.error = _createSpawnSyncError("ENOENT", command, args); + } + } + return result; +} + +// These are Node.js CLI flags that expect a value. It's necessary to +// understand these flags in order to properly replace flags passed to the +// child process. For example, -e is a Node flag for eval mode if it is part +// of process.execArgv. However, -e could also be an application flag if it is +// part of process.execv instead. We only want to process execArgv flags. +const kLongArgType = 1; +const kShortArgType = 2; +const kLongArg = { type: kLongArgType }; +const kShortArg = { type: kShortArgType }; +const kNodeFlagsMap = new Map([ + ["--build-snapshot", kLongArg], + ["-c", kShortArg], + ["--check", kLongArg], + ["-C", kShortArg], + ["--conditions", kLongArg], + ["--cpu-prof-dir", kLongArg], + ["--cpu-prof-interval", kLongArg], + ["--cpu-prof-name", kLongArg], + ["--diagnostic-dir", kLongArg], + ["--disable-proto", kLongArg], + ["--dns-result-order", kLongArg], + ["-e", kShortArg], + ["--eval", kLongArg], + ["--experimental-loader", kLongArg], + ["--experimental-policy", kLongArg], + ["--experimental-specifier-resolution", kLongArg], + ["--heapsnapshot-near-heap-limit", kLongArg], + ["--heapsnapshot-signal", kLongArg], + ["--heap-prof-dir", kLongArg], + ["--heap-prof-interval", kLongArg], + ["--heap-prof-name", kLongArg], + ["--icu-data-dir", kLongArg], + ["--input-type", kLongArg], + ["--inspect-publish-uid", kLongArg], + ["--max-http-header-size", kLongArg], + ["--openssl-config", kLongArg], + ["-p", kShortArg], + ["--print", kLongArg], + ["--policy-integrity", kLongArg], + ["--prof-process", kLongArg], + ["-r", kShortArg], + ["--require", kLongArg], + ["--redirect-warnings", kLongArg], + ["--report-dir", kLongArg], + ["--report-directory", kLongArg], + ["--report-filename", kLongArg], + ["--report-signal", kLongArg], + ["--secure-heap", kLongArg], + ["--secure-heap-min", kLongArg], + ["--snapshot-blob", kLongArg], + ["--title", kLongArg], + ["--tls-cipher-list", kLongArg], + ["--tls-keylog", kLongArg], + ["--unhandled-rejections", kLongArg], + ["--use-largepages", kLongArg], + ["--v8-pool-size", kLongArg], +]); +const kDenoSubcommands = new Set([ + "bench", + "bundle", + "cache", + "check", + "compile", + "completions", + "coverage", + "doc", + "eval", + "fmt", + "help", + "info", + "init", + "install", + "lint", + "lsp", + "repl", + "run", + "tasks", + "test", + "types", + "uninstall", + "upgrade", + "vendor", +]); + +function toDenoArgs(args: string[]): string[] { + if (args.length === 0) { + return args; + } + + // Update this logic as more CLI arguments are mapped from Node to Deno. + const denoArgs: string[] = []; + let useRunArgs = true; + + for (let i = 0; i < args.length; i++) { + const arg = args[i]; + + if (arg.charAt(0) !== "-" || arg === "--") { + // Not a flag or no more arguments. + + // If the arg is a Deno subcommand, then the child process is being + // spawned as Deno, not Deno in Node compat mode. In this case, bail out + // and return the original args. + if (kDenoSubcommands.has(arg)) { + return args; + } + + // Copy of the rest of the arguments to the output. + for (let j = i; j < args.length; j++) { + denoArgs.push(args[j]); + } + + break; + } + + // Something that looks like a flag was passed. + let flag = arg; + let flagInfo = kNodeFlagsMap.get(arg); + let isLongWithValue = false; + let flagValue; + + if (flagInfo === undefined) { + // If the flag was not found, it's either not a known flag or it's a long + // flag containing an '='. + const splitAt = arg.indexOf("="); + + if (splitAt !== -1) { + flag = arg.slice(0, splitAt); + flagInfo = kNodeFlagsMap.get(flag); + flagValue = arg.slice(splitAt + 1); + isLongWithValue = true; + } + } + + if (flagInfo === undefined) { + // Not a known flag that expects a value. Just copy it to the output. + denoArgs.push(arg); + continue; + } + + // This is a flag with a value. Get the value if we don't already have it. + if (flagValue === undefined) { + i++; + + if (i >= args.length) { + // There was user error. There should be another arg for the value, but + // there isn't one. Just copy the arg to the output. It's not going + // to work anyway. + denoArgs.push(arg); + continue; + } + + flagValue = args[i]; + } + + // Remap Node's eval flags to Deno. + if (flag === "-e" || flag === "--eval") { + denoArgs.push("eval", flagValue); + useRunArgs = false; + } else if (isLongWithValue) { + denoArgs.push(arg); + } else { + denoArgs.push(flag, flagValue); + } + } + + if (useRunArgs) { + // -A is not ideal, but needed to propagate permissions. + // --unstable is needed for Node compat. + denoArgs.unshift("run", "-A", "--unstable"); + } + + return denoArgs; +} + +export default { + ChildProcess, + normalizeSpawnArguments, + stdioStringToArray, + spawnSync, +}; diff --git a/ext/node/polyfills/internal/cli_table.ts b/ext/node/polyfills/internal/cli_table.ts new file mode 100644 index 0000000000..68cc6d0443 --- /dev/null +++ b/ext/node/polyfills/internal/cli_table.ts @@ -0,0 +1,85 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { getStringWidth } from "internal:deno_node/polyfills/internal/util/inspect.mjs"; + +// The use of Unicode characters below is the only non-comment use of non-ASCII +// Unicode characters in Node.js built-in modules. If they are ever removed or +// rewritten with \u escapes, then a test will need to be (re-)added to Node.js +// core to verify that Unicode characters work in built-ins. +// Refs: https://github.com/nodejs/node/issues/10673 +const tableChars = { + middleMiddle: "─", + rowMiddle: "┼", + topRight: "┐", + topLeft: "┌", + leftMiddle: "├", + topMiddle: "┬", + bottomRight: "┘", + bottomLeft: "└", + bottomMiddle: "┴", + rightMiddle: "┤", + left: "│ ", + right: " │", + middle: " │ ", +}; + +const renderRow = (row: string[], columnWidths: number[]) => { + let out = tableChars.left; + for (let i = 0; i < row.length; i++) { + const cell = row[i]; + const len = getStringWidth(cell); + const needed = (columnWidths[i] - len) / 2; + // round(needed) + ceil(needed) will always add up to the amount + // of spaces we need while also left justifying the output. + out += " ".repeat(needed) + cell + + " ".repeat(Math.ceil(needed)); + if (i !== row.length - 1) { + out += tableChars.middle; + } + } + out += tableChars.right; + return out; +}; + +const table = (head: string[], columns: string[][]) => { + const rows: string[][] = []; + const columnWidths = head.map((h) => getStringWidth(h)); + const longestColumn = Math.max(...columns.map((a) => a.length)); + + for (let i = 0; i < head.length; i++) { + const column = columns[i]; + for (let j = 0; j < longestColumn; j++) { + if (rows[j] === undefined) { + rows[j] = []; + } + const value = rows[j][i] = Object.hasOwn(column, j) ? column[j] : ""; + const width = columnWidths[i] || 0; + const counted = getStringWidth(value); + columnWidths[i] = Math.max(width, counted); + } + } + + const divider = columnWidths.map((i) => + tableChars.middleMiddle.repeat(i + 2) + ); + + let result = tableChars.topLeft + + divider.join(tableChars.topMiddle) + + tableChars.topRight + "\n" + + renderRow(head, columnWidths) + "\n" + + tableChars.leftMiddle + + divider.join(tableChars.rowMiddle) + + tableChars.rightMiddle + "\n"; + + for (const row of rows) { + result += `${renderRow(row, columnWidths)}\n`; + } + + result += tableChars.bottomLeft + + divider.join(tableChars.bottomMiddle) + + tableChars.bottomRight; + + return result; +}; +export default table; diff --git a/ext/node/polyfills/internal/console/constructor.mjs b/ext/node/polyfills/internal/console/constructor.mjs new file mode 100644 index 0000000000..362c97f689 --- /dev/null +++ b/ext/node/polyfills/internal/console/constructor.mjs @@ -0,0 +1,677 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +// Mock trace for now +const trace = () => {}; +import { + ERR_CONSOLE_WRITABLE_STREAM, + ERR_INCOMPATIBLE_OPTION_PAIR, + ERR_INVALID_ARG_VALUE, + isStackOverflowError, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateArray, + validateInteger, + validateObject, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +const previewEntries = (iter, isKeyValue) => { + if (isKeyValue) { + const arr = [...iter]; + if (Array.isArray(arr[0]) && arr[0].length === 2) { + return [[].concat(...arr), true]; + } + return [arr, false]; + } else { + return [...iter]; + } +}; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +const { isBuffer } = Buffer; +import { formatWithOptions, inspect } from "internal:deno_node/polyfills/internal/util/inspect.mjs"; +import { + isMap, + isMapIterator, + isSet, + isSetIterator, + isTypedArray, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { + CHAR_LOWERCASE_B as kTraceBegin, + CHAR_LOWERCASE_E as kTraceEnd, + CHAR_LOWERCASE_N as kTraceInstant, + CHAR_UPPERCASE_C as kTraceCount, +} from "internal:deno_node/polyfills/internal/constants.ts"; +import { clearScreenDown, cursorTo } from "internal:deno_node/polyfills/internal/readline/callbacks.mjs"; +import cliTable from "internal:deno_node/polyfills/internal/cli_table.ts"; +const kCounts = Symbol("counts"); + +const kTraceConsoleCategory = "node,node.console"; + +const kSecond = 1000; +const kMinute = 60 * kSecond; +const kHour = 60 * kMinute; +const kMaxGroupIndentation = 1000; + +// Track amount of indentation required via `console.group()`. +const kGroupIndent = Symbol("kGroupIndent"); +const kGroupIndentationWidth = Symbol("kGroupIndentWidth"); +const kFormatForStderr = Symbol("kFormatForStderr"); +const kFormatForStdout = Symbol("kFormatForStdout"); +const kGetInspectOptions = Symbol("kGetInspectOptions"); +const kColorMode = Symbol("kColorMode"); +const kIsConsole = Symbol("kIsConsole"); +const kWriteToConsole = Symbol("kWriteToConsole"); +const kBindProperties = Symbol("kBindProperties"); +const kBindStreamsEager = Symbol("kBindStreamsEager"); +const kBindStreamsLazy = Symbol("kBindStreamsLazy"); +const kUseStdout = Symbol("kUseStdout"); +const kUseStderr = Symbol("kUseStderr"); + +const optionsMap = new WeakMap(); + +function Console(options /* or: stdout, stderr, ignoreErrors = true */) { + // We have to test new.target here to see if this function is called + // with new, because we need to define a custom instanceof to accommodate + // the global console. + if (!new.target) { + return Reflect.construct(Console, arguments); + } + + if (!options || typeof options.write === "function") { + options = { + stdout: options, + stderr: arguments[1], + ignoreErrors: arguments[2], + }; + } + + const { + stdout, + stderr = stdout, + ignoreErrors = true, + colorMode = "auto", + inspectOptions, + groupIndentation, + } = options; + + if (!stdout || typeof stdout.write !== "function") { + throw new ERR_CONSOLE_WRITABLE_STREAM("stdout"); + } + if (!stderr || typeof stderr.write !== "function") { + throw new ERR_CONSOLE_WRITABLE_STREAM("stderr"); + } + + if (typeof colorMode !== "boolean" && colorMode !== "auto") { + throw new ERR_INVALID_ARG_VALUE("colorMode", colorMode); + } + + if (groupIndentation !== undefined) { + validateInteger( + groupIndentation, + "groupIndentation", + 0, + kMaxGroupIndentation, + ); + } + + if (inspectOptions !== undefined) { + validateObject(inspectOptions, "options.inspectOptions"); + + if ( + inspectOptions.colors !== undefined && + options.colorMode !== undefined + ) { + throw new ERR_INCOMPATIBLE_OPTION_PAIR( + "options.inspectOptions.color", + "colorMode", + ); + } + optionsMap.set(this, inspectOptions); + } + + // Bind the prototype functions to this Console instance + Object.keys(Console.prototype).forEach((key) => { + // We have to bind the methods grabbed from the instance instead of from + // the prototype so that users extending the Console can override them + // from the prototype chain of the subclass. + this[key] = this[key].bind(this); + Object.defineProperty(this[key], "name", { + value: key, + }); + }); + + this[kBindStreamsEager](stdout, stderr); + this[kBindProperties](ignoreErrors, colorMode, groupIndentation); +} + +const consolePropAttributes = { + writable: true, + enumerable: false, + configurable: true, +}; + +// Fixup global.console instanceof global.console.Console +Object.defineProperty(Console, Symbol.hasInstance, { + value(instance) { + return instance === console || instance[kIsConsole]; + }, +}); + +const kColorInspectOptions = { colors: true }; +const kNoColorInspectOptions = {}; + +Object.defineProperties(Console.prototype, { + [kBindStreamsEager]: { + ...consolePropAttributes, + // Eager version for the Console constructor + value: function (stdout, stderr) { + Object.defineProperties(this, { + "_stdout": { ...consolePropAttributes, value: stdout }, + "_stderr": { ...consolePropAttributes, value: stderr }, + }); + }, + }, + [kBindStreamsLazy]: { + ...consolePropAttributes, + // Lazily load the stdout and stderr from an object so we don't + // create the stdio streams when they are not even accessed + value: function (object) { + let stdout; + let stderr; + Object.defineProperties(this, { + "_stdout": { + enumerable: false, + configurable: true, + get() { + if (!stdout) stdout = object.stdout; + return stdout; + }, + set(value) { + stdout = value; + }, + }, + "_stderr": { + enumerable: false, + configurable: true, + get() { + if (!stderr) stderr = object.stderr; + return stderr; + }, + set(value) { + stderr = value; + }, + }, + }); + }, + }, + [kBindProperties]: { + ...consolePropAttributes, + value: function (ignoreErrors, colorMode, groupIndentation = 2) { + Object.defineProperties(this, { + "_stdoutErrorHandler": { + ...consolePropAttributes, + value: createWriteErrorHandler(this, kUseStdout), + }, + "_stderrErrorHandler": { + ...consolePropAttributes, + value: createWriteErrorHandler(this, kUseStderr), + }, + "_ignoreErrors": { + ...consolePropAttributes, + value: Boolean(ignoreErrors), + }, + "_times": { ...consolePropAttributes, value: new Map() }, + // Corresponds to https://console.spec.whatwg.org/#count-map + [kCounts]: { ...consolePropAttributes, value: new Map() }, + [kColorMode]: { ...consolePropAttributes, value: colorMode }, + [kIsConsole]: { ...consolePropAttributes, value: true }, + [kGroupIndent]: { ...consolePropAttributes, value: "" }, + [kGroupIndentationWidth]: { + ...consolePropAttributes, + value: groupIndentation, + }, + [Symbol.toStringTag]: { + writable: false, + enumerable: false, + configurable: true, + value: "console", + }, + }); + }, + }, + [kWriteToConsole]: { + ...consolePropAttributes, + value: function (streamSymbol, string) { + const ignoreErrors = this._ignoreErrors; + const groupIndent = this[kGroupIndent]; + + const useStdout = streamSymbol === kUseStdout; + const stream = useStdout ? this._stdout : this._stderr; + const errorHandler = useStdout + ? this._stdoutErrorHandler + : this._stderrErrorHandler; + + if (groupIndent.length !== 0) { + if (string.includes("\n")) { + string = string.replace(/\n/g, `\n${groupIndent}`); + } + string = groupIndent + string; + } + string += "\n"; + + if (ignoreErrors === false) return stream.write(string); + + // There may be an error occurring synchronously (e.g. for files or TTYs + // on POSIX systems) or asynchronously (e.g. pipes on POSIX systems), so + // handle both situations. + try { + // Add and later remove a noop error handler to catch synchronous + // errors. + if (stream.listenerCount("error") === 0) { + stream.once("error", noop); + } + + stream.write(string, errorHandler); + } catch (e) { + // Console is a debugging utility, so it swallowing errors is not + // desirable even in edge cases such as low stack space. + if (isStackOverflowError(e)) { + throw e; + } + // Sorry, there's no proper way to pass along the error here. + } finally { + stream.removeListener("error", noop); + } + }, + }, + [kGetInspectOptions]: { + ...consolePropAttributes, + value: function (stream) { + let color = this[kColorMode]; + if (color === "auto") { + color = stream.isTTY && ( + typeof stream.getColorDepth === "function" + ? stream.getColorDepth() > 2 + : true + ); + } + + const options = optionsMap.get(this); + if (options) { + if (options.colors === undefined) { + options.colors = color; + } + return options; + } + + return color ? kColorInspectOptions : kNoColorInspectOptions; + }, + }, + [kFormatForStdout]: { + ...consolePropAttributes, + value: function (args) { + const opts = this[kGetInspectOptions](this._stdout); + args.unshift(opts); + return Reflect.apply(formatWithOptions, null, args); + }, + }, + [kFormatForStderr]: { + ...consolePropAttributes, + value: function (args) { + const opts = this[kGetInspectOptions](this._stderr); + args.unshift(opts); + return Reflect.apply(formatWithOptions, null, args); + }, + }, +}); + +// Make a function that can serve as the callback passed to `stream.write()`. +function createWriteErrorHandler(instance, streamSymbol) { + return (err) => { + // This conditional evaluates to true if and only if there was an error + // that was not already emitted (which happens when the _write callback + // is invoked asynchronously). + const stream = streamSymbol === kUseStdout + ? instance._stdout + : instance._stderr; + if (err !== null && !stream._writableState.errorEmitted) { + // If there was an error, it will be emitted on `stream` as + // an `error` event. Adding a `once` listener will keep that error + // from becoming an uncaught exception, but since the handler is + // removed after the event, non-console.* writes won't be affected. + // we are only adding noop if there is no one else listening for 'error' + if (stream.listenerCount("error") === 0) { + stream.once("error", noop); + } + } + }; +} + +const consoleMethods = { + log(...args) { + this[kWriteToConsole](kUseStdout, this[kFormatForStdout](args)); + }, + + warn(...args) { + this[kWriteToConsole](kUseStderr, this[kFormatForStderr](args)); + }, + + dir(object, options) { + this[kWriteToConsole]( + kUseStdout, + inspect(object, { + customInspect: false, + ...this[kGetInspectOptions](this._stdout), + ...options, + }), + ); + }, + + time(label = "default") { + // Coerces everything other than Symbol to a string + label = `${label}`; + if (this._times.has(label)) { + emitWarning(`Label '${label}' already exists for console.time()`); + return; + } + trace(kTraceBegin, kTraceConsoleCategory, `time::${label}`, 0); + this._times.set(label, process.hrtime()); + }, + + timeEnd(label = "default") { + // Coerces everything other than Symbol to a string + label = `${label}`; + const found = timeLogImpl(this, "timeEnd", label); + trace(kTraceEnd, kTraceConsoleCategory, `time::${label}`, 0); + if (found) { + this._times.delete(label); + } + }, + + timeLog(label = "default", ...data) { + // Coerces everything other than Symbol to a string + label = `${label}`; + timeLogImpl(this, "timeLog", label, data); + trace(kTraceInstant, kTraceConsoleCategory, `time::${label}`, 0); + }, + + trace: function trace(...args) { + const err = { + name: "Trace", + message: this[kFormatForStderr](args), + }; + Error.captureStackTrace(err, trace); + this.error(err.stack); + }, + + assert(expression, ...args) { + if (!expression) { + args[0] = `Assertion failed${args.length === 0 ? "" : `: ${args[0]}`}`; + // The arguments will be formatted in warn() again + Reflect.apply(this.warn, this, args); + } + }, + + // Defined by: https://console.spec.whatwg.org/#clear + clear() { + // It only makes sense to clear if _stdout is a TTY. + // Otherwise, do nothing. + if (this._stdout.isTTY && process.env.TERM !== "dumb") { + cursorTo(this._stdout, 0, 0); + clearScreenDown(this._stdout); + } + }, + + // Defined by: https://console.spec.whatwg.org/#count + count(label = "default") { + // Ensures that label is a string, and only things that can be + // coerced to strings. e.g. Symbol is not allowed + label = `${label}`; + const counts = this[kCounts]; + let count = counts.get(label); + if (count === undefined) { + count = 1; + } else { + count++; + } + counts.set(label, count); + trace(kTraceCount, kTraceConsoleCategory, `count::${label}`, 0, count); + this.log(`${label}: ${count}`); + }, + + // Defined by: https://console.spec.whatwg.org/#countreset + countReset(label = "default") { + const counts = this[kCounts]; + if (!counts.has(label)) { + emitWarning(`Count for '${label}' does not exist`); + return; + } + trace(kTraceCount, kTraceConsoleCategory, `count::${label}`, 0, 0); + counts.delete(`${label}`); + }, + + group(...data) { + if (data.length > 0) { + Reflect.apply(this.log, this, data); + } + this[kGroupIndent] += " ".repeat(this[kGroupIndentationWidth]); + }, + + groupEnd() { + this[kGroupIndent] = this[kGroupIndent].slice( + 0, + this[kGroupIndent].length - this[kGroupIndentationWidth], + ); + }, + + // https://console.spec.whatwg.org/#table + table(tabularData, properties) { + console.log("tabularData", tabularData); + if (properties !== undefined) { + validateArray(properties, "properties"); + } + + if (tabularData === null || typeof tabularData !== "object") { + return this.log(tabularData); + } + + const final = (k, v) => this.log(cliTable(k, v)); + + const _inspect = (v) => { + const depth = v !== null && + typeof v === "object" && + !isArray(v) && + Object.keys(v).length > 2 + ? -1 + : 0; + const opt = { + depth, + maxArrayLength: 3, + breakLength: Infinity, + ...this[kGetInspectOptions](this._stdout), + }; + return inspect(v, opt); + }; + const getIndexArray = (length) => + Array.from( + { length }, + (_, i) => _inspect(i), + ); + + const mapIter = isMapIterator(tabularData); + let isKeyValue = false; + let i = 0; + if (mapIter) { + const res = previewEntries(tabularData, true); + tabularData = res[0]; + isKeyValue = res[1]; + } + + if (isKeyValue || isMap(tabularData)) { + const keys = []; + const values = []; + let length = 0; + if (mapIter) { + for (; i < tabularData.length / 2; ++i) { + keys.push(_inspect(tabularData[i * 2])); + values.push(_inspect(tabularData[i * 2 + 1])); + length++; + } + } else { + for (const { 0: k, 1: v } of tabularData) { + keys.push(_inspect(k)); + values.push(_inspect(v)); + length++; + } + } + return final([ + iterKey, + keyKey, + valuesKey, + ], [ + getIndexArray(length), + keys, + values, + ]); + } + + const setIter = isSetIterator(tabularData); + if (setIter) { + tabularData = previewEntries(tabularData); + } + + const setlike = setIter || mapIter || isSet(tabularData); + if (setlike) { + const values = []; + let length = 0; + console.log("tabularData", tabularData); + for (const v of tabularData) { + values.push(_inspect(v)); + length++; + } + return final([iterKey, valuesKey], [getIndexArray(length), values]); + } + + const map = Object.create(null); + let hasPrimitives = false; + const valuesKeyArray = []; + const indexKeyArray = Object.keys(tabularData); + + for (; i < indexKeyArray.length; i++) { + const item = tabularData[indexKeyArray[i]]; + const primitive = item === null || + (typeof item !== "function" && typeof item !== "object"); + if (properties === undefined && primitive) { + hasPrimitives = true; + valuesKeyArray[i] = _inspect(item); + } else { + const keys = properties || Object.keys(item); + for (const key of keys) { + if (map[key] === undefined) { + map[key] = []; + } + if ( + (primitive && properties) || + !Object.hasOwn(item, key) + ) { + map[key][i] = ""; + } else { + map[key][i] = _inspect(item[key]); + } + } + } + } + + const keys = Object.keys(map); + const values = Object.values(map); + if (hasPrimitives) { + keys.push(valuesKey); + values.push(valuesKeyArray); + } + keys.unshift(indexKey); + values.unshift(indexKeyArray); + + return final(keys, values); + }, +}; + +// Returns true if label was found +function timeLogImpl(self, name, label, data) { + const time = self._times.get(label); + if (time === undefined) { + emitWarning(`No such label '${label}' for console.${name}()`); + return false; + } + const duration = process.hrtime(time); + const ms = duration[0] * 1000 + duration[1] / 1e6; + + const formatted = formatTime(ms); + + if (data === undefined) { + self.log("%s: %s", label, formatted); + } else { + self.log("%s: %s", label, formatted, ...data); + } + return true; +} + +function pad(value) { + return `${value}`.padStart(2, "0"); +} + +function formatTime(ms) { + let hours = 0; + let minutes = 0; + let seconds = 0; + + if (ms >= kSecond) { + if (ms >= kMinute) { + if (ms >= kHour) { + hours = Math.floor(ms / kHour); + ms = ms % kHour; + } + minutes = Math.floor(ms / kMinute); + ms = ms % kMinute; + } + seconds = ms / kSecond; + } + + if (hours !== 0 || minutes !== 0) { + ({ 0: seconds, 1: ms } = seconds.toFixed(3).split(".")); + const res = hours !== 0 ? `${hours}:${pad(minutes)}` : minutes; + return `${res}:${pad(seconds)}.${ms} (${hours !== 0 ? "h:m" : ""}m:ss.mmm)`; + } + + if (seconds !== 0) { + return `${seconds.toFixed(3)}s`; + } + + return `${Number(ms.toFixed(3))}ms`; +} + +const keyKey = "Key"; +const valuesKey = "Values"; +const indexKey = "(index)"; +const iterKey = "(iteration index)"; + +const isArray = (v) => Array.isArray(v) || isTypedArray(v) || isBuffer(v); + +function noop() {} + +for (const method of Reflect.ownKeys(consoleMethods)) { + Console.prototype[method] = consoleMethods[method]; +} + +Console.prototype.debug = Console.prototype.log; +Console.prototype.info = Console.prototype.log; +Console.prototype.dirxml = Console.prototype.log; +Console.prototype.error = Console.prototype.warn; +Console.prototype.groupCollapsed = Console.prototype.group; + +export { Console, formatTime, kBindProperties, kBindStreamsLazy }; +export default { + Console, + kBindStreamsLazy, + kBindProperties, + formatTime, +}; diff --git a/ext/node/polyfills/internal/constants.ts b/ext/node/polyfills/internal/constants.ts new file mode 100644 index 0000000000..5c5cafe8d1 --- /dev/null +++ b/ext/node/polyfills/internal/constants.ts @@ -0,0 +1,105 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +const { ops } = globalThis.__bootstrap.core; +const isWindows = ops.op_node_build_os() === "windows"; + +// Alphabet chars. +export const CHAR_UPPERCASE_A = 65; /* A */ +export const CHAR_LOWERCASE_A = 97; /* a */ +export const CHAR_UPPERCASE_Z = 90; /* Z */ +export const CHAR_LOWERCASE_Z = 122; /* z */ +export const CHAR_UPPERCASE_C = 67; /* C */ +export const CHAR_LOWERCASE_B = 98; /* b */ +export const CHAR_LOWERCASE_E = 101; /* e */ +export const CHAR_LOWERCASE_N = 110; /* n */ + +// Non-alphabetic chars. +export const CHAR_DOT = 46; /* . */ +export const CHAR_FORWARD_SLASH = 47; /* / */ +export const CHAR_BACKWARD_SLASH = 92; /* \ */ +export const CHAR_VERTICAL_LINE = 124; /* | */ +export const CHAR_COLON = 58; /* = */ +export const CHAR_QUESTION_MARK = 63; /* ? */ +export const CHAR_UNDERSCORE = 95; /* _ */ +export const CHAR_LINE_FEED = 10; /* \n */ +export const CHAR_CARRIAGE_RETURN = 13; /* \r */ +export const CHAR_TAB = 9; /* \t */ +export const CHAR_FORM_FEED = 12; /* \f */ +export const CHAR_EXCLAMATION_MARK = 33; /* ! */ +export const CHAR_HASH = 35; /* # */ +export const CHAR_SPACE = 32; /* */ +export const CHAR_NO_BREAK_SPACE = 160; /* \u00A0 */ +export const CHAR_ZERO_WIDTH_NOBREAK_SPACE = 65279; /* \uFEFF */ +export const CHAR_LEFT_SQUARE_BRACKET = 91; /* [ */ +export const CHAR_RIGHT_SQUARE_BRACKET = 93; /* ] */ +export const CHAR_LEFT_ANGLE_BRACKET = 60; /* < */ +export const CHAR_RIGHT_ANGLE_BRACKET = 62; /* > */ +export const CHAR_LEFT_CURLY_BRACKET = 123; /* { */ +export const CHAR_RIGHT_CURLY_BRACKET = 125; /* } */ +export const CHAR_HYPHEN_MINUS = 45; /* - */ +export const CHAR_PLUS = 43; /* + */ +export const CHAR_DOUBLE_QUOTE = 34; /* " */ +export const CHAR_SINGLE_QUOTE = 39; /* ' */ +export const CHAR_PERCENT = 37; /* % */ +export const CHAR_SEMICOLON = 59; /* ; */ +export const CHAR_CIRCUMFLEX_ACCENT = 94; /* ^ */ +export const CHAR_GRAVE_ACCENT = 96; /* ` */ +export const CHAR_AT = 64; /* @ */ +export const CHAR_AMPERSAND = 38; /* & */ +export const CHAR_EQUAL = 61; /* = */ + +// Digits +export const CHAR_0 = 48; /* 0 */ +export const CHAR_9 = 57; /* 9 */ + +export const EOL = isWindows ? "\r\n" : "\n"; + +export default { + CHAR_UPPERCASE_A, + CHAR_LOWERCASE_A, + CHAR_UPPERCASE_Z, + CHAR_LOWERCASE_Z, + CHAR_UPPERCASE_C, + CHAR_LOWERCASE_B, + CHAR_LOWERCASE_E, + CHAR_LOWERCASE_N, + CHAR_DOT, + CHAR_FORWARD_SLASH, + CHAR_BACKWARD_SLASH, + CHAR_VERTICAL_LINE, + CHAR_COLON, + CHAR_QUESTION_MARK, + CHAR_UNDERSCORE, + CHAR_LINE_FEED, + CHAR_CARRIAGE_RETURN, + CHAR_TAB, + CHAR_FORM_FEED, + CHAR_EXCLAMATION_MARK, + CHAR_HASH, + CHAR_SPACE, + CHAR_NO_BREAK_SPACE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE, + CHAR_LEFT_SQUARE_BRACKET, + CHAR_RIGHT_SQUARE_BRACKET, + CHAR_LEFT_ANGLE_BRACKET, + CHAR_RIGHT_ANGLE_BRACKET, + CHAR_LEFT_CURLY_BRACKET, + CHAR_RIGHT_CURLY_BRACKET, + CHAR_HYPHEN_MINUS, + CHAR_PLUS, + CHAR_DOUBLE_QUOTE, + CHAR_SINGLE_QUOTE, + CHAR_PERCENT, + CHAR_SEMICOLON, + CHAR_CIRCUMFLEX_ACCENT, + CHAR_GRAVE_ACCENT, + CHAR_AT, + CHAR_AMPERSAND, + CHAR_EQUAL, + + CHAR_0, + CHAR_9, + + EOL, +}; diff --git a/ext/node/polyfills/internal/crypto/_hex.ts b/ext/node/polyfills/internal/crypto/_hex.ts new file mode 100644 index 0000000000..5cc44aaa87 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_hex.ts @@ -0,0 +1,19 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-fmt-ignore +const hexTable = new Uint8Array([ + 48, 49, 50, 51, 52, 53, + 54, 55, 56, 57, 97, 98, + 99, 100, 101, 102 +]); + +/** Encodes `src` into `src.length * 2` bytes. */ +export function encode(src: Uint8Array): Uint8Array { + const dst = new Uint8Array(src.length * 2); + for (let i = 0; i < dst.length; i++) { + const v = src[i]; + dst[i * 2] = hexTable[v >> 4]; + dst[i * 2 + 1] = hexTable[v & 0x0f]; + } + return dst; +} diff --git a/ext/node/polyfills/internal/crypto/_keys.ts b/ext/node/polyfills/internal/crypto/_keys.ts new file mode 100644 index 0000000000..794582bf17 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_keys.ts @@ -0,0 +1,16 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { kKeyObject } from "internal:deno_node/polyfills/internal/crypto/constants.ts"; + +export const kKeyType = Symbol("kKeyType"); + +export function isKeyObject(obj: unknown): boolean { + return ( + obj != null && (obj as Record)[kKeyType] !== undefined + ); +} + +export function isCryptoKey(obj: unknown): boolean { + return ( + obj != null && (obj as Record)[kKeyObject] !== undefined + ); +} diff --git a/ext/node/polyfills/internal/crypto/_randomBytes.ts b/ext/node/polyfills/internal/crypto/_randomBytes.ts new file mode 100644 index 0000000000..41678fcf1f --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_randomBytes.ts @@ -0,0 +1,70 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export const MAX_RANDOM_VALUES = 65536; +export const MAX_SIZE = 4294967295; + +function generateRandomBytes(size: number) { + if (size > MAX_SIZE) { + throw new RangeError( + `The value of "size" is out of range. It must be >= 0 && <= ${MAX_SIZE}. Received ${size}`, + ); + } + + const bytes = Buffer.allocUnsafe(size); + + //Work around for getRandomValues max generation + if (size > MAX_RANDOM_VALUES) { + for (let generated = 0; generated < size; generated += MAX_RANDOM_VALUES) { + globalThis.crypto.getRandomValues( + bytes.slice(generated, generated + MAX_RANDOM_VALUES), + ); + } + } else { + globalThis.crypto.getRandomValues(bytes); + } + + return bytes; +} + +/** + * @param size Buffer length, must be equal or greater than zero + */ +export default function randomBytes(size: number): Buffer; +export default function randomBytes( + size: number, + cb?: (err: Error | null, buf?: Buffer) => void, +): void; +export default function randomBytes( + size: number, + cb?: (err: Error | null, buf?: Buffer) => void, +): Buffer | void { + if (typeof cb === "function") { + let err: Error | null = null, bytes: Buffer; + try { + bytes = generateRandomBytes(size); + } catch (e) { + //NodeJS nonsense + //If the size is out of range it will throw sync, otherwise throw async + if ( + e instanceof RangeError && + e.message.includes('The value of "size" is out of range') + ) { + throw e; + } else if (e instanceof Error) { + err = e; + } else { + err = new Error("[non-error thrown]"); + } + } + setTimeout(() => { + if (err) { + cb(err); + } else { + cb(null, bytes); + } + }, 0); + } else { + return generateRandomBytes(size); + } +} diff --git a/ext/node/polyfills/internal/crypto/_randomFill.ts b/ext/node/polyfills/internal/crypto/_randomFill.ts new file mode 100644 index 0000000000..0450726968 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_randomFill.ts @@ -0,0 +1,84 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import randomBytes, { + MAX_SIZE as kMaxUint32, +} from "internal:deno_node/polyfills/internal/crypto/_randomBytes.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +const kBufferMaxLength = 0x7fffffff; + +function assertOffset(offset: number, length: number) { + if (offset > kMaxUint32 || offset < 0) { + throw new TypeError("offset must be a uint32"); + } + + if (offset > kBufferMaxLength || offset > length) { + throw new RangeError("offset out of range"); + } +} + +function assertSize(size: number, offset: number, length: number) { + if (size > kMaxUint32 || size < 0) { + throw new TypeError("size must be a uint32"); + } + + if (size + offset > length || size > kBufferMaxLength) { + throw new RangeError("buffer too small"); + } +} + +export default function randomFill( + buf: Buffer, + cb: (err: Error | null, buf: Buffer) => void, +): void; + +export default function randomFill( + buf: Buffer, + offset: number, + cb: (err: Error | null, buf: Buffer) => void, +): void; + +export default function randomFill( + buf: Buffer, + offset: number, + size: number, + cb: (err: Error | null, buf: Buffer) => void, +): void; + +export default function randomFill( + buf: Buffer, + offset?: number | ((err: Error | null, buf: Buffer) => void), + size?: number | ((err: Error | null, buf: Buffer) => void), + cb?: (err: Error | null, buf: Buffer) => void, +) { + if (typeof offset === "function") { + cb = offset; + offset = 0; + size = buf.length; + } else if (typeof size === "function") { + cb = size; + size = buf.length - Number(offset as number); + } + + assertOffset(offset as number, buf.length); + assertSize(size as number, offset as number, buf.length); + + randomBytes(size as number, (err, bytes) => { + if (err) return cb!(err, buf); + bytes?.copy(buf, offset as number); + cb!(null, buf); + }); +} + +export function randomFillSync(buf: Buffer, offset = 0, size?: number) { + assertOffset(offset, buf.length); + + if (size === undefined) size = buf.length - offset; + + assertSize(size, offset, buf.length); + + const bytes = randomBytes(size); + + bytes.copy(buf, offset); + + return buf; +} diff --git a/ext/node/polyfills/internal/crypto/_randomInt.ts b/ext/node/polyfills/internal/crypto/_randomInt.ts new file mode 100644 index 0000000000..6372515410 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/_randomInt.ts @@ -0,0 +1,60 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +export default function randomInt(max: number): number; +export default function randomInt(min: number, max: number): number; +export default function randomInt( + max: number, + cb: (err: Error | null, n?: number) => void, +): void; +export default function randomInt( + min: number, + max: number, + cb: (err: Error | null, n?: number) => void, +): void; + +export default function randomInt( + max: number, + min?: ((err: Error | null, n?: number) => void) | number, + cb?: (err: Error | null, n?: number) => void, +): number | void { + if (typeof max === "number" && typeof min === "number") { + [max, min] = [min, max]; + } + if (min === undefined) min = 0; + else if (typeof min === "function") { + cb = min; + min = 0; + } + + if ( + !Number.isSafeInteger(min) || + typeof max === "number" && !Number.isSafeInteger(max) + ) { + throw new Error("max or min is not a Safe Number"); + } + + if (max - min > Math.pow(2, 48)) { + throw new RangeError("max - min should be less than 2^48!"); + } + + if (min >= max) { + throw new Error("Min is bigger than Max!"); + } + + const randomBuffer = new Uint32Array(1); + + globalThis.crypto.getRandomValues(randomBuffer); + + const randomNumber = randomBuffer[0] / (0xffffffff + 1); + + min = Math.ceil(min); + max = Math.floor(max); + + const result = Math.floor(randomNumber * (max - min)) + min; + + if (cb) { + cb(null, result); + return; + } + + return result; +} diff --git a/ext/node/polyfills/internal/crypto/certificate.ts b/ext/node/polyfills/internal/crypto/certificate.ts new file mode 100644 index 0000000000..f6fb333a9c --- /dev/null +++ b/ext/node/polyfills/internal/crypto/certificate.ts @@ -0,0 +1,23 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { BinaryLike } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +export class Certificate { + static Certificate = Certificate; + static exportChallenge(_spkac: BinaryLike, _encoding?: string): Buffer { + notImplemented("crypto.Certificate.exportChallenge"); + } + + static exportPublicKey(_spkac: BinaryLike, _encoding?: string): Buffer { + notImplemented("crypto.Certificate.exportPublicKey"); + } + + static verifySpkac(_spkac: BinaryLike, _encoding?: string): boolean { + notImplemented("crypto.Certificate.verifySpkac"); + } +} + +export default Certificate; diff --git a/ext/node/polyfills/internal/crypto/cipher.ts b/ext/node/polyfills/internal/crypto/cipher.ts new file mode 100644 index 0000000000..2778b40fac --- /dev/null +++ b/ext/node/polyfills/internal/crypto/cipher.ts @@ -0,0 +1,292 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateInt32, + validateObject, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import type { TransformOptions } from "internal:deno_node/polyfills/_stream.d.ts"; +import { Transform } from "internal:deno_node/polyfills/_stream.mjs"; +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import type { BufferEncoding } from "internal:deno_node/polyfills/_global.d.ts"; +import type { + BinaryLike, + Encoding, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, +} from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/mod.js"; + +export { + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, +} from "internal:deno_node/polyfills/_crypto/crypto_browserify/public_encrypt/mod.js"; + +export type CipherCCMTypes = + | "aes-128-ccm" + | "aes-192-ccm" + | "aes-256-ccm" + | "chacha20-poly1305"; +export type CipherGCMTypes = "aes-128-gcm" | "aes-192-gcm" | "aes-256-gcm"; +export type CipherOCBTypes = "aes-128-ocb" | "aes-192-ocb" | "aes-256-ocb"; + +export type CipherKey = BinaryLike | KeyObject; + +export interface CipherCCMOptions extends TransformOptions { + authTagLength: number; +} + +export interface CipherGCMOptions extends TransformOptions { + authTagLength?: number | undefined; +} + +export interface CipherOCBOptions extends TransformOptions { + authTagLength: number; +} + +export interface Cipher extends ReturnType { + update(data: BinaryLike): Buffer; + update(data: string, inputEncoding: Encoding): Buffer; + update( + data: ArrayBufferView, + inputEncoding: undefined, + outputEncoding: Encoding, + ): string; + update( + data: string, + inputEncoding: Encoding | undefined, + outputEncoding: Encoding, + ): string; + + final(): Buffer; + final(outputEncoding: BufferEncoding): string; + + setAutoPadding(autoPadding?: boolean): this; +} + +export type Decipher = Cipher; + +export interface CipherCCM extends Cipher { + setAAD( + buffer: ArrayBufferView, + options: { + plaintextLength: number; + }, + ): this; + getAuthTag(): Buffer; +} + +export interface CipherGCM extends Cipher { + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; + getAuthTag(): Buffer; +} + +export interface CipherOCB extends Cipher { + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; + getAuthTag(): Buffer; +} + +export interface DecipherCCM extends Decipher { + setAuthTag(buffer: ArrayBufferView): this; + setAAD( + buffer: ArrayBufferView, + options: { + plaintextLength: number; + }, + ): this; +} + +export interface DecipherGCM extends Decipher { + setAuthTag(buffer: ArrayBufferView): this; + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; +} + +export interface DecipherOCB extends Decipher { + setAuthTag(buffer: ArrayBufferView): this; + setAAD( + buffer: ArrayBufferView, + options?: { + plaintextLength: number; + }, + ): this; +} + +export class Cipheriv extends Transform implements Cipher { + constructor( + _cipher: string, + _key: CipherKey, + _iv: BinaryLike | null, + _options?: TransformOptions, + ) { + super(); + + notImplemented("crypto.Cipheriv"); + } + + final(): Buffer; + final(outputEncoding: BufferEncoding): string; + final(_outputEncoding?: string): Buffer | string { + notImplemented("crypto.Cipheriv.prototype.final"); + } + + getAuthTag(): Buffer { + notImplemented("crypto.Cipheriv.prototype.getAuthTag"); + } + + setAAD( + _buffer: ArrayBufferView, + _options?: { + plaintextLength: number; + }, + ): this { + notImplemented("crypto.Cipheriv.prototype.setAAD"); + } + + setAutoPadding(_autoPadding?: boolean): this { + notImplemented("crypto.Cipheriv.prototype.setAutoPadding"); + } + + update(data: BinaryLike): Buffer; + update(data: string, inputEncoding: Encoding): Buffer; + update( + data: ArrayBufferView, + inputEncoding: undefined, + outputEncoding: Encoding, + ): string; + update( + data: string, + inputEncoding: Encoding | undefined, + outputEncoding: Encoding, + ): string; + update( + _data: string | BinaryLike | ArrayBufferView, + _inputEncoding?: Encoding, + _outputEncoding?: Encoding, + ): Buffer | string { + notImplemented("crypto.Cipheriv.prototype.update"); + } +} + +export class Decipheriv extends Transform implements Cipher { + constructor( + _cipher: string, + _key: CipherKey, + _iv: BinaryLike | null, + _options?: TransformOptions, + ) { + super(); + + notImplemented("crypto.Decipheriv"); + } + + final(): Buffer; + final(outputEncoding: BufferEncoding): string; + final(_outputEncoding?: string): Buffer | string { + notImplemented("crypto.Decipheriv.prototype.final"); + } + + setAAD( + _buffer: ArrayBufferView, + _options?: { + plaintextLength: number; + }, + ): this { + notImplemented("crypto.Decipheriv.prototype.setAAD"); + } + + setAuthTag(_buffer: BinaryLike, _encoding?: string): this { + notImplemented("crypto.Decipheriv.prototype.setAuthTag"); + } + + setAutoPadding(_autoPadding?: boolean): this { + notImplemented("crypto.Decipheriv.prototype.setAutoPadding"); + } + + update(data: BinaryLike): Buffer; + update(data: string, inputEncoding: Encoding): Buffer; + update( + data: ArrayBufferView, + inputEncoding: undefined, + outputEncoding: Encoding, + ): string; + update( + data: string, + inputEncoding: Encoding | undefined, + outputEncoding: Encoding, + ): string; + update( + _data: string | BinaryLike | ArrayBufferView, + _inputEncoding?: Encoding, + _outputEncoding?: Encoding, + ): Buffer | string { + notImplemented("crypto.Decipheriv.prototype.update"); + } +} + +export function getCipherInfo( + nameOrNid: string | number, + options?: { keyLength?: number; ivLength?: number }, +) { + if (typeof nameOrNid !== "string" && typeof nameOrNid !== "number") { + throw new ERR_INVALID_ARG_TYPE( + "nameOrNid", + ["string", "number"], + nameOrNid, + ); + } + + if (typeof nameOrNid === "number") { + validateInt32(nameOrNid, "nameOrNid"); + } + + let keyLength, ivLength; + + if (options !== undefined) { + validateObject(options, "options"); + + ({ keyLength, ivLength } = options); + + if (keyLength !== undefined) { + validateInt32(keyLength, "options.keyLength"); + } + + if (ivLength !== undefined) { + validateInt32(ivLength, "options.ivLength"); + } + } + + notImplemented("crypto.getCipherInfo"); +} + +export default { + privateDecrypt, + privateEncrypt, + publicDecrypt, + publicEncrypt, + Cipheriv, + Decipheriv, + getCipherInfo, +}; diff --git a/ext/node/polyfills/internal/crypto/constants.ts b/ext/node/polyfills/internal/crypto/constants.ts new file mode 100644 index 0000000000..d624153601 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/constants.ts @@ -0,0 +1,5 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +export const kHandle = Symbol("kHandle"); +export const kKeyObject = Symbol("kKeyObject"); diff --git a/ext/node/polyfills/internal/crypto/diffiehellman.ts b/ext/node/polyfills/internal/crypto/diffiehellman.ts new file mode 100644 index 0000000000..eb903ccb3f --- /dev/null +++ b/ext/node/polyfills/internal/crypto/diffiehellman.ts @@ -0,0 +1,306 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateInt32, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + getDefaultEncoding, + toBuf, +} from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import type { + BinaryLike, + BinaryToTextEncoding, + ECDHKeyFormat, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import type { BufferEncoding } from "internal:deno_node/polyfills/_global.d.ts"; + +const DH_GENERATOR = 2; + +export class DiffieHellman { + verifyError!: number; + + constructor( + sizeOrKey: unknown, + keyEncoding?: unknown, + generator?: unknown, + genEncoding?: unknown, + ) { + if ( + typeof sizeOrKey !== "number" && + typeof sizeOrKey !== "string" && + !isArrayBufferView(sizeOrKey) && + !isAnyArrayBuffer(sizeOrKey) + ) { + throw new ERR_INVALID_ARG_TYPE( + "sizeOrKey", + ["number", "string", "ArrayBuffer", "Buffer", "TypedArray", "DataView"], + sizeOrKey, + ); + } + + if (typeof sizeOrKey === "number") { + validateInt32(sizeOrKey, "sizeOrKey"); + } + + if ( + keyEncoding && + !Buffer.isEncoding(keyEncoding as BinaryToTextEncoding) && + keyEncoding !== "buffer" + ) { + genEncoding = generator; + generator = keyEncoding; + keyEncoding = false; + } + + const encoding = getDefaultEncoding(); + keyEncoding = keyEncoding || encoding; + genEncoding = genEncoding || encoding; + + if (typeof sizeOrKey !== "number") { + sizeOrKey = toBuf(sizeOrKey as string, keyEncoding as string); + } + + if (!generator) { + generator = DH_GENERATOR; + } else if (typeof generator === "number") { + validateInt32(generator, "generator"); + } else if (typeof generator === "string") { + generator = toBuf(generator, genEncoding as string); + } else if (!isArrayBufferView(generator) && !isAnyArrayBuffer(generator)) { + throw new ERR_INVALID_ARG_TYPE( + "generator", + ["number", "string", "ArrayBuffer", "Buffer", "TypedArray", "DataView"], + generator, + ); + } + + notImplemented("crypto.DiffieHellman"); + } + + computeSecret(otherPublicKey: ArrayBufferView): Buffer; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + ): Buffer; + computeSecret( + otherPublicKey: ArrayBufferView, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + _otherPublicKey: ArrayBufferView | string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.computeSecret"); + } + + generateKeys(): Buffer; + generateKeys(encoding: BinaryToTextEncoding): string; + generateKeys(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.generateKeys"); + } + + getGenerator(): Buffer; + getGenerator(encoding: BinaryToTextEncoding): string; + getGenerator(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getGenerator"); + } + + getPrime(): Buffer; + getPrime(encoding: BinaryToTextEncoding): string; + getPrime(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrime"); + } + + getPrivateKey(): Buffer; + getPrivateKey(encoding: BinaryToTextEncoding): string; + getPrivateKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrivateKey"); + } + + getPublicKey(): Buffer; + getPublicKey(encoding: BinaryToTextEncoding): string; + getPublicKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPublicKey"); + } + + setPrivateKey(privateKey: ArrayBufferView): void; + setPrivateKey(privateKey: string, encoding: BufferEncoding): void; + setPrivateKey( + _privateKey: ArrayBufferView | string, + _encoding?: BufferEncoding, + ) { + notImplemented("crypto.DiffieHellman.prototype.setPrivateKey"); + } + + setPublicKey(publicKey: ArrayBufferView): void; + setPublicKey(publicKey: string, encoding: BufferEncoding): void; + setPublicKey( + _publicKey: ArrayBufferView | string, + _encoding?: BufferEncoding, + ) { + notImplemented("crypto.DiffieHellman.prototype.setPublicKey"); + } +} + +export class DiffieHellmanGroup { + verifyError!: number; + + constructor(_name: string) { + notImplemented("crypto.DiffieHellmanGroup"); + } + + computeSecret(otherPublicKey: ArrayBufferView): Buffer; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + ): Buffer; + computeSecret( + otherPublicKey: ArrayBufferView, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + _otherPublicKey: ArrayBufferView | string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.computeSecret"); + } + + generateKeys(): Buffer; + generateKeys(encoding: BinaryToTextEncoding): string; + generateKeys(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.generateKeys"); + } + + getGenerator(): Buffer; + getGenerator(encoding: BinaryToTextEncoding): string; + getGenerator(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getGenerator"); + } + + getPrime(): Buffer; + getPrime(encoding: BinaryToTextEncoding): string; + getPrime(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrime"); + } + + getPrivateKey(): Buffer; + getPrivateKey(encoding: BinaryToTextEncoding): string; + getPrivateKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPrivateKey"); + } + + getPublicKey(): Buffer; + getPublicKey(encoding: BinaryToTextEncoding): string; + getPublicKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.DiffieHellman.prototype.getPublicKey"); + } +} + +export class ECDH { + constructor(curve: string) { + validateString(curve, "curve"); + + notImplemented("crypto.ECDH"); + } + + static convertKey( + _key: BinaryLike, + _curve: string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: "latin1" | "hex" | "base64" | "base64url", + _format?: "uncompressed" | "compressed" | "hybrid", + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.convertKey"); + } + + computeSecret(otherPublicKey: ArrayBufferView): Buffer; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + ): Buffer; + computeSecret( + otherPublicKey: ArrayBufferView, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + otherPublicKey: string, + inputEncoding: BinaryToTextEncoding, + outputEncoding: BinaryToTextEncoding, + ): string; + computeSecret( + _otherPublicKey: ArrayBufferView | string, + _inputEncoding?: BinaryToTextEncoding, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.computeSecret"); + } + + generateKeys(): Buffer; + generateKeys(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string; + generateKeys( + _encoding?: BinaryToTextEncoding, + _format?: ECDHKeyFormat, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.generateKeys"); + } + + getPrivateKey(): Buffer; + getPrivateKey(encoding: BinaryToTextEncoding): string; + getPrivateKey(_encoding?: BinaryToTextEncoding): Buffer | string { + notImplemented("crypto.ECDH.prototype.getPrivateKey"); + } + + getPublicKey(): Buffer; + getPublicKey(encoding: BinaryToTextEncoding, format?: ECDHKeyFormat): string; + getPublicKey( + _encoding?: BinaryToTextEncoding, + _format?: ECDHKeyFormat, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.getPublicKey"); + } + + setPrivateKey(privateKey: ArrayBufferView): void; + setPrivateKey(privateKey: string, encoding: BinaryToTextEncoding): void; + setPrivateKey( + _privateKey: ArrayBufferView | string, + _encoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.ECDH.prototype.setPrivateKey"); + } +} + +export function diffieHellman(_options: { + privateKey: KeyObject; + publicKey: KeyObject; +}): Buffer { + notImplemented("crypto.diffieHellman"); +} + +export default { + DiffieHellman, + DiffieHellmanGroup, + ECDH, + diffieHellman, +}; diff --git a/ext/node/polyfills/internal/crypto/hash.ts b/ext/node/polyfills/internal/crypto/hash.ts new file mode 100644 index 0000000000..7995e5f8c4 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/hash.ts @@ -0,0 +1,230 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { + TextDecoder, + TextEncoder, +} from "internal:deno_web/08_text_encoding.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { Transform } from "internal:deno_node/polyfills/stream.ts"; +import { encode as encodeToHex } from "internal:deno_node/polyfills/internal/crypto/_hex.ts"; +import { + forgivingBase64Encode as encodeToBase64, + forgivingBase64UrlEncode as encodeToBase64Url, +} from "internal:deno_web/00_infra.js"; +import type { TransformOptions } from "internal:deno_node/polyfills/_stream.d.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import type { + BinaryToTextEncoding, + Encoding, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { + KeyObject, + prepareSecretKey, +} from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +const { ops } = globalThis.__bootstrap.core; + +const coerceToBytes = (data: string | BufferSource): Uint8Array => { + if (data instanceof Uint8Array) { + return data; + } else if (typeof data === "string") { + // This assumes UTF-8, which may not be correct. + return new TextEncoder().encode(data); + } else if (ArrayBuffer.isView(data)) { + return new Uint8Array(data.buffer, data.byteOffset, data.byteLength); + } else if (data instanceof ArrayBuffer) { + return new Uint8Array(data); + } else { + throw new TypeError("expected data to be string | BufferSource"); + } +}; + +/** + * The Hash class is a utility for creating hash digests of data. It can be used in one of two ways: + * + * - As a stream that is both readable and writable, where data is written to produce a computed hash digest on the readable side, or + * - Using the hash.update() and hash.digest() methods to produce the computed hash. + * + * The crypto.createHash() method is used to create Hash instances. Hash objects are not to be created directly using the new keyword. + */ +export class Hash extends Transform { + #context: number; + + constructor( + algorithm: string | number, + _opts?: TransformOptions, + ) { + super({ + transform(chunk: string, _encoding: string, callback: () => void) { + ops.op_node_hash_update(context, coerceToBytes(chunk)); + callback(); + }, + flush(callback: () => void) { + this.push(context.digest(undefined)); + callback(); + }, + }); + + if (typeof algorithm === "string") { + this.#context = ops.op_node_create_hash( + algorithm, + ); + } else { + this.#context = algorithm; + } + + const context = this.#context; + } + + copy(): Hash { + return new Hash(ops.op_node_clone_hash(this.#context)); + } + + /** + * Updates the hash content with the given data. + */ + update(data: string | ArrayBuffer, _encoding?: string): this { + let bytes; + if (typeof data === "string") { + data = new TextEncoder().encode(data); + bytes = coerceToBytes(data); + } else { + bytes = coerceToBytes(data); + } + + ops.op_node_hash_update(this.#context, bytes); + + return this; + } + + /** + * Calculates the digest of all of the data. + * + * If encoding is provided a string will be returned; otherwise a Buffer is returned. + * + * Supported encodings are currently 'hex', 'binary', 'base64', 'base64url'. + */ + digest(encoding?: string): Buffer | string { + const digest = this.#context.digest(undefined); + if (encoding === undefined) { + return Buffer.from(digest); + } + + switch (encoding) { + case "hex": + return new TextDecoder().decode(encodeToHex(new Uint8Array(digest))); + case "binary": + return String.fromCharCode(...digest); + case "base64": + return encodeToBase64(digest); + case "base64url": + return encodeToBase64Url(digest); + case "buffer": + return Buffer.from(digest); + default: + return Buffer.from(digest).toString(encoding); + } + } +} + +export function Hmac( + hmac: string, + key: string | ArrayBuffer | KeyObject, + options?: TransformOptions, +): Hmac { + return new HmacImpl(hmac, key, options); +} + +type Hmac = HmacImpl; + +class HmacImpl extends Transform { + #ipad: Uint8Array; + #opad: Uint8Array; + #ZEROES = Buffer.alloc(128); + #algorithm: string; + #hash: Hash; + + constructor( + hmac: string, + key: string | ArrayBuffer | KeyObject, + options?: TransformOptions, + ) { + super({ + transform(chunk: string, encoding: string, callback: () => void) { + // deno-lint-ignore no-explicit-any + self.update(coerceToBytes(chunk), encoding as any); + callback(); + }, + flush(callback: () => void) { + this.push(self.digest()); + callback(); + }, + }); + // deno-lint-ignore no-this-alias + const self = this; + if (key instanceof KeyObject) { + notImplemented("Hmac: KeyObject key is not implemented"); + } + + validateString(hmac, "hmac"); + const u8Key = prepareSecretKey(key, options?.encoding) as Buffer; + + const alg = hmac.toLowerCase(); + this.#hash = new Hash(alg, options); + this.#algorithm = alg; + const blockSize = (alg === "sha512" || alg === "sha384") ? 128 : 64; + const keySize = u8Key.length; + + let bufKey: Buffer; + + if (keySize > blockSize) { + bufKey = this.#hash.update(u8Key).digest() as Buffer; + } else { + bufKey = Buffer.concat([u8Key, this.#ZEROES], blockSize); + } + + this.#ipad = Buffer.allocUnsafe(blockSize); + this.#opad = Buffer.allocUnsafe(blockSize); + + for (let i = 0; i < blockSize; i++) { + this.#ipad[i] = bufKey[i] ^ 0x36; + this.#opad[i] = bufKey[i] ^ 0x5C; + } + + this.#hash = new Hash(alg); + this.#hash.update(this.#ipad); + } + + digest(): Buffer; + digest(encoding: BinaryToTextEncoding): string; + digest(encoding?: BinaryToTextEncoding): Buffer | string { + const result = this.#hash.digest(); + + return new Hash(this.#algorithm).update(this.#opad).update(result).digest( + encoding, + ); + } + + update(data: string | ArrayBuffer, inputEncoding?: Encoding): this { + this.#hash.update(data, inputEncoding); + return this; + } +} + +Hmac.prototype = HmacImpl.prototype; + +/** + * Creates and returns a Hash object that can be used to generate hash digests + * using the given `algorithm`. Optional `options` argument controls stream behavior. + */ +export function createHash(algorithm: string, opts?: TransformOptions) { + return new Hash(algorithm, opts); +} + +export default { + Hash, + Hmac, + createHash, +}; diff --git a/ext/node/polyfills/internal/crypto/hkdf.ts b/ext/node/polyfills/internal/crypto/hkdf.ts new file mode 100644 index 0000000000..aebdd9152f --- /dev/null +++ b/ext/node/polyfills/internal/crypto/hkdf.ts @@ -0,0 +1,130 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { + validateFunction, + validateInteger, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + ERR_INVALID_ARG_TYPE, + ERR_OUT_OF_RANGE, + hideStackFrames, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + toBuf, + validateByteSource, +} from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import { + createSecretKey, + isKeyObject, + KeyObject, +} from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import type { BinaryLike } from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { kMaxLength } from "internal:deno_node/polyfills/internal/buffer.mjs"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +const validateParameters = hideStackFrames((hash, key, salt, info, length) => { + key = prepareKey(key); + salt = toBuf(salt); + info = toBuf(info); + + validateString(hash, "digest"); + validateByteSource(salt, "salt"); + validateByteSource(info, "info"); + + validateInteger(length, "length", 0, kMaxLength); + + if (info.byteLength > 1024) { + throw new ERR_OUT_OF_RANGE( + "info", + "must not contain more than 1024 bytes", + info.byteLength, + ); + } + + return { + hash, + key, + salt, + info, + length, + }; +}); + +function prepareKey(key: BinaryLike | KeyObject) { + if (isKeyObject(key)) { + return key; + } + + if (isAnyArrayBuffer(key)) { + return createSecretKey(new Uint8Array(key as unknown as ArrayBufferLike)); + } + + key = toBuf(key as string); + + if (!isArrayBufferView(key)) { + throw new ERR_INVALID_ARG_TYPE( + "ikm", + [ + "string", + "SecretKeyObject", + "ArrayBuffer", + "TypedArray", + "DataView", + "Buffer", + ], + key, + ); + } + + return createSecretKey(key); +} + +export function hkdf( + hash: string, + key: BinaryLike | KeyObject, + salt: BinaryLike, + info: BinaryLike, + length: number, + callback: (err: Error | null, derivedKey: ArrayBuffer) => void, +) { + ({ hash, key, salt, info, length } = validateParameters( + hash, + key, + salt, + info, + length, + )); + + validateFunction(callback, "callback"); + + notImplemented("crypto.hkdf"); +} + +export function hkdfSync( + hash: string, + key: BinaryLike | KeyObject, + salt: BinaryLike, + info: BinaryLike, + length: number, +) { + ({ hash, key, salt, info, length } = validateParameters( + hash, + key, + salt, + info, + length, + )); + + notImplemented("crypto.hkdfSync"); +} + +export default { + hkdf, + hkdfSync, +}; diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts new file mode 100644 index 0000000000..1a947b95ba --- /dev/null +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -0,0 +1,682 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + KeyFormat, + KeyType, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +export function generateKey( + _type: "hmac" | "aes", + _options: { + length: number; + }, + _callback: (err: Error | null, key: KeyObject) => void, +) { + notImplemented("crypto.generateKey"); +} + +export interface BasePrivateKeyEncodingOptions { + format: T; + cipher?: string | undefined; + passphrase?: string | undefined; +} + +export interface RSAKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; + publicKeyEncoding: { + type: "pkcs1" | "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "pkcs1" | "pkcs8"; + }; +} + +export interface RSAPSSKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; + /** + * Name of the message digest + */ + hashAlgorithm?: string; + /** + * Name of the message digest used by MGF1 + */ + mgf1HashAlgorithm?: string; + /** + * Minimal salt length in bytes + */ + saltLength?: string; + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "pkcs8"; + }; +} + +export interface DSAKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Size of q in bits + */ + divisorLength: number; + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "pkcs8"; + }; +} + +export interface ECKeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + /** + * Name of the curve to use. + */ + namedCurve: string; + publicKeyEncoding: { + type: "pkcs1" | "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "sec1" | "pkcs8"; + }; +} + +export interface ED25519KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "pkcs8"; + }; +} + +export interface ED448KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "pkcs8"; + }; +} + +export interface X25519KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "pkcs8"; + }; +} + +export interface X448KeyPairOptions< + PubF extends KeyFormat, + PrivF extends KeyFormat, +> { + publicKeyEncoding: { + type: "spki"; + format: PubF; + }; + privateKeyEncoding: BasePrivateKeyEncodingOptions & { + type: "pkcs8"; + }; +} + +export interface RSAKeyPairKeyObjectOptions { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; +} + +export interface RSAPSSKeyPairKeyObjectOptions { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Public exponent + * @default 0x10001 + */ + publicExponent?: number | undefined; + /** + * Name of the message digest + */ + hashAlgorithm?: string; + /** + * Name of the message digest used by MGF1 + */ + mgf1HashAlgorithm?: string; + /** + * Minimal salt length in bytes + */ + saltLength?: string; +} + +export interface DSAKeyPairKeyObjectOptions { + /** + * Key size in bits + */ + modulusLength: number; + /** + * Size of q in bits + */ + divisorLength: number; +} + +// deno-lint-ignore no-empty-interface +export interface ED25519KeyPairKeyObjectOptions {} + +// deno-lint-ignore no-empty-interface +export interface ED448KeyPairKeyObjectOptions {} + +// deno-lint-ignore no-empty-interface +export interface X25519KeyPairKeyObjectOptions {} + +// deno-lint-ignore no-empty-interface +export interface X448KeyPairKeyObjectOptions {} + +export interface ECKeyPairKeyObjectOptions { + /** + * Name of the curve to use + */ + namedCurve: string; +} + +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa", + options: RSAKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "rsa-pss", + options: RSAPSSKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "dsa", + options: DSAKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ec", + options: ECKeyPairKeyObjectOptions, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed25519", + options: ED25519KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "ed448", + options: ED448KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x25519", + options: X25519KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"pem", "pem">, + callback: (err: Error | null, publicKey: string, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"pem", "der">, + callback: (err: Error | null, publicKey: string, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"der", "pem">, + callback: (err: Error | null, publicKey: Buffer, privateKey: string) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairOptions<"der", "der">, + callback: (err: Error | null, publicKey: Buffer, privateKey: Buffer) => void, +): void; +export function generateKeyPair( + type: "x448", + options: X448KeyPairKeyObjectOptions | undefined, + callback: ( + err: Error | null, + publicKey: KeyObject, + privateKey: KeyObject, + ) => void, +): void; +export function generateKeyPair( + _type: KeyType, + _options: unknown, + _callback: ( + err: Error | null, + // deno-lint-ignore no-explicit-any + publicKey: any, + // deno-lint-ignore no-explicit-any + privateKey: any, + ) => void, +) { + notImplemented("crypto.generateKeyPair"); +} + +export interface KeyPairKeyObjectResult { + publicKey: KeyObject; + privateKey: KeyObject; +} + +export interface KeyPairSyncResult< + T1 extends string | Buffer, + T2 extends string | Buffer, +> { + publicKey: T1; + privateKey: T2; +} + +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa", + options: RSAKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "rsa-pss", + options: RSAPSSKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "dsa", + options: DSAKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ec", + options: ECKeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed25519", + options: ED25519KeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed25519", + options?: ED25519KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed448", + options: ED448KeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "ed448", + options?: ED448KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x25519", + options: X25519KeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x25519", + options?: X25519KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"pem", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"pem", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"der", "pem">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x448", + options: X448KeyPairOptions<"der", "der">, +): KeyPairSyncResult; +export function generateKeyPairSync( + type: "x448", + options?: X448KeyPairKeyObjectOptions, +): KeyPairKeyObjectResult; +export function generateKeyPairSync( + _type: KeyType, + _options: unknown, +): + | KeyPairKeyObjectResult + | KeyPairSyncResult { + notImplemented("crypto.generateKeyPairSync"); +} + +export function generateKeySync( + _type: "hmac" | "aes", + _options: { + length: number; + }, +): KeyObject { + notImplemented("crypto.generateKeySync"); +} + +export default { + generateKey, + generateKeySync, + generateKeyPair, + generateKeyPairSync, +}; diff --git a/ext/node/polyfills/internal/crypto/keys.ts b/ext/node/polyfills/internal/crypto/keys.ts new file mode 100644 index 0000000000..7c9e7bad95 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/keys.ts @@ -0,0 +1,294 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { + kHandle, + kKeyObject, +} from "internal:deno_node/polyfills/internal/crypto/constants.ts"; +import { + ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import type { + KeyFormat, + KeyType, + PrivateKeyInput, + PublicKeyInput, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { hideStackFrames } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + isCryptoKey as isCryptoKey_, + isKeyObject as isKeyObject_, + kKeyType, +} from "internal:deno_node/polyfills/internal/crypto/_keys.ts"; + +const getArrayBufferOrView = hideStackFrames( + ( + buffer, + name, + encoding, + ): + | ArrayBuffer + | SharedArrayBuffer + | Buffer + | DataView + | BigInt64Array + | BigUint64Array + | Float32Array + | Float64Array + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array => { + if (isAnyArrayBuffer(buffer)) { + return buffer; + } + if (typeof buffer === "string") { + if (encoding === "buffer") { + encoding = "utf8"; + } + return Buffer.from(buffer, encoding); + } + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + name, + [ + "string", + "ArrayBuffer", + "Buffer", + "TypedArray", + "DataView", + ], + buffer, + ); + } + return buffer; + }, +); + +export interface AsymmetricKeyDetails { + /** + * Key size in bits (RSA, DSA). + */ + modulusLength?: number | undefined; + /** + * Public exponent (RSA). + */ + publicExponent?: bigint | undefined; + /** + * Name of the message digest (RSA-PSS). + */ + hashAlgorithm?: string | undefined; + /** + * Name of the message digest used by MGF1 (RSA-PSS). + */ + mgf1HashAlgorithm?: string | undefined; + /** + * Minimal salt length in bytes (RSA-PSS). + */ + saltLength?: number | undefined; + /** + * Size of q in bits (DSA). + */ + divisorLength?: number | undefined; + /** + * Name of the curve (EC). + */ + namedCurve?: string | undefined; +} + +export type KeyObjectType = "secret" | "public" | "private"; + +export interface KeyExportOptions { + type: "pkcs1" | "spki" | "pkcs8" | "sec1"; + format: T; + cipher?: string | undefined; + passphrase?: string | Buffer | undefined; +} + +export interface JwkKeyExportOptions { + format: "jwk"; +} + +export function isKeyObject(obj: unknown): obj is KeyObject { + return isKeyObject_(obj); +} + +export function isCryptoKey( + obj: unknown, +): obj is { type: string; [kKeyObject]: KeyObject } { + return isCryptoKey_(obj); +} + +export class KeyObject { + [kKeyType]: KeyObjectType; + [kHandle]: unknown; + + constructor(type: KeyObjectType, handle: unknown) { + if (type !== "secret" && type !== "public" && type !== "private") { + throw new ERR_INVALID_ARG_VALUE("type", type); + } + + if (typeof handle !== "object") { + throw new ERR_INVALID_ARG_TYPE("handle", "object", handle); + } + + this[kKeyType] = type; + + Object.defineProperty(this, kHandle, { + value: handle, + enumerable: false, + configurable: false, + writable: false, + }); + } + + get type(): KeyObjectType { + return this[kKeyType]; + } + + get asymmetricKeyDetails(): AsymmetricKeyDetails | undefined { + notImplemented("crypto.KeyObject.prototype.asymmetricKeyDetails"); + + return undefined; + } + + get asymmetricKeyType(): KeyType | undefined { + notImplemented("crypto.KeyObject.prototype.asymmetricKeyType"); + + return undefined; + } + + get symmetricKeySize(): number | undefined { + notImplemented("crypto.KeyObject.prototype.symmetricKeySize"); + + return undefined; + } + + static from(key: CryptoKey): KeyObject { + if (!isCryptoKey(key)) { + throw new ERR_INVALID_ARG_TYPE("key", "CryptoKey", key); + } + + notImplemented("crypto.KeyObject.prototype.from"); + } + + equals(otherKeyObject: KeyObject): boolean { + if (!isKeyObject(otherKeyObject)) { + throw new ERR_INVALID_ARG_TYPE( + "otherKeyObject", + "KeyObject", + otherKeyObject, + ); + } + + notImplemented("crypto.KeyObject.prototype.equals"); + } + + export(options: KeyExportOptions<"pem">): string | Buffer; + export(options?: KeyExportOptions<"der">): Buffer; + export(options?: JwkKeyExportOptions): JsonWebKey; + export(_options?: unknown): string | Buffer | JsonWebKey { + notImplemented("crypto.KeyObject.prototype.asymmetricKeyType"); + } +} + +export interface JsonWebKeyInput { + key: JsonWebKey; + format: "jwk"; +} + +export function createPrivateKey( + _key: PrivateKeyInput | string | Buffer | JsonWebKeyInput, +): KeyObject { + notImplemented("crypto.createPrivateKey"); +} + +export function createPublicKey( + _key: PublicKeyInput | string | Buffer | KeyObject | JsonWebKeyInput, +): KeyObject { + notImplemented("crypto.createPublicKey"); +} + +function getKeyTypes(allowKeyObject: boolean, bufferOnly = false) { + const types = [ + "ArrayBuffer", + "Buffer", + "TypedArray", + "DataView", + "string", // Only if bufferOnly == false + "KeyObject", // Only if allowKeyObject == true && bufferOnly == false + "CryptoKey", // Only if allowKeyObject == true && bufferOnly == false + ]; + if (bufferOnly) { + return types.slice(0, 4); + } else if (!allowKeyObject) { + return types.slice(0, 5); + } + return types; +} + +export function prepareSecretKey( + key: string | ArrayBuffer | KeyObject, + encoding: string | undefined, + bufferOnly = false, +) { + if (!bufferOnly) { + if (isKeyObject(key)) { + if (key.type !== "secret") { + throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, "secret"); + } + return key[kHandle]; + } else if (isCryptoKey(key)) { + if (key.type !== "secret") { + throw new ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE(key.type, "secret"); + } + return key[kKeyObject][kHandle]; + } + } + if ( + typeof key !== "string" && + !isArrayBufferView(key) && + !isAnyArrayBuffer(key) + ) { + throw new ERR_INVALID_ARG_TYPE( + "key", + getKeyTypes(!bufferOnly, bufferOnly), + key, + ); + } + + return getArrayBufferOrView(key, "key", encoding); +} + +export function createSecretKey(key: ArrayBufferView): KeyObject; +export function createSecretKey( + key: string, + encoding: string, +): KeyObject; +export function createSecretKey( + _key: string | ArrayBufferView, + _encoding?: string, +): KeyObject { + notImplemented("crypto.createSecretKey"); +} + +export default { + createPrivateKey, + createPublicKey, + createSecretKey, + isKeyObject, + isCryptoKey, + KeyObject, + prepareSecretKey, +}; diff --git a/ext/node/polyfills/internal/crypto/pbkdf2.ts b/ext/node/polyfills/internal/crypto/pbkdf2.ts new file mode 100644 index 0000000000..a3d821ae79 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/pbkdf2.ts @@ -0,0 +1,183 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { createHash } from "internal:deno_node/polyfills/internal/crypto/hash.ts"; +import { HASH_DATA } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +export const MAX_ALLOC = Math.pow(2, 30) - 1; + +export type NormalizedAlgorithms = + | "md5" + | "ripemd160" + | "sha1" + | "sha224" + | "sha256" + | "sha384" + | "sha512"; + +export type Algorithms = + | "md5" + | "ripemd160" + | "rmd160" + | "sha1" + | "sha224" + | "sha256" + | "sha384" + | "sha512"; + +const createHasher = (algorithm: string) => (value: Uint8Array) => + Buffer.from(createHash(algorithm).update(value).digest() as Buffer); + +function getZeroes(zeros: number) { + return Buffer.alloc(zeros); +} + +const sizes = { + md5: 16, + sha1: 20, + sha224: 28, + sha256: 32, + sha384: 48, + sha512: 64, + rmd160: 20, + ripemd160: 20, +}; + +function toBuffer(bufferable: HASH_DATA) { + if (bufferable instanceof Uint8Array || typeof bufferable === "string") { + return Buffer.from(bufferable as Uint8Array); + } else { + return Buffer.from(bufferable.buffer); + } +} + +export class Hmac { + hash: (value: Uint8Array) => Buffer; + ipad1: Buffer; + opad: Buffer; + alg: string; + blocksize: number; + size: number; + ipad2: Buffer; + + constructor(alg: Algorithms, key: Buffer, saltLen: number) { + this.hash = createHasher(alg); + + const blocksize = alg === "sha512" || alg === "sha384" ? 128 : 64; + + if (key.length > blocksize) { + key = this.hash(key); + } else if (key.length < blocksize) { + key = Buffer.concat([key, getZeroes(blocksize - key.length)], blocksize); + } + + const ipad = Buffer.allocUnsafe(blocksize + sizes[alg]); + const opad = Buffer.allocUnsafe(blocksize + sizes[alg]); + for (let i = 0; i < blocksize; i++) { + ipad[i] = key[i] ^ 0x36; + opad[i] = key[i] ^ 0x5c; + } + + const ipad1 = Buffer.allocUnsafe(blocksize + saltLen + 4); + ipad.copy(ipad1, 0, 0, blocksize); + + this.ipad1 = ipad1; + this.ipad2 = ipad; + this.opad = opad; + this.alg = alg; + this.blocksize = blocksize; + this.size = sizes[alg]; + } + + run(data: Buffer, ipad: Buffer) { + data.copy(ipad, this.blocksize); + const h = this.hash(ipad); + h.copy(this.opad, this.blocksize); + return this.hash(this.opad); + } +} + +/** + * @param iterations Needs to be higher or equal than zero + * @param keylen Needs to be higher or equal than zero but less than max allocation size (2^30) + * @param digest Algorithm to be used for encryption + */ +export function pbkdf2Sync( + password: HASH_DATA, + salt: HASH_DATA, + iterations: number, + keylen: number, + digest: Algorithms = "sha1", +): Buffer { + if (typeof iterations !== "number" || iterations < 0) { + throw new TypeError("Bad iterations"); + } + if (typeof keylen !== "number" || keylen < 0 || keylen > MAX_ALLOC) { + throw new TypeError("Bad key length"); + } + + const bufferedPassword = toBuffer(password); + const bufferedSalt = toBuffer(salt); + + const hmac = new Hmac(digest, bufferedPassword, bufferedSalt.length); + + const DK = Buffer.allocUnsafe(keylen); + const block1 = Buffer.allocUnsafe(bufferedSalt.length + 4); + bufferedSalt.copy(block1, 0, 0, bufferedSalt.length); + + let destPos = 0; + const hLen = sizes[digest]; + const l = Math.ceil(keylen / hLen); + + for (let i = 1; i <= l; i++) { + block1.writeUInt32BE(i, bufferedSalt.length); + + const T = hmac.run(block1, hmac.ipad1); + let U = T; + + for (let j = 1; j < iterations; j++) { + U = hmac.run(U, hmac.ipad2); + for (let k = 0; k < hLen; k++) T[k] ^= U[k]; + } + + T.copy(DK, destPos); + destPos += hLen; + } + + return DK; +} + +/** + * @param iterations Needs to be higher or equal than zero + * @param keylen Needs to be higher or equal than zero but less than max allocation size (2^30) + * @param digest Algorithm to be used for encryption + */ +export function pbkdf2( + password: HASH_DATA, + salt: HASH_DATA, + iterations: number, + keylen: number, + digest: Algorithms = "sha1", + callback: (err: Error | null, derivedKey?: Buffer) => void, +) { + setTimeout(() => { + let err = null, + res; + try { + res = pbkdf2Sync(password, salt, iterations, keylen, digest); + } catch (e) { + err = e; + } + if (err) { + callback(err instanceof Error ? err : new Error("[non-error thrown]")); + } else { + callback(null, res); + } + }, 0); +} + +export default { + Hmac, + MAX_ALLOC, + pbkdf2, + pbkdf2Sync, +}; diff --git a/ext/node/polyfills/internal/crypto/random.ts b/ext/node/polyfills/internal/crypto/random.ts new file mode 100644 index 0000000000..158ea40dac --- /dev/null +++ b/ext/node/polyfills/internal/crypto/random.ts @@ -0,0 +1,140 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import randomBytes from "internal:deno_node/polyfills/internal/crypto/_randomBytes.ts"; +import randomFill, { + randomFillSync, +} from "internal:deno_node/polyfills/internal/crypto/_randomFill.ts"; +import randomInt from "internal:deno_node/polyfills/internal/crypto/_randomInt.ts"; + +export { default as randomBytes } from "internal:deno_node/polyfills/internal/crypto/_randomBytes.ts"; +export { + default as randomFill, + randomFillSync, +} from "internal:deno_node/polyfills/internal/crypto/_randomFill.ts"; +export { default as randomInt } from "internal:deno_node/polyfills/internal/crypto/_randomInt.ts"; + +export type LargeNumberLike = + | ArrayBufferView + | SharedArrayBuffer + | ArrayBuffer + | bigint; + +export interface CheckPrimeOptions { + /** + * The number of Miller-Rabin probabilistic primality iterations to perform. + * When the value is 0 (zero), a number of checks is used that yields a false positive rate of at most 2-64 for random input. + * Care must be used when selecting a number of checks. + * Refer to the OpenSSL documentation for the BN_is_prime_ex function nchecks options for more details. + * + * @default 0 + */ + checks?: number | undefined; +} + +export function checkPrime( + candidate: LargeNumberLike, + callback: (err: Error | null, result: boolean) => void, +): void; +export function checkPrime( + candidate: LargeNumberLike, + options: CheckPrimeOptions, + callback: (err: Error | null, result: boolean) => void, +): void; +export function checkPrime( + _candidate: LargeNumberLike, + _options?: CheckPrimeOptions | ((err: Error | null, result: boolean) => void), + _callback?: (err: Error | null, result: boolean) => void, +) { + notImplemented("crypto.checkPrime"); +} + +export function checkPrimeSync( + _candidate: LargeNumberLike, + _options?: CheckPrimeOptions, +): boolean { + notImplemented("crypto.checkPrimeSync"); +} + +export interface GeneratePrimeOptions { + add?: LargeNumberLike | undefined; + rem?: LargeNumberLike | undefined; + /** + * @default false + */ + safe?: boolean | undefined; + bigint?: boolean | undefined; +} + +export interface GeneratePrimeOptionsBigInt extends GeneratePrimeOptions { + bigint: true; +} + +export interface GeneratePrimeOptionsArrayBuffer extends GeneratePrimeOptions { + bigint?: false | undefined; +} + +export function generatePrime( + size: number, + callback: (err: Error | null, prime: ArrayBuffer) => void, +): void; +export function generatePrime( + size: number, + options: GeneratePrimeOptionsBigInt, + callback: (err: Error | null, prime: bigint) => void, +): void; +export function generatePrime( + size: number, + options: GeneratePrimeOptionsArrayBuffer, + callback: (err: Error | null, prime: ArrayBuffer) => void, +): void; +export function generatePrime( + size: number, + options: GeneratePrimeOptions, + callback: (err: Error | null, prime: ArrayBuffer | bigint) => void, +): void; +export function generatePrime( + _size: number, + _options?: unknown, + _callback?: unknown, +) { + notImplemented("crypto.generatePrime"); +} + +export function generatePrimeSync(size: number): ArrayBuffer; +export function generatePrimeSync( + size: number, + options: GeneratePrimeOptionsBigInt, +): bigint; +export function generatePrimeSync( + size: number, + options: GeneratePrimeOptionsArrayBuffer, +): ArrayBuffer; +export function generatePrimeSync( + size: number, + options: GeneratePrimeOptions, +): ArrayBuffer | bigint; +export function generatePrimeSync( + _size: number, + _options?: + | GeneratePrimeOptionsBigInt + | GeneratePrimeOptionsArrayBuffer + | GeneratePrimeOptions, +): ArrayBuffer | bigint { + notImplemented("crypto.generatePrimeSync"); +} + +export const randomUUID = () => globalThis.crypto.randomUUID(); + +export default { + checkPrime, + checkPrimeSync, + generatePrime, + generatePrimeSync, + randomUUID, + randomInt, + randomBytes, + randomFill, + randomFillSync, +}; diff --git a/ext/node/polyfills/internal/crypto/scrypt.ts b/ext/node/polyfills/internal/crypto/scrypt.ts new file mode 100644 index 0000000000..1bdafb63d4 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/scrypt.ts @@ -0,0 +1,278 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +/* +MIT License + +Copyright (c) 2018 cryptocoinjs + +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. + */ + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { pbkdf2Sync as pbkdf2 } from "internal:deno_node/polyfills/internal/crypto/pbkdf2.ts"; +import { HASH_DATA } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +type Opts = Partial<{ + N: number; + cost: number; + p: number; + parallelization: number; + r: number; + blockSize: number; + maxmem: number; +}>; + +const fixOpts = (opts?: Opts) => { + const out = { N: 16384, p: 1, r: 8, maxmem: 32 << 20 }; + if (!opts) return out; + + if (opts.N) out.N = opts.N; + else if (opts.cost) out.N = opts.cost; + + if (opts.p) out.p = opts.p; + else if (opts.parallelization) out.p = opts.parallelization; + + if (opts.r) out.r = opts.r; + else if (opts.blockSize) out.r = opts.blockSize; + + if (opts.maxmem) out.maxmem = opts.maxmem; + + return out; +}; + +function blockxor(S: Buffer, Si: number, D: Buffer, Di: number, len: number) { + let i = -1; + while (++i < len) D[Di + i] ^= S[Si + i]; +} +function arraycopy( + src: Buffer, + srcPos: number, + dest: Buffer, + destPos: number, + length: number, +) { + src.copy(dest, destPos, srcPos, srcPos + length); +} + +const R = (a: number, b: number) => (a << b) | (a >>> (32 - b)); + +class ScryptRom { + B: Buffer; + r: number; + N: number; + p: number; + XY: Buffer; + V: Buffer; + B32: Int32Array; + x: Int32Array; + _X: Buffer; + constructor(b: Buffer, r: number, N: number, p: number) { + this.B = b; + this.r = r; + this.N = N; + this.p = p; + this.XY = Buffer.allocUnsafe(256 * r); + this.V = Buffer.allocUnsafe(128 * r * N); + this.B32 = new Int32Array(16); // salsa20_8 + this.x = new Int32Array(16); // salsa20_8 + this._X = Buffer.allocUnsafe(64); // blockmix_salsa8 + } + + run() { + const p = this.p | 0; + const r = this.r | 0; + for (let i = 0; i < p; i++) this.scryptROMix(i, r); + + return this.B; + } + + scryptROMix(i: number, r: number) { + const blockStart = i * 128 * r; + const offset = (2 * r - 1) * 64; + const blockLen = 128 * r; + const B = this.B; + const N = this.N | 0; + const V = this.V; + const XY = this.XY; + B.copy(XY, 0, blockStart, blockStart + blockLen); + for (let i1 = 0; i1 < N; i1++) { + XY.copy(V, i1 * blockLen, 0, blockLen); + this.blockmix_salsa8(blockLen); + } + + let j: number; + for (let i2 = 0; i2 < N; i2++) { + j = XY.readUInt32LE(offset) & (N - 1); + blockxor(V, j * blockLen, XY, 0, blockLen); + this.blockmix_salsa8(blockLen); + } + XY.copy(B, blockStart, 0, blockLen); + } + + blockmix_salsa8(blockLen: number) { + const BY = this.XY; + const r = this.r; + const _X = this._X; + arraycopy(BY, (2 * r - 1) * 64, _X, 0, 64); + let i; + for (i = 0; i < 2 * r; i++) { + blockxor(BY, i * 64, _X, 0, 64); + this.salsa20_8(); + arraycopy(_X, 0, BY, blockLen + i * 64, 64); + } + for (i = 0; i < r; i++) { + arraycopy(BY, blockLen + i * 2 * 64, BY, i * 64, 64); + arraycopy(BY, blockLen + (i * 2 + 1) * 64, BY, (i + r) * 64, 64); + } + } + + salsa20_8() { + const B32 = this.B32; + const B = this._X; + const x = this.x; + + let i; + for (i = 0; i < 16; i++) { + B32[i] = (B[i * 4 + 0] & 0xff) << 0; + B32[i] |= (B[i * 4 + 1] & 0xff) << 8; + B32[i] |= (B[i * 4 + 2] & 0xff) << 16; + B32[i] |= (B[i * 4 + 3] & 0xff) << 24; + } + + for (i = 0; i < 16; i++) x[i] = B32[i]; + + for (i = 0; i < 4; i++) { + x[4] ^= R(x[0] + x[12], 7); + x[8] ^= R(x[4] + x[0], 9); + x[12] ^= R(x[8] + x[4], 13); + x[0] ^= R(x[12] + x[8], 18); + x[9] ^= R(x[5] + x[1], 7); + x[13] ^= R(x[9] + x[5], 9); + x[1] ^= R(x[13] + x[9], 13); + x[5] ^= R(x[1] + x[13], 18); + x[14] ^= R(x[10] + x[6], 7); + x[2] ^= R(x[14] + x[10], 9); + x[6] ^= R(x[2] + x[14], 13); + x[10] ^= R(x[6] + x[2], 18); + x[3] ^= R(x[15] + x[11], 7); + x[7] ^= R(x[3] + x[15], 9); + x[11] ^= R(x[7] + x[3], 13); + x[15] ^= R(x[11] + x[7], 18); + x[1] ^= R(x[0] + x[3], 7); + x[2] ^= R(x[1] + x[0], 9); + x[3] ^= R(x[2] + x[1], 13); + x[0] ^= R(x[3] + x[2], 18); + x[6] ^= R(x[5] + x[4], 7); + x[7] ^= R(x[6] + x[5], 9); + x[4] ^= R(x[7] + x[6], 13); + x[5] ^= R(x[4] + x[7], 18); + x[11] ^= R(x[10] + x[9], 7); + x[8] ^= R(x[11] + x[10], 9); + x[9] ^= R(x[8] + x[11], 13); + x[10] ^= R(x[9] + x[8], 18); + x[12] ^= R(x[15] + x[14], 7); + x[13] ^= R(x[12] + x[15], 9); + x[14] ^= R(x[13] + x[12], 13); + x[15] ^= R(x[14] + x[13], 18); + } + for (i = 0; i < 16; i++) B32[i] += x[i]; + + let bi; + + for (i = 0; i < 16; i++) { + bi = i * 4; + B[bi + 0] = (B32[i] >> 0) & 0xff; + B[bi + 1] = (B32[i] >> 8) & 0xff; + B[bi + 2] = (B32[i] >> 16) & 0xff; + B[bi + 3] = (B32[i] >> 24) & 0xff; + } + } + + clean() { + this.XY.fill(0); + this.V.fill(0); + this._X.fill(0); + this.B.fill(0); + for (let i = 0; i < 16; i++) { + this.B32[i] = 0; + this.x[i] = 0; + } + } +} + +export function scryptSync( + password: HASH_DATA, + salt: HASH_DATA, + keylen: number, + _opts?: Opts, +): Buffer { + const { N, r, p, maxmem } = fixOpts(_opts); + + const blen = p * 128 * r; + + if (32 * r * (N + 2) * 4 + blen > maxmem) { + throw new Error("excedes max memory"); + } + + const b = pbkdf2(password, salt, 1, blen, "sha256"); + + const scryptRom = new ScryptRom(b, r, N, p); + const out = scryptRom.run(); + + const fin = pbkdf2(password, out, 1, keylen, "sha256"); + scryptRom.clean(); + return fin; +} + +type Callback = (err: unknown, result?: Buffer) => void; + +export function scrypt( + password: HASH_DATA, + salt: HASH_DATA, + keylen: number, + _opts: Opts | null | Callback, + cb?: Callback, +) { + if (!cb) { + cb = _opts as Callback; + _opts = null; + } + const { N, r, p, maxmem } = fixOpts(_opts as Opts); + + const blen = p * 128 * r; + if (32 * r * (N + 2) * 4 + blen > maxmem) { + throw new Error("excedes max memory"); + } + + try { + const b = pbkdf2(password, salt, 1, blen, "sha256"); + + const scryptRom = new ScryptRom(b, r, N, p); + const out = scryptRom.run(); + const result = pbkdf2(password, out, 1, keylen, "sha256"); + scryptRom.clean(); + cb(null, result); + } catch (err: unknown) { + return cb(err); + } +} + +export default { + scrypt, + scryptSync, +}; diff --git a/ext/node/polyfills/internal/crypto/sig.ts b/ext/node/polyfills/internal/crypto/sig.ts new file mode 100644 index 0000000000..6c163c8e5b --- /dev/null +++ b/ext/node/polyfills/internal/crypto/sig.ts @@ -0,0 +1,148 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import type { WritableOptions } from "internal:deno_node/polyfills/_stream.d.ts"; +import Writable from "internal:deno_node/polyfills/internal/streams/writable.mjs"; +import type { + BinaryLike, + BinaryToTextEncoding, + Encoding, + PrivateKeyInput, + PublicKeyInput, +} from "internal:deno_node/polyfills/internal/crypto/types.ts"; +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; + +export type DSAEncoding = "der" | "ieee-p1363"; + +export interface SigningOptions { + padding?: number | undefined; + saltLength?: number | undefined; + dsaEncoding?: DSAEncoding | undefined; +} + +export interface SignPrivateKeyInput extends PrivateKeyInput, SigningOptions {} + +export interface SignKeyObjectInput extends SigningOptions { + key: KeyObject; +} +export interface VerifyPublicKeyInput extends PublicKeyInput, SigningOptions {} + +export interface VerifyKeyObjectInput extends SigningOptions { + key: KeyObject; +} + +export type KeyLike = string | Buffer | KeyObject; + +export class Sign extends Writable { + constructor(algorithm: string, _options?: WritableOptions) { + validateString(algorithm, "algorithm"); + + super(); + + notImplemented("crypto.Sign"); + } + + sign(privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput): Buffer; + sign( + privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + outputFormat: BinaryToTextEncoding, + ): string; + sign( + _privateKey: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + _outputEncoding?: BinaryToTextEncoding, + ): Buffer | string { + notImplemented("crypto.Sign.prototype.sign"); + } + + update(data: BinaryLike): this; + update(data: string, inputEncoding: Encoding): this; + update(_data: BinaryLike | string, _inputEncoding?: Encoding): this { + notImplemented("crypto.Sign.prototype.update"); + } +} + +export class Verify extends Writable { + constructor(algorithm: string, _options?: WritableOptions) { + validateString(algorithm, "algorithm"); + + super(); + + notImplemented("crypto.Verify"); + } + + update(data: BinaryLike): this; + update(data: string, inputEncoding: Encoding): this; + update(_data: BinaryLike, _inputEncoding?: string): this { + notImplemented("crypto.Sign.prototype.update"); + } + + verify( + object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: ArrayBufferView, + ): boolean; + verify( + object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: string, + signatureEncoding?: BinaryToTextEncoding, + ): boolean; + verify( + _object: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + _signature: ArrayBufferView | string, + _signatureEncoding?: BinaryToTextEncoding, + ): boolean { + notImplemented("crypto.Sign.prototype.sign"); + } +} + +export function signOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, +): Buffer; +export function signOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + callback: (error: Error | null, data: Buffer) => void, +): void; +export function signOneShot( + _algorithm: string | null | undefined, + _data: ArrayBufferView, + _key: KeyLike | SignKeyObjectInput | SignPrivateKeyInput, + _callback?: (error: Error | null, data: Buffer) => void, +): Buffer | void { + notImplemented("crypto.sign"); +} + +export function verifyOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: ArrayBufferView, +): boolean; +export function verifyOneShot( + algorithm: string | null | undefined, + data: ArrayBufferView, + key: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + signature: ArrayBufferView, + callback: (error: Error | null, result: boolean) => void, +): void; +export function verifyOneShot( + _algorithm: string | null | undefined, + _data: ArrayBufferView, + _key: KeyLike | VerifyKeyObjectInput | VerifyPublicKeyInput, + _signature: ArrayBufferView, + _callback?: (error: Error | null, result: boolean) => void, +): boolean | void { + notImplemented("crypto.verify"); +} + +export default { + signOneShot, + verifyOneShot, + Sign, + Verify, +}; diff --git a/ext/node/polyfills/internal/crypto/types.ts b/ext/node/polyfills/internal/crypto/types.ts new file mode 100644 index 0000000000..3bb9ec1600 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/types.ts @@ -0,0 +1,46 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export type HASH_DATA = string | ArrayBufferView | Buffer; + +export type BinaryToTextEncoding = "base64" | "base64url" | "hex" | "binary"; + +export type CharacterEncoding = "utf8" | "utf-8" | "utf16le" | "latin1"; + +export type LegacyCharacterEncoding = "ascii" | "binary" | "ucs2" | "ucs-2"; + +export type Encoding = + | BinaryToTextEncoding + | CharacterEncoding + | LegacyCharacterEncoding; + +export type ECDHKeyFormat = "compressed" | "uncompressed" | "hybrid"; + +export type BinaryLike = string | ArrayBufferView; + +export type KeyFormat = "pem" | "der"; + +export type KeyType = + | "rsa" + | "rsa-pss" + | "dsa" + | "ec" + | "ed25519" + | "ed448" + | "x25519" + | "x448"; + +export interface PrivateKeyInput { + key: string | Buffer; + format?: KeyFormat | undefined; + type?: "pkcs1" | "pkcs8" | "sec1" | undefined; + passphrase?: string | Buffer | undefined; +} + +export interface PublicKeyInput { + key: string | Buffer; + format?: KeyFormat | undefined; + type?: "pkcs1" | "spki" | undefined; +} diff --git a/ext/node/polyfills/internal/crypto/util.ts b/ext/node/polyfills/internal/crypto/util.ts new file mode 100644 index 0000000000..f9fce8b2d3 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/util.ts @@ -0,0 +1,129 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { getCiphers } from "internal:deno_node/polyfills/_crypto/crypto_browserify/browserify_aes/mod.js"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + ERR_INVALID_ARG_TYPE, + hideStackFrames, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + isAnyArrayBuffer, + isArrayBufferView, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { crypto as constants } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { + kHandle, + kKeyObject, +} from "internal:deno_node/polyfills/internal/crypto/constants.ts"; + +// TODO(kt3k): Generate this list from `digestAlgorithms` +// of std/crypto/_wasm/mod.ts +const digestAlgorithms = [ + "blake2b256", + "blake2b384", + "blake2b", + "blake2s", + "blake3", + "keccak-224", + "keccak-256", + "keccak-384", + "keccak-512", + "sha384", + "sha3-224", + "sha3-256", + "sha3-384", + "sha3-512", + "shake128", + "shake256", + "tiger", + "rmd160", + "sha224", + "sha256", + "sha512", + "md4", + "md5", + "sha1", +]; + +let defaultEncoding = "buffer"; + +export function setDefaultEncoding(val: string) { + defaultEncoding = val; +} + +export function getDefaultEncoding(): string { + return defaultEncoding; +} + +// This is here because many functions accepted binary strings without +// any explicit encoding in older versions of node, and we don't want +// to break them unnecessarily. +export function toBuf(val: string | Buffer, encoding?: string): Buffer { + if (typeof val === "string") { + if (encoding === "buffer") { + encoding = "utf8"; + } + + return Buffer.from(val, encoding); + } + + return val; +} + +export const validateByteSource = hideStackFrames((val, name) => { + val = toBuf(val); + + if (isAnyArrayBuffer(val) || isArrayBufferView(val)) { + return; + } + + throw new ERR_INVALID_ARG_TYPE( + name, + ["string", "ArrayBuffer", "TypedArray", "DataView", "Buffer"], + val, + ); +}); + +/** + * Returns an array of the names of the supported hash algorithms, such as 'sha1'. + */ +export function getHashes(): readonly string[] { + return digestAlgorithms; +} + +export function getCurves(): readonly string[] { + notImplemented("crypto.getCurves"); +} + +export interface SecureHeapUsage { + total: number; + min: number; + used: number; + utilization: number; +} + +export function secureHeapUsed(): SecureHeapUsage { + notImplemented("crypto.secureHeapUsed"); +} + +export function setEngine(_engine: string, _flags: typeof constants) { + notImplemented("crypto.setEngine"); +} + +export { getCiphers, kHandle, kKeyObject }; + +export default { + getDefaultEncoding, + getHashes, + setDefaultEncoding, + getCiphers, + getCurves, + secureHeapUsed, + setEngine, + validateByteSource, + toBuf, + kHandle, + kKeyObject, +}; diff --git a/ext/node/polyfills/internal/crypto/x509.ts b/ext/node/polyfills/internal/crypto/x509.ts new file mode 100644 index 0000000000..0722d78652 --- /dev/null +++ b/ext/node/polyfills/internal/crypto/x509.ts @@ -0,0 +1,186 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { KeyObject } from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { isArrayBufferView } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { BinaryLike } from "internal:deno_node/polyfills/internal/crypto/types.ts"; + +// deno-lint-ignore no-explicit-any +export type PeerCertificate = any; + +export interface X509CheckOptions { + /** + * @default 'always' + */ + subject: "always" | "never"; + /** + * @default true + */ + wildcards: boolean; + /** + * @default true + */ + partialWildcards: boolean; + /** + * @default false + */ + multiLabelWildcards: boolean; + /** + * @default false + */ + singleLabelSubdomains: boolean; +} + +export class X509Certificate { + constructor(buffer: BinaryLike) { + if (typeof buffer === "string") { + buffer = Buffer.from(buffer); + } + + if (!isArrayBufferView(buffer)) { + throw new ERR_INVALID_ARG_TYPE( + "buffer", + ["string", "Buffer", "TypedArray", "DataView"], + buffer, + ); + } + + notImplemented("crypto.X509Certificate"); + } + + get ca(): boolean { + notImplemented("crypto.X509Certificate.prototype.ca"); + + return false; + } + + checkEmail( + _email: string, + _options?: Pick, + ): string | undefined { + notImplemented("crypto.X509Certificate.prototype.checkEmail"); + } + + checkHost(_name: string, _options?: X509CheckOptions): string | undefined { + notImplemented("crypto.X509Certificate.prototype.checkHost"); + } + + checkIP(_ip: string): string | undefined { + notImplemented("crypto.X509Certificate.prototype.checkIP"); + } + + checkIssued(_otherCert: X509Certificate): boolean { + notImplemented("crypto.X509Certificate.prototype.checkIssued"); + } + + checkPrivateKey(_privateKey: KeyObject): boolean { + notImplemented("crypto.X509Certificate.prototype.checkPrivateKey"); + } + + get fingerprint(): string { + notImplemented("crypto.X509Certificate.prototype.fingerprint"); + + return ""; + } + + get fingerprint256(): string { + notImplemented("crypto.X509Certificate.prototype.fingerprint256"); + + return ""; + } + + get fingerprint512(): string { + notImplemented("crypto.X509Certificate.prototype.fingerprint512"); + + return ""; + } + + get infoAccess(): string | undefined { + notImplemented("crypto.X509Certificate.prototype.infoAccess"); + + return ""; + } + + get issuer(): string { + notImplemented("crypto.X509Certificate.prototype.issuer"); + + return ""; + } + + get issuerCertificate(): X509Certificate | undefined { + notImplemented("crypto.X509Certificate.prototype.issuerCertificate"); + + return {} as X509Certificate; + } + + get keyUsage(): string[] { + notImplemented("crypto.X509Certificate.prototype.keyUsage"); + + return []; + } + + get publicKey(): KeyObject { + notImplemented("crypto.X509Certificate.prototype.publicKey"); + + return {} as KeyObject; + } + + get raw(): Buffer { + notImplemented("crypto.X509Certificate.prototype.raw"); + + return {} as Buffer; + } + + get serialNumber(): string { + notImplemented("crypto.X509Certificate.prototype.serialNumber"); + + return ""; + } + + get subject(): string { + notImplemented("crypto.X509Certificate.prototype.subject"); + + return ""; + } + + get subjectAltName(): string | undefined { + notImplemented("crypto.X509Certificate.prototype.subjectAltName"); + + return ""; + } + + toJSON(): string { + return this.toString(); + } + + toLegacyObject(): PeerCertificate { + notImplemented("crypto.X509Certificate.prototype.toLegacyObject"); + } + + toString(): string { + notImplemented("crypto.X509Certificate.prototype.toString"); + } + + get validFrom(): string { + notImplemented("crypto.X509Certificate.prototype.validFrom"); + + return ""; + } + + get validTo(): string { + notImplemented("crypto.X509Certificate.prototype.validTo"); + + return ""; + } + + verify(_publicKey: KeyObject): boolean { + notImplemented("crypto.X509Certificate.prototype.verify"); + } +} + +export default { + X509Certificate, +}; diff --git a/ext/node/polyfills/internal/dgram.ts b/ext/node/polyfills/internal/dgram.ts new file mode 100644 index 0000000000..8e8e50fabd --- /dev/null +++ b/ext/node/polyfills/internal/dgram.ts @@ -0,0 +1,129 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { lookup as defaultLookup } from "internal:deno_node/polyfills/dns.ts"; +import { + isInt32, + validateFunction, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import type { ErrnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { ERR_SOCKET_BAD_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { UDP } from "internal:deno_node/polyfills/internal_binding/udp_wrap.ts"; +import { guessHandleType } from "internal:deno_node/polyfills/internal_binding/util.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; + +export type SocketType = "udp4" | "udp6"; + +export const kStateSymbol: unique symbol = Symbol("kStateSymbol"); + +function lookup4( + lookup: typeof defaultLookup, + address: string, + callback: ( + err: ErrnoException | null, + address: string, + family: number, + ) => void, +) { + return lookup(address || "127.0.0.1", 4, callback); +} + +function lookup6( + lookup: typeof defaultLookup, + address: string, + callback: ( + err: ErrnoException | null, + address: string, + family: number, + ) => void, +) { + return lookup(address || "::1", 6, callback); +} + +export function newHandle( + type: SocketType, + lookup?: typeof defaultLookup, +): UDP { + if (lookup === undefined) { + lookup = defaultLookup; + } else { + validateFunction(lookup, "lookup"); + } + + if (type === "udp4") { + const handle = new UDP(); + + handle.lookup = lookup4.bind(handle, lookup); + + return handle; + } + + if (type === "udp6") { + const handle = new UDP(); + + handle.lookup = lookup6.bind(handle, lookup); + handle.bind = handle.bind6; + handle.connect = handle.connect6; + handle.send = handle.send6; + + return handle; + } + + throw new ERR_SOCKET_BAD_TYPE(); +} + +export function _createSocketHandle( + address: string, + port: number, + addressType: SocketType, + fd: number, + flags: number, +) { + const handle = newHandle(addressType); + let err; + + if (isInt32(fd) && fd > 0) { + const type = guessHandleType(fd); + + if (type !== "UDP") { + err = codeMap.get("EINVAL")!; + } else { + err = handle.open(fd); + } + } else if (port || address) { + err = handle.bind(address, port || 0, flags); + } + + if (err) { + handle.close(); + + return err; + } + + return handle; +} + +export default { + kStateSymbol, + newHandle, + _createSocketHandle, +}; diff --git a/ext/node/polyfills/internal/dns/promises.ts b/ext/node/polyfills/internal/dns/promises.ts new file mode 100644 index 0000000000..e5e15dc871 --- /dev/null +++ b/ext/node/polyfills/internal/dns/promises.ts @@ -0,0 +1,511 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { + validateBoolean, + validateNumber, + validateOneOf, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isIP } from "internal:deno_node/polyfills/internal/net.ts"; +import { + emitInvalidHostnameWarning, + getDefaultResolver, + getDefaultVerbatim, + isFamily, + isLookupOptions, + Resolver as CallbackResolver, + validateHints, +} from "internal:deno_node/polyfills/internal/dns/utils.ts"; +import type { + LookupAddress, + LookupAllOptions, + LookupOneOptions, + LookupOptions, + Records, + ResolveOptions, + ResolveWithTtlOptions, +} from "internal:deno_node/polyfills/internal/dns/utils.ts"; +import { + dnsException, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { + ChannelWrapQuery, + getaddrinfo, + GetAddrInfoReqWrap, + QueryReqWrap, +} from "internal:deno_node/polyfills/internal_binding/cares_wrap.ts"; +import { toASCII } from "internal:deno_node/polyfills/internal/idna.ts"; + +function onlookup( + this: GetAddrInfoReqWrap, + err: number | null, + addresses: string[], +) { + if (err) { + this.reject(dnsException(err, "getaddrinfo", this.hostname)); + return; + } + + const family = this.family || isIP(addresses[0]); + this.resolve({ address: addresses[0], family }); +} + +function onlookupall( + this: GetAddrInfoReqWrap, + err: number | null, + addresses: string[], +) { + if (err) { + this.reject(dnsException(err, "getaddrinfo", this.hostname)); + + return; + } + + const family = this.family; + const parsedAddresses = []; + + for (let i = 0; i < addresses.length; i++) { + const address = addresses[i]; + parsedAddresses[i] = { + address, + family: family ? family : isIP(address), + }; + } + + this.resolve(parsedAddresses); +} + +function createLookupPromise( + family: number, + hostname: string, + all: boolean, + hints: number, + verbatim: boolean, +): Promise { + return new Promise((resolve, reject) => { + if (!hostname) { + emitInvalidHostnameWarning(hostname); + resolve(all ? [] : { address: null, family: family === 6 ? 6 : 4 }); + + return; + } + + const matchedFamily = isIP(hostname); + + if (matchedFamily !== 0) { + const result = { address: hostname, family: matchedFamily }; + resolve(all ? [result] : result); + + return; + } + + const req = new GetAddrInfoReqWrap(); + + req.family = family; + req.hostname = hostname; + req.oncomplete = all ? onlookupall : onlookup; + req.resolve = resolve; + req.reject = reject; + + const err = getaddrinfo(req, toASCII(hostname), family, hints, verbatim); + + if (err) { + reject(dnsException(err, "getaddrinfo", hostname)); + } + }); +} + +const validFamilies = [0, 4, 6]; + +export function lookup( + hostname: string, + family: number, +): Promise; +export function lookup( + hostname: string, + options: LookupOneOptions, +): Promise; +export function lookup( + hostname: string, + options: LookupAllOptions, +): Promise; +export function lookup( + hostname: string, + options: LookupOptions, +): Promise; +export function lookup( + hostname: string, + options: unknown, +): Promise { + let hints = 0; + let family = 0; + let all = false; + let verbatim = getDefaultVerbatim(); + + // Parse arguments + if (hostname) { + validateString(hostname, "hostname"); + } + + if (isFamily(options)) { + validateOneOf(options, "family", validFamilies); + family = options; + } else if (!isLookupOptions(options)) { + throw new ERR_INVALID_ARG_TYPE("options", ["integer", "object"], options); + } else { + if (options?.hints != null) { + validateNumber(options.hints, "options.hints"); + hints = options.hints >>> 0; + validateHints(hints); + } + + if (options?.family != null) { + validateOneOf(options.family, "options.family", validFamilies); + family = options.family; + } + + if (options?.all != null) { + validateBoolean(options.all, "options.all"); + all = options.all; + } + + if (options?.verbatim != null) { + validateBoolean(options.verbatim, "options.verbatim"); + verbatim = options.verbatim; + } + } + + return createLookupPromise(family, hostname, all, hints, verbatim); +} + +function onresolve( + this: QueryReqWrap, + err: number, + records: Records, + ttls?: number[], +) { + if (err) { + this.reject(dnsException(err, this.bindingName, this.hostname)); + + return; + } + + const parsedRecords = ttls && this.ttl + ? (records as string[]).map((address: string, index: number) => ({ + address, + ttl: ttls[index], + })) + : records; + + this.resolve(parsedRecords); +} + +function createResolverPromise( + resolver: Resolver, + bindingName: keyof ChannelWrapQuery, + hostname: string, + ttl: boolean, +) { + return new Promise((resolve, reject) => { + const req = new QueryReqWrap(); + + req.bindingName = bindingName; + req.hostname = hostname; + req.oncomplete = onresolve; + req.resolve = resolve; + req.reject = reject; + req.ttl = ttl; + + const err = resolver._handle[bindingName](req, toASCII(hostname)); + + if (err) { + reject(dnsException(err, bindingName, hostname)); + } + }); +} + +function resolver(bindingName: keyof ChannelWrapQuery) { + function query( + this: Resolver, + name: string, + options?: unknown, + ) { + validateString(name, "name"); + + const ttl = !!(options && (options as ResolveOptions).ttl); + + return createResolverPromise(this, bindingName, name, ttl); + } + + Object.defineProperty(query, "name", { value: bindingName }); + + return query; +} + +const resolveMap = Object.create(null); + +class Resolver extends CallbackResolver { + // deno-lint-ignore no-explicit-any + [resolveMethod: string]: any; +} + +Resolver.prototype.resolveAny = resolveMap.ANY = resolver("queryAny"); +Resolver.prototype.resolve4 = resolveMap.A = resolver("queryA"); +Resolver.prototype.resolve6 = resolveMap.AAAA = resolver("queryAaaa"); +Resolver.prototype.resolveCaa = resolveMap.CAA = resolver("queryCaa"); +Resolver.prototype.resolveCname = resolveMap.CNAME = resolver("queryCname"); +Resolver.prototype.resolveMx = resolveMap.MX = resolver("queryMx"); +Resolver.prototype.resolveNs = resolveMap.NS = resolver("queryNs"); +Resolver.prototype.resolveTxt = resolveMap.TXT = resolver("queryTxt"); +Resolver.prototype.resolveSrv = resolveMap.SRV = resolver("querySrv"); +Resolver.prototype.resolvePtr = resolveMap.PTR = resolver("queryPtr"); +Resolver.prototype.resolveNaptr = resolveMap.NAPTR = resolver("queryNaptr"); +Resolver.prototype.resolveSoa = resolveMap.SOA = resolver("querySoa"); +Resolver.prototype.reverse = resolver("getHostByAddr"); +Resolver.prototype.resolve = _resolve; + +function _resolve( + this: Resolver, + hostname: string, + rrtype?: string, +) { + let resolver; + + if (typeof hostname !== "string") { + throw new ERR_INVALID_ARG_TYPE("name", "string", hostname); + } + + if (rrtype !== undefined) { + validateString(rrtype, "rrtype"); + + resolver = resolveMap[rrtype]; + + if (typeof resolver !== "function") { + throw new ERR_INVALID_ARG_VALUE("rrtype", rrtype); + } + } else { + resolver = resolveMap.A; + } + + return Reflect.apply(resolver, this, [hostname]); +} + +// The Node implementation uses `bindDefaultResolver` to set the follow methods +// on `module.exports` bound to the current `defaultResolver`. We don't have +// the same ability in ESM but can simulate this (at some cost) by explicitly +// exporting these methods which dynamically bind to the default resolver when +// called. + +export function getServers(): string[] { + return Resolver.prototype.getServers.bind(getDefaultResolver())(); +} + +export function resolveAny( + hostname: string, +) { + return Resolver.prototype.resolveAny.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolve4( + hostname: string, +): Promise; +export function resolve4( + hostname: string, + options: ResolveWithTtlOptions, +): Promise; +export function resolve4( + hostname: string, + options: ResolveOptions, +): Promise; +export function resolve4(hostname: string, options?: unknown) { + return Resolver.prototype.resolve4.bind(getDefaultResolver() as Resolver)( + hostname, + options, + ); +} + +export function resolve6(hostname: string): Promise; +export function resolve6( + hostname: string, + options: ResolveWithTtlOptions, +): Promise; +export function resolve6( + hostname: string, + options: ResolveOptions, +): Promise; +export function resolve6(hostname: string, options?: unknown) { + return Resolver.prototype.resolve6.bind(getDefaultResolver() as Resolver)( + hostname, + options, + ); +} + +export function resolveCaa( + hostname: string, +) { + return Resolver.prototype.resolveCaa.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveCname( + hostname: string, +) { + return Resolver.prototype.resolveCname.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveMx( + hostname: string, +) { + return Resolver.prototype.resolveMx.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveNs(hostname: string) { + return Resolver.prototype.resolveNs.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveTxt(hostname: string) { + return Resolver.prototype.resolveTxt.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveSrv(hostname: string) { + return Resolver.prototype.resolveSrv.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolvePtr(hostname: string) { + return Resolver.prototype.resolvePtr.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveNaptr(hostname: string) { + return Resolver.prototype.resolveNaptr.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function resolveSoa(hostname: string) { + return Resolver.prototype.resolveSoa.bind(getDefaultResolver() as Resolver)( + hostname, + ); +} + +export function reverse(ip: string) { + return Resolver.prototype.reverse.bind(getDefaultResolver() as Resolver)( + ip, + ); +} + +export function resolve( + hostname: string, +): Promise; +export function resolve( + hostname: string, + rrtype: "A", +): Promise; +export function resolve( + hostname: string, + rrtype: "AAAA", +): Promise; +export function resolve( + hostname: string, + rrtype: "ANY", +): Promise; +export function resolve( + hostname: string, + rrtype: "CNAME", +): Promise; +export function resolve( + hostname: string, + rrtype: "MX", +): Promise; +export function resolve( + hostname: string, + rrtype: "NAPTR", +): Promise; +export function resolve( + hostname: string, + rrtype: "NS", +): Promise; +export function resolve( + hostname: string, + rrtype: "PTR", +): Promise; +export function resolve( + hostname: string, + rrtype: "SOA", +): Promise; +export function resolve( + hostname: string, + rrtype: "SRV", +): Promise; +export function resolve( + hostname: string, + rrtype: "TXT", +): Promise; +export function resolve( + hostname: string, + rrtype: string, +): Promise; +export function resolve(hostname: string, rrtype?: string) { + return Resolver.prototype.resolve.bind(getDefaultResolver() as Resolver)( + hostname, + rrtype, + ); +} + +export { Resolver }; + +export default { + lookup, + Resolver, + getServers, + resolveAny, + resolve4, + resolve6, + resolveCaa, + resolveCname, + resolveMx, + resolveNs, + resolveTxt, + resolveSrv, + resolvePtr, + resolveNaptr, + resolveSoa, + resolve, + reverse, +}; diff --git a/ext/node/polyfills/internal/dns/utils.ts b/ext/node/polyfills/internal/dns/utils.ts new file mode 100644 index 0000000000..0afd106170 --- /dev/null +++ b/ext/node/polyfills/internal/dns/utils.ts @@ -0,0 +1,456 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { getOptionValue } from "internal:deno_node/polyfills/internal/options.ts"; +import { emitWarning } from "internal:deno_node/polyfills/process.ts"; +import { + AI_ADDRCONFIG, + AI_ALL, + AI_V4MAPPED, +} from "internal:deno_node/polyfills/internal_binding/ares.ts"; +import { + ChannelWrap, + strerror, +} from "internal:deno_node/polyfills/internal_binding/cares_wrap.ts"; +import { + ERR_DNS_SET_SERVERS_FAILED, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_IP_ADDRESS, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import type { ErrnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateArray, + validateInt32, + validateOneOf, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isIP } from "internal:deno_node/polyfills/internal/net.ts"; + +export interface LookupOptions { + family?: number | undefined; + hints?: number | undefined; + all?: boolean | undefined; + verbatim?: boolean | undefined; +} + +export interface LookupOneOptions extends LookupOptions { + all?: false | undefined; +} + +export interface LookupAllOptions extends LookupOptions { + all: true; +} + +export interface LookupAddress { + address: string | null; + family: number; +} + +export function isLookupOptions( + options: unknown, +): options is LookupOptions | undefined { + return typeof options === "object" || typeof options === "undefined"; +} + +export function isLookupCallback( + options: unknown, +): options is (...args: unknown[]) => void { + return typeof options === "function"; +} + +export function isFamily(options: unknown): options is number { + return typeof options === "number"; +} + +export interface ResolveOptions { + ttl?: boolean; +} + +export interface ResolveWithTtlOptions extends ResolveOptions { + ttl: true; +} + +export interface RecordWithTtl { + address: string; + ttl: number; +} + +export interface AnyARecord extends RecordWithTtl { + type: "A"; +} + +export interface AnyAaaaRecord extends RecordWithTtl { + type: "AAAA"; +} + +export interface CaaRecord { + critial: number; + issue?: string | undefined; + issuewild?: string | undefined; + iodef?: string | undefined; + contactemail?: string | undefined; + contactphone?: string | undefined; +} + +export interface MxRecord { + priority: number; + exchange: string; +} + +export interface AnyMxRecord extends MxRecord { + type: "MX"; +} + +export interface NaptrRecord { + flags: string; + service: string; + regexp: string; + replacement: string; + order: number; + preference: number; +} + +export interface AnyNaptrRecord extends NaptrRecord { + type: "NAPTR"; +} + +export interface SoaRecord { + nsname: string; + hostmaster: string; + serial: number; + refresh: number; + retry: number; + expire: number; + minttl: number; +} + +export interface AnySoaRecord extends SoaRecord { + type: "SOA"; +} + +export interface SrvRecord { + priority: number; + weight: number; + port: number; + name: string; +} + +export interface AnySrvRecord extends SrvRecord { + type: "SRV"; +} + +export interface AnyTxtRecord { + type: "TXT"; + entries: string[]; +} + +export interface AnyNsRecord { + type: "NS"; + value: string; +} + +export interface AnyPtrRecord { + type: "PTR"; + value: string; +} + +export interface AnyCnameRecord { + type: "CNAME"; + value: string; +} + +export type AnyRecord = + | AnyARecord + | AnyAaaaRecord + | AnyCnameRecord + | AnyMxRecord + | AnyNaptrRecord + | AnyNsRecord + | AnyPtrRecord + | AnySoaRecord + | AnySrvRecord + | AnyTxtRecord; + +export type Records = + | string[] + | AnyRecord[] + | MxRecord[] + | NaptrRecord[] + | SoaRecord + | SrvRecord[] + | string[]; + +export type ResolveCallback = ( + err: ErrnoException | null, + addresses: Records, +) => void; + +export function isResolveCallback( + callback: unknown, +): callback is ResolveCallback { + return typeof callback === "function"; +} + +const IANA_DNS_PORT = 53; +const IPv6RE = /^\[([^[\]]*)\]/; +const addrSplitRE = /(^.+?)(?::(\d+))?$/; + +export function validateTimeout(options?: { timeout?: number }) { + const { timeout = -1 } = { ...options }; + validateInt32(timeout, "options.timeout", -1, 2 ** 31 - 1); + return timeout; +} + +export function validateTries(options?: { tries?: number }) { + const { tries = 4 } = { ...options }; + validateInt32(tries, "options.tries", 1, 2 ** 31 - 1); + return tries; +} + +export interface ResolverOptions { + timeout?: number | undefined; + /** + * @default 4 + */ + tries?: number; +} + +/** + * An independent resolver for DNS requests. + * + * Creating a new resolver uses the default server settings. Setting + * the servers used for a resolver using `resolver.setServers()` does not affect + * other resolvers: + * + * ```js + * const { Resolver } = require('dns'); + * const resolver = new Resolver(); + * resolver.setServers(['4.4.4.4']); + * + * // This request will use the server at 4.4.4.4, independent of global settings. + * resolver.resolve4('example.org', (err, addresses) => { + * // ... + * }); + * ``` + * + * The following methods from the `dns` module are available: + * + * - `resolver.getServers()` + * - `resolver.resolve()` + * - `resolver.resolve4()` + * - `resolver.resolve6()` + * - `resolver.resolveAny()` + * - `resolver.resolveCaa()` + * - `resolver.resolveCname()` + * - `resolver.resolveMx()` + * - `resolver.resolveNaptr()` + * - `resolver.resolveNs()` + * - `resolver.resolvePtr()` + * - `resolver.resolveSoa()` + * - `resolver.resolveSrv()` + * - `resolver.resolveTxt()` + * - `resolver.reverse()` + * - `resolver.setServers()` + */ +export class Resolver { + _handle!: ChannelWrap; + + constructor(options?: ResolverOptions) { + const timeout = validateTimeout(options); + const tries = validateTries(options); + this._handle = new ChannelWrap(timeout, tries); + } + + cancel() { + this._handle.cancel(); + } + + getServers(): string[] { + return this._handle.getServers().map((val: [string, number]) => { + if (!val[1] || val[1] === IANA_DNS_PORT) { + return val[0]; + } + + const host = isIP(val[0]) === 6 ? `[${val[0]}]` : val[0]; + return `${host}:${val[1]}`; + }); + } + + setServers(servers: ReadonlyArray) { + validateArray(servers, "servers"); + + // Cache the original servers because in the event of an error while + // setting the servers, c-ares won't have any servers available for + // resolution. + const orig = this._handle.getServers(); + const newSet: [number, string, number][] = []; + + servers.forEach((serv, index) => { + validateString(serv, `servers[${index}]`); + let ipVersion = isIP(serv); + + if (ipVersion !== 0) { + return newSet.push([ipVersion, serv, IANA_DNS_PORT]); + } + + const match = serv.match(IPv6RE); + + // Check for an IPv6 in brackets. + if (match) { + ipVersion = isIP(match[1]); + + if (ipVersion !== 0) { + const port = Number.parseInt(serv.replace(addrSplitRE, "$2")) || + IANA_DNS_PORT; + + return newSet.push([ipVersion, match[1], port]); + } + } + + // addr::port + const addrSplitMatch = serv.match(addrSplitRE); + + if (addrSplitMatch) { + const hostIP = addrSplitMatch[1]; + const port = addrSplitMatch[2] || `${IANA_DNS_PORT}`; + + ipVersion = isIP(hostIP); + + if (ipVersion !== 0) { + return newSet.push([ipVersion, hostIP, Number.parseInt(port)]); + } + } + + throw new ERR_INVALID_IP_ADDRESS(serv); + }); + + const errorNumber = this._handle.setServers(newSet); + + if (errorNumber !== 0) { + // Reset the servers to the old servers, because ares probably unset them. + this._handle.setServers(orig.join(",")); + const err = strerror(errorNumber); + + throw new ERR_DNS_SET_SERVERS_FAILED(err, servers.toString()); + } + } + + /** + * The resolver instance will send its requests from the specified IP address. + * This allows programs to specify outbound interfaces when used on multi-homed + * systems. + * + * If a v4 or v6 address is not specified, it is set to the default, and the + * operating system will choose a local address automatically. + * + * The resolver will use the v4 local address when making requests to IPv4 DNS + * servers, and the v6 local address when making requests to IPv6 DNS servers. + * The `rrtype` of resolution requests has no impact on the local address used. + * + * @param [ipv4='0.0.0.0'] A string representation of an IPv4 address. + * @param [ipv6='::0'] A string representation of an IPv6 address. + */ + setLocalAddress(ipv4: string, ipv6?: string) { + validateString(ipv4, "ipv4"); + + if (ipv6 !== undefined) { + validateString(ipv6, "ipv6"); + } + + this._handle.setLocalAddress(ipv4, ipv6); + } +} + +let defaultResolver = new Resolver(); + +export function getDefaultResolver(): Resolver { + return defaultResolver; +} + +export function setDefaultResolver(resolver: T) { + defaultResolver = resolver; +} + +export function validateHints(hints: number) { + if ((hints & ~(AI_ADDRCONFIG | AI_ALL | AI_V4MAPPED)) !== 0) { + throw new ERR_INVALID_ARG_VALUE("hints", hints, "is invalid"); + } +} + +let invalidHostnameWarningEmitted = false; + +export function emitInvalidHostnameWarning(hostname: string) { + if (invalidHostnameWarningEmitted) { + return; + } + + invalidHostnameWarningEmitted = true; + + emitWarning( + `The provided hostname "${hostname}" is not a valid ` + + "hostname, and is supported in the dns module solely for compatibility.", + "DeprecationWarning", + "DEP0118", + ); +} + +let dnsOrder = getOptionValue("--dns-result-order") || "ipv4first"; + +export function getDefaultVerbatim() { + switch (dnsOrder) { + case "verbatim": { + return true; + } + case "ipv4first": { + return false; + } + default: { + return false; + } + } +} + +/** + * Set the default value of `verbatim` in `lookup` and `dnsPromises.lookup()`. + * The value could be: + * + * - `ipv4first`: sets default `verbatim` `false`. + * - `verbatim`: sets default `verbatim` `true`. + * + * The default is `ipv4first` and `setDefaultResultOrder` have higher + * priority than `--dns-result-order`. When using `worker threads`, + * `setDefaultResultOrder` from the main thread won't affect the default + * dns orders in workers. + * + * @param order must be `'ipv4first'` or `'verbatim'`. + */ +export function setDefaultResultOrder(order: "ipv4first" | "verbatim") { + validateOneOf(order, "dnsOrder", ["verbatim", "ipv4first"]); + dnsOrder = order; +} + +export function defaultResolverSetServers(servers: string[]) { + const resolver = new Resolver(); + + resolver.setServers(servers); + setDefaultResolver(resolver); +} diff --git a/ext/node/polyfills/internal/dtrace.ts b/ext/node/polyfills/internal/dtrace.ts new file mode 100644 index 0000000000..5a44703366 --- /dev/null +++ b/ext/node/polyfills/internal/dtrace.ts @@ -0,0 +1,41 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// deno-lint-ignore-file + +const { + DTRACE_HTTP_CLIENT_REQUEST = (..._args: any[]) => {}, + DTRACE_HTTP_CLIENT_RESPONSE = (..._args: any[]) => {}, + DTRACE_HTTP_SERVER_REQUEST = (..._args: any[]) => {}, + DTRACE_HTTP_SERVER_RESPONSE = (..._args: any[]) => {}, + DTRACE_NET_SERVER_CONNECTION = (..._args: any[]) => {}, + DTRACE_NET_STREAM_END = (..._args: any[]) => {}, +} = {}; + +export { + DTRACE_HTTP_CLIENT_REQUEST, + DTRACE_HTTP_CLIENT_RESPONSE, + DTRACE_HTTP_SERVER_REQUEST, + DTRACE_HTTP_SERVER_RESPONSE, + DTRACE_NET_SERVER_CONNECTION, + DTRACE_NET_STREAM_END, +}; diff --git a/ext/node/polyfills/internal/error_codes.ts b/ext/node/polyfills/internal/error_codes.ts new file mode 100644 index 0000000000..6af88bb112 --- /dev/null +++ b/ext/node/polyfills/internal/error_codes.ts @@ -0,0 +1,7 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// Lazily initializes the error classes in this object. +// This trick is necessary for avoiding circular dendencies between +// `internal/errors` and other modules. +// deno-lint-ignore no-explicit-any +export const codes: Record = {}; diff --git a/ext/node/polyfills/internal/errors.ts b/ext/node/polyfills/internal/errors.ts new file mode 100644 index 0000000000..67f729f8d3 --- /dev/null +++ b/ext/node/polyfills/internal/errors.ts @@ -0,0 +1,2864 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Node.js contributors. All rights reserved. MIT License. +/** NOT IMPLEMENTED + * ERR_MANIFEST_ASSERT_INTEGRITY + * ERR_QUICSESSION_VERSION_NEGOTIATION + * ERR_REQUIRE_ESM + * ERR_TLS_CERT_ALTNAME_INVALID + * ERR_WORKER_INVALID_EXEC_ARGV + * ERR_WORKER_PATH + * ERR_QUIC_ERROR + * ERR_SYSTEM_ERROR //System error, shouldn't ever happen inside Deno + * ERR_TTY_INIT_FAILED //System error, shouldn't ever happen inside Deno + * ERR_INVALID_PACKAGE_CONFIG // package.json stuff, probably useless + */ + +import { inspect } from "internal:deno_node/polyfills/internal/util/inspect.mjs"; +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; +import { + codeMap, + errorMap, + mapSysErrnoToUvErrno, +} from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; +import { os as osConstants } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { hideStackFrames } from "internal:deno_node/polyfills/internal/hide_stack_frames.ts"; +import { getSystemErrorName } from "internal:deno_node/polyfills/_utils.ts"; + +export { errorMap }; + +const kIsNodeError = Symbol("kIsNodeError"); + +/** + * @see https://github.com/nodejs/node/blob/f3eb224/lib/internal/errors.js + */ +const classRegExp = /^([A-Z][a-z0-9]*)+$/; + +/** + * @see https://github.com/nodejs/node/blob/f3eb224/lib/internal/errors.js + * @description Sorted by a rough estimate on most frequently used entries. + */ +const kTypes = [ + "string", + "function", + "number", + "object", + // Accept 'Function' and 'Object' as alternative to the lower cased version. + "Function", + "Object", + "boolean", + "bigint", + "symbol", +]; + +// Node uses an AbortError that isn't exactly the same as the DOMException +// to make usage of the error in userland and readable-stream easier. +// It is a regular error with `.code` and `.name`. +export class AbortError extends Error { + code: string; + + constructor(message = "The operation was aborted", options?: ErrorOptions) { + if (options !== undefined && typeof options !== "object") { + throw new codes.ERR_INVALID_ARG_TYPE("options", "Object", options); + } + super(message, options); + this.code = "ABORT_ERR"; + this.name = "AbortError"; + } +} + +let maxStackErrorName: string | undefined; +let maxStackErrorMessage: string | undefined; +/** + * Returns true if `err.name` and `err.message` are equal to engine-specific + * values indicating max call stack size has been exceeded. + * "Maximum call stack size exceeded" in V8. + */ +export function isStackOverflowError(err: Error): boolean { + if (maxStackErrorMessage === undefined) { + try { + // deno-lint-ignore no-inner-declarations + function overflowStack() { + overflowStack(); + } + overflowStack(); + // deno-lint-ignore no-explicit-any + } catch (err: any) { + maxStackErrorMessage = err.message; + maxStackErrorName = err.name; + } + } + + return err && err.name === maxStackErrorName && + err.message === maxStackErrorMessage; +} + +function addNumericalSeparator(val: string) { + let res = ""; + let i = val.length; + const start = val[0] === "-" ? 1 : 0; + for (; i >= start + 4; i -= 3) { + res = `_${val.slice(i - 3, i)}${res}`; + } + return `${val.slice(0, i)}${res}`; +} + +const captureLargerStackTrace = hideStackFrames( + function captureLargerStackTrace(err) { + // @ts-ignore this function is not available in lib.dom.d.ts + Error.captureStackTrace(err); + + return err; + }, +); + +export interface ErrnoException extends Error { + errno?: number; + code?: string; + path?: string; + syscall?: string; + spawnargs?: string[]; +} + +/** + * This creates an error compatible with errors produced in the C++ + * This function should replace the deprecated + * `exceptionWithHostPort()` function. + * + * @param err A libuv error number + * @param syscall + * @param address + * @param port + * @return The error. + */ +export const uvExceptionWithHostPort = hideStackFrames( + function uvExceptionWithHostPort( + err: number, + syscall: string, + address?: string | null, + port?: number | null, + ) { + const { 0: code, 1: uvmsg } = uvErrmapGet(err) || uvUnmappedError; + const message = `${syscall} ${code}: ${uvmsg}`; + let details = ""; + + if (port && port > 0) { + details = ` ${address}:${port}`; + } else if (address) { + details = ` ${address}`; + } + + // deno-lint-ignore no-explicit-any + const ex: any = new Error(`${message}${details}`); + ex.code = code; + ex.errno = err; + ex.syscall = syscall; + ex.address = address; + + if (port) { + ex.port = port; + } + + return captureLargerStackTrace(ex); + }, +); + +/** + * This used to be `util._errnoException()`. + * + * @param err A libuv error number + * @param syscall + * @param original + * @return A `ErrnoException` + */ +export const errnoException = hideStackFrames(function errnoException( + err, + syscall, + original?, +): ErrnoException { + const code = getSystemErrorName(err); + const message = original + ? `${syscall} ${code} ${original}` + : `${syscall} ${code}`; + + // deno-lint-ignore no-explicit-any + const ex: any = new Error(message); + ex.errno = err; + ex.code = code; + ex.syscall = syscall; + + return captureLargerStackTrace(ex); +}); + +function uvErrmapGet(name: number) { + return errorMap.get(name); +} + +const uvUnmappedError = ["UNKNOWN", "unknown error"]; + +/** + * This creates an error compatible with errors produced in the C++ + * function UVException using a context object with data assembled in C++. + * The goal is to migrate them to ERR_* errors later when compatibility is + * not a concern. + * + * @param ctx + * @return The error. + */ +export const uvException = hideStackFrames(function uvException(ctx) { + const { 0: code, 1: uvmsg } = uvErrmapGet(ctx.errno) || uvUnmappedError; + + let message = `${code}: ${ctx.message || uvmsg}, ${ctx.syscall}`; + + let path; + let dest; + + if (ctx.path) { + path = ctx.path.toString(); + message += ` '${path}'`; + } + if (ctx.dest) { + dest = ctx.dest.toString(); + message += ` -> '${dest}'`; + } + + // deno-lint-ignore no-explicit-any + const err: any = new Error(message); + + for (const prop of Object.keys(ctx)) { + if (prop === "message" || prop === "path" || prop === "dest") { + continue; + } + + err[prop] = ctx[prop]; + } + + err.code = code; + + if (path) { + err.path = path; + } + + if (dest) { + err.dest = dest; + } + + return captureLargerStackTrace(err); +}); + +/** + * Deprecated, new function is `uvExceptionWithHostPort()` + * New function added the error description directly + * from C++. this method for backwards compatibility + * @param err A libuv error number + * @param syscall + * @param address + * @param port + * @param additional + */ +export const exceptionWithHostPort = hideStackFrames( + function exceptionWithHostPort( + err: number, + syscall: string, + address: string, + port: number, + additional?: string, + ) { + const code = getSystemErrorName(err); + let details = ""; + + if (port && port > 0) { + details = ` ${address}:${port}`; + } else if (address) { + details = ` ${address}`; + } + + if (additional) { + details += ` - Local (${additional})`; + } + + // deno-lint-ignore no-explicit-any + const ex: any = new Error(`${syscall} ${code}${details}`); + ex.errno = err; + ex.code = code; + ex.syscall = syscall; + ex.address = address; + + if (port) { + ex.port = port; + } + + return captureLargerStackTrace(ex); + }, +); + +/** + * @param code A libuv error number or a c-ares error code + * @param syscall + * @param hostname + */ +export const dnsException = hideStackFrames(function (code, syscall, hostname) { + let errno; + + // If `code` is of type number, it is a libuv error number, else it is a + // c-ares error code. + if (typeof code === "number") { + errno = code; + // ENOTFOUND is not a proper POSIX error, but this error has been in place + // long enough that it's not practical to remove it. + if ( + code === codeMap.get("EAI_NODATA") || + code === codeMap.get("EAI_NONAME") + ) { + code = "ENOTFOUND"; // Fabricated error name. + } else { + code = getSystemErrorName(code); + } + } + + const message = `${syscall} ${code}${hostname ? ` ${hostname}` : ""}`; + + // deno-lint-ignore no-explicit-any + const ex: any = new Error(message); + ex.errno = errno; + ex.code = code; + ex.syscall = syscall; + + if (hostname) { + ex.hostname = hostname; + } + + return captureLargerStackTrace(ex); +}); + +/** + * All error instances in Node have additional methods and properties + * This export class is meant to be extended by these instances abstracting native JS error instances + */ +export class NodeErrorAbstraction extends Error { + code: string; + + constructor(name: string, code: string, message: string) { + super(message); + this.code = code; + this.name = name; + //This number changes depending on the name of this class + //20 characters as of now + this.stack = this.stack && `${name} [${this.code}]${this.stack.slice(20)}`; + } + + override toString() { + return `${this.name} [${this.code}]: ${this.message}`; + } +} + +export class NodeError extends NodeErrorAbstraction { + constructor(code: string, message: string) { + super(Error.prototype.name, code, message); + } +} + +export class NodeSyntaxError extends NodeErrorAbstraction + implements SyntaxError { + constructor(code: string, message: string) { + super(SyntaxError.prototype.name, code, message); + Object.setPrototypeOf(this, SyntaxError.prototype); + this.toString = function () { + return `${this.name} [${this.code}]: ${this.message}`; + }; + } +} + +export class NodeRangeError extends NodeErrorAbstraction { + constructor(code: string, message: string) { + super(RangeError.prototype.name, code, message); + Object.setPrototypeOf(this, RangeError.prototype); + this.toString = function () { + return `${this.name} [${this.code}]: ${this.message}`; + }; + } +} + +export class NodeTypeError extends NodeErrorAbstraction implements TypeError { + constructor(code: string, message: string) { + super(TypeError.prototype.name, code, message); + Object.setPrototypeOf(this, TypeError.prototype); + this.toString = function () { + return `${this.name} [${this.code}]: ${this.message}`; + }; + } +} + +export class NodeURIError extends NodeErrorAbstraction implements URIError { + constructor(code: string, message: string) { + super(URIError.prototype.name, code, message); + Object.setPrototypeOf(this, URIError.prototype); + this.toString = function () { + return `${this.name} [${this.code}]: ${this.message}`; + }; + } +} + +export interface NodeSystemErrorCtx { + code: string; + syscall: string; + message: string; + errno: number; + path?: string; + dest?: string; +} +// A specialized Error that includes an additional info property with +// additional information about the error condition. +// It has the properties present in a UVException but with a custom error +// message followed by the uv error code and uv error message. +// It also has its own error code with the original uv error context put into +// `err.info`. +// The context passed into this error must have .code, .syscall and .message, +// and may have .path and .dest. +class NodeSystemError extends NodeErrorAbstraction { + constructor(key: string, context: NodeSystemErrorCtx, msgPrefix: string) { + let message = `${msgPrefix}: ${context.syscall} returned ` + + `${context.code} (${context.message})`; + + if (context.path !== undefined) { + message += ` ${context.path}`; + } + if (context.dest !== undefined) { + message += ` => ${context.dest}`; + } + + super("SystemError", key, message); + + captureLargerStackTrace(this); + + Object.defineProperties(this, { + [kIsNodeError]: { + value: true, + enumerable: false, + writable: false, + configurable: true, + }, + info: { + value: context, + enumerable: true, + configurable: true, + writable: false, + }, + errno: { + get() { + return context.errno; + }, + set: (value) => { + context.errno = value; + }, + enumerable: true, + configurable: true, + }, + syscall: { + get() { + return context.syscall; + }, + set: (value) => { + context.syscall = value; + }, + enumerable: true, + configurable: true, + }, + }); + + if (context.path !== undefined) { + Object.defineProperty(this, "path", { + get() { + return context.path; + }, + set: (value) => { + context.path = value; + }, + enumerable: true, + configurable: true, + }); + } + + if (context.dest !== undefined) { + Object.defineProperty(this, "dest", { + get() { + return context.dest; + }, + set: (value) => { + context.dest = value; + }, + enumerable: true, + configurable: true, + }); + } + } + + override toString() { + return `${this.name} [${this.code}]: ${this.message}`; + } +} + +function makeSystemErrorWithCode(key: string, msgPrfix: string) { + return class NodeError extends NodeSystemError { + constructor(ctx: NodeSystemErrorCtx) { + super(key, ctx, msgPrfix); + } + }; +} + +export const ERR_FS_EISDIR = makeSystemErrorWithCode( + "ERR_FS_EISDIR", + "Path is a directory", +); + +function createInvalidArgType( + name: string, + expected: string | string[], +): string { + // https://github.com/nodejs/node/blob/f3eb224/lib/internal/errors.js#L1037-L1087 + expected = Array.isArray(expected) ? expected : [expected]; + let msg = "The "; + if (name.endsWith(" argument")) { + // For cases like 'first argument' + msg += `${name} `; + } else { + const type = name.includes(".") ? "property" : "argument"; + msg += `"${name}" ${type} `; + } + msg += "must be "; + + const types = []; + const instances = []; + const other = []; + for (const value of expected) { + if (kTypes.includes(value)) { + types.push(value.toLocaleLowerCase()); + } else if (classRegExp.test(value)) { + instances.push(value); + } else { + other.push(value); + } + } + + // Special handle `object` in case other instances are allowed to outline + // the differences between each other. + if (instances.length > 0) { + const pos = types.indexOf("object"); + if (pos !== -1) { + types.splice(pos, 1); + instances.push("Object"); + } + } + + if (types.length > 0) { + if (types.length > 2) { + const last = types.pop(); + msg += `one of type ${types.join(", ")}, or ${last}`; + } else if (types.length === 2) { + msg += `one of type ${types[0]} or ${types[1]}`; + } else { + msg += `of type ${types[0]}`; + } + if (instances.length > 0 || other.length > 0) { + msg += " or "; + } + } + + if (instances.length > 0) { + if (instances.length > 2) { + const last = instances.pop(); + msg += `an instance of ${instances.join(", ")}, or ${last}`; + } else { + msg += `an instance of ${instances[0]}`; + if (instances.length === 2) { + msg += ` or ${instances[1]}`; + } + } + if (other.length > 0) { + msg += " or "; + } + } + + if (other.length > 0) { + if (other.length > 2) { + const last = other.pop(); + msg += `one of ${other.join(", ")}, or ${last}`; + } else if (other.length === 2) { + msg += `one of ${other[0]} or ${other[1]}`; + } else { + if (other[0].toLowerCase() !== other[0]) { + msg += "an "; + } + msg += `${other[0]}`; + } + } + + return msg; +} + +export class ERR_INVALID_ARG_TYPE_RANGE extends NodeRangeError { + constructor(name: string, expected: string | string[], actual: unknown) { + const msg = createInvalidArgType(name, expected); + + super("ERR_INVALID_ARG_TYPE", `${msg}.${invalidArgTypeHelper(actual)}`); + } +} + +export class ERR_INVALID_ARG_TYPE extends NodeTypeError { + constructor(name: string, expected: string | string[], actual: unknown) { + const msg = createInvalidArgType(name, expected); + + super("ERR_INVALID_ARG_TYPE", `${msg}.${invalidArgTypeHelper(actual)}`); + } + + static RangeError = ERR_INVALID_ARG_TYPE_RANGE; +} + +export class ERR_INVALID_ARG_VALUE_RANGE extends NodeRangeError { + constructor(name: string, value: unknown, reason: string = "is invalid") { + const type = name.includes(".") ? "property" : "argument"; + const inspected = inspect(value); + + super( + "ERR_INVALID_ARG_VALUE", + `The ${type} '${name}' ${reason}. Received ${inspected}`, + ); + } +} + +export class ERR_INVALID_ARG_VALUE extends NodeTypeError { + constructor(name: string, value: unknown, reason: string = "is invalid") { + const type = name.includes(".") ? "property" : "argument"; + const inspected = inspect(value); + + super( + "ERR_INVALID_ARG_VALUE", + `The ${type} '${name}' ${reason}. Received ${inspected}`, + ); + } + + static RangeError = ERR_INVALID_ARG_VALUE_RANGE; +} + +// A helper function to simplify checking for ERR_INVALID_ARG_TYPE output. +// deno-lint-ignore no-explicit-any +function invalidArgTypeHelper(input: any) { + if (input == null) { + return ` Received ${input}`; + } + if (typeof input === "function" && input.name) { + return ` Received function ${input.name}`; + } + if (typeof input === "object") { + if (input.constructor && input.constructor.name) { + return ` Received an instance of ${input.constructor.name}`; + } + return ` Received ${inspect(input, { depth: -1 })}`; + } + let inspected = inspect(input, { colors: false }); + if (inspected.length > 25) { + inspected = `${inspected.slice(0, 25)}...`; + } + return ` Received type ${typeof input} (${inspected})`; +} + +export class ERR_OUT_OF_RANGE extends RangeError { + code = "ERR_OUT_OF_RANGE"; + + constructor( + str: string, + range: string, + input: unknown, + replaceDefaultBoolean = false, + ) { + assert(range, 'Missing "range" argument'); + let msg = replaceDefaultBoolean + ? str + : `The value of "${str}" is out of range.`; + let received; + if (Number.isInteger(input) && Math.abs(input as number) > 2 ** 32) { + received = addNumericalSeparator(String(input)); + } else if (typeof input === "bigint") { + received = String(input); + if (input > 2n ** 32n || input < -(2n ** 32n)) { + received = addNumericalSeparator(received); + } + received += "n"; + } else { + received = inspect(input); + } + msg += ` It must be ${range}. Received ${received}`; + + super(msg); + + const { name } = this; + // Add the error code to the name to include it in the stack trace. + this.name = `${name} [${this.code}]`; + // Access the stack to generate the error message including the error code from the name. + this.stack; + // Reset the name to the actual name. + this.name = name; + } +} + +export class ERR_AMBIGUOUS_ARGUMENT extends NodeTypeError { + constructor(x: string, y: string) { + super("ERR_AMBIGUOUS_ARGUMENT", `The "${x}" argument is ambiguous. ${y}`); + } +} + +export class ERR_ARG_NOT_ITERABLE extends NodeTypeError { + constructor(x: string) { + super("ERR_ARG_NOT_ITERABLE", `${x} must be iterable`); + } +} + +export class ERR_ASSERTION extends NodeError { + constructor(x: string) { + super("ERR_ASSERTION", `${x}`); + } +} + +export class ERR_ASYNC_CALLBACK extends NodeTypeError { + constructor(x: string) { + super("ERR_ASYNC_CALLBACK", `${x} must be a function`); + } +} + +export class ERR_ASYNC_TYPE extends NodeTypeError { + constructor(x: string) { + super("ERR_ASYNC_TYPE", `Invalid name for async "type": ${x}`); + } +} + +export class ERR_BROTLI_INVALID_PARAM extends NodeRangeError { + constructor(x: string) { + super("ERR_BROTLI_INVALID_PARAM", `${x} is not a valid Brotli parameter`); + } +} + +export class ERR_BUFFER_OUT_OF_BOUNDS extends NodeRangeError { + constructor(name?: string) { + super( + "ERR_BUFFER_OUT_OF_BOUNDS", + name + ? `"${name}" is outside of buffer bounds` + : "Attempt to access memory outside buffer bounds", + ); + } +} + +export class ERR_BUFFER_TOO_LARGE extends NodeRangeError { + constructor(x: string) { + super( + "ERR_BUFFER_TOO_LARGE", + `Cannot create a Buffer larger than ${x} bytes`, + ); + } +} + +export class ERR_CANNOT_WATCH_SIGINT extends NodeError { + constructor() { + super("ERR_CANNOT_WATCH_SIGINT", "Cannot watch for SIGINT signals"); + } +} + +export class ERR_CHILD_CLOSED_BEFORE_REPLY extends NodeError { + constructor() { + super( + "ERR_CHILD_CLOSED_BEFORE_REPLY", + "Child closed before reply received", + ); + } +} + +export class ERR_CHILD_PROCESS_IPC_REQUIRED extends NodeError { + constructor(x: string) { + super( + "ERR_CHILD_PROCESS_IPC_REQUIRED", + `Forked processes must have an IPC channel, missing value 'ipc' in ${x}`, + ); + } +} + +export class ERR_CHILD_PROCESS_STDIO_MAXBUFFER extends NodeRangeError { + constructor(x: string) { + super( + "ERR_CHILD_PROCESS_STDIO_MAXBUFFER", + `${x} maxBuffer length exceeded`, + ); + } +} + +export class ERR_CONSOLE_WRITABLE_STREAM extends NodeTypeError { + constructor(x: string) { + super( + "ERR_CONSOLE_WRITABLE_STREAM", + `Console expects a writable stream instance for ${x}`, + ); + } +} + +export class ERR_CONTEXT_NOT_INITIALIZED extends NodeError { + constructor() { + super("ERR_CONTEXT_NOT_INITIALIZED", "context used is not initialized"); + } +} + +export class ERR_CPU_USAGE extends NodeError { + constructor(x: string) { + super("ERR_CPU_USAGE", `Unable to obtain cpu usage ${x}`); + } +} + +export class ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED extends NodeError { + constructor() { + super( + "ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED", + "Custom engines not supported by this OpenSSL", + ); + } +} + +export class ERR_CRYPTO_ECDH_INVALID_FORMAT extends NodeTypeError { + constructor(x: string) { + super("ERR_CRYPTO_ECDH_INVALID_FORMAT", `Invalid ECDH format: ${x}`); + } +} + +export class ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY extends NodeError { + constructor() { + super( + "ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY", + "Public key is not valid for specified curve", + ); + } +} + +export class ERR_CRYPTO_ENGINE_UNKNOWN extends NodeError { + constructor(x: string) { + super("ERR_CRYPTO_ENGINE_UNKNOWN", `Engine "${x}" was not found`); + } +} + +export class ERR_CRYPTO_FIPS_FORCED extends NodeError { + constructor() { + super( + "ERR_CRYPTO_FIPS_FORCED", + "Cannot set FIPS mode, it was forced with --force-fips at startup.", + ); + } +} + +export class ERR_CRYPTO_FIPS_UNAVAILABLE extends NodeError { + constructor() { + super( + "ERR_CRYPTO_FIPS_UNAVAILABLE", + "Cannot set FIPS mode in a non-FIPS build.", + ); + } +} + +export class ERR_CRYPTO_HASH_FINALIZED extends NodeError { + constructor() { + super("ERR_CRYPTO_HASH_FINALIZED", "Digest already called"); + } +} + +export class ERR_CRYPTO_HASH_UPDATE_FAILED extends NodeError { + constructor() { + super("ERR_CRYPTO_HASH_UPDATE_FAILED", "Hash update failed"); + } +} + +export class ERR_CRYPTO_INCOMPATIBLE_KEY extends NodeError { + constructor(x: string, y: string) { + super("ERR_CRYPTO_INCOMPATIBLE_KEY", `Incompatible ${x}: ${y}`); + } +} + +export class ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS extends NodeError { + constructor(x: string, y: string) { + super( + "ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS", + `The selected key encoding ${x} ${y}.`, + ); + } +} + +export class ERR_CRYPTO_INVALID_DIGEST extends NodeTypeError { + constructor(x: string) { + super("ERR_CRYPTO_INVALID_DIGEST", `Invalid digest: ${x}`); + } +} + +export class ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE extends NodeTypeError { + constructor(x: string, y: string) { + super( + "ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE", + `Invalid key object type ${x}, expected ${y}.`, + ); + } +} + +export class ERR_CRYPTO_INVALID_STATE extends NodeError { + constructor(x: string) { + super("ERR_CRYPTO_INVALID_STATE", `Invalid state for operation ${x}`); + } +} + +export class ERR_CRYPTO_PBKDF2_ERROR extends NodeError { + constructor() { + super("ERR_CRYPTO_PBKDF2_ERROR", "PBKDF2 error"); + } +} + +export class ERR_CRYPTO_SCRYPT_INVALID_PARAMETER extends NodeError { + constructor() { + super("ERR_CRYPTO_SCRYPT_INVALID_PARAMETER", "Invalid scrypt parameter"); + } +} + +export class ERR_CRYPTO_SCRYPT_NOT_SUPPORTED extends NodeError { + constructor() { + super("ERR_CRYPTO_SCRYPT_NOT_SUPPORTED", "Scrypt algorithm not supported"); + } +} + +export class ERR_CRYPTO_SIGN_KEY_REQUIRED extends NodeError { + constructor() { + super("ERR_CRYPTO_SIGN_KEY_REQUIRED", "No key provided to sign"); + } +} + +export class ERR_DIR_CLOSED extends NodeError { + constructor() { + super("ERR_DIR_CLOSED", "Directory handle was closed"); + } +} + +export class ERR_DIR_CONCURRENT_OPERATION extends NodeError { + constructor() { + super( + "ERR_DIR_CONCURRENT_OPERATION", + "Cannot do synchronous work on directory handle with concurrent asynchronous operations", + ); + } +} + +export class ERR_DNS_SET_SERVERS_FAILED extends NodeError { + constructor(x: string, y: string) { + super( + "ERR_DNS_SET_SERVERS_FAILED", + `c-ares failed to set servers: "${x}" [${y}]`, + ); + } +} + +export class ERR_DOMAIN_CALLBACK_NOT_AVAILABLE extends NodeError { + constructor() { + super( + "ERR_DOMAIN_CALLBACK_NOT_AVAILABLE", + "A callback was registered through " + + "process.setUncaughtExceptionCaptureCallback(), which is mutually " + + "exclusive with using the `domain` module", + ); + } +} + +export class ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE + extends NodeError { + constructor() { + super( + "ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE", + "The `domain` module is in use, which is mutually exclusive with calling " + + "process.setUncaughtExceptionCaptureCallback()", + ); + } +} + +export class ERR_ENCODING_INVALID_ENCODED_DATA extends NodeErrorAbstraction + implements TypeError { + errno: number; + constructor(encoding: string, ret: number) { + super( + TypeError.prototype.name, + "ERR_ENCODING_INVALID_ENCODED_DATA", + `The encoded data was not valid for encoding ${encoding}`, + ); + Object.setPrototypeOf(this, TypeError.prototype); + + this.errno = ret; + } +} + +export class ERR_ENCODING_NOT_SUPPORTED extends NodeRangeError { + constructor(x: string) { + super("ERR_ENCODING_NOT_SUPPORTED", `The "${x}" encoding is not supported`); + } +} +export class ERR_EVAL_ESM_CANNOT_PRINT extends NodeError { + constructor() { + super("ERR_EVAL_ESM_CANNOT_PRINT", `--print cannot be used with ESM input`); + } +} +export class ERR_EVENT_RECURSION extends NodeError { + constructor(x: string) { + super( + "ERR_EVENT_RECURSION", + `The event "${x}" is already being dispatched`, + ); + } +} +export class ERR_FEATURE_UNAVAILABLE_ON_PLATFORM extends NodeTypeError { + constructor(x: string) { + super( + "ERR_FEATURE_UNAVAILABLE_ON_PLATFORM", + `The feature ${x} is unavailable on the current platform, which is being used to run Node.js`, + ); + } +} +export class ERR_FS_FILE_TOO_LARGE extends NodeRangeError { + constructor(x: string) { + super("ERR_FS_FILE_TOO_LARGE", `File size (${x}) is greater than 2 GB`); + } +} +export class ERR_FS_INVALID_SYMLINK_TYPE extends NodeError { + constructor(x: string) { + super( + "ERR_FS_INVALID_SYMLINK_TYPE", + `Symlink type must be one of "dir", "file", or "junction". Received "${x}"`, + ); + } +} +export class ERR_HTTP2_ALTSVC_INVALID_ORIGIN extends NodeTypeError { + constructor() { + super( + "ERR_HTTP2_ALTSVC_INVALID_ORIGIN", + `HTTP/2 ALTSVC frames require a valid origin`, + ); + } +} +export class ERR_HTTP2_ALTSVC_LENGTH extends NodeTypeError { + constructor() { + super( + "ERR_HTTP2_ALTSVC_LENGTH", + `HTTP/2 ALTSVC frames are limited to 16382 bytes`, + ); + } +} +export class ERR_HTTP2_CONNECT_AUTHORITY extends NodeError { + constructor() { + super( + "ERR_HTTP2_CONNECT_AUTHORITY", + `:authority header is required for CONNECT requests`, + ); + } +} +export class ERR_HTTP2_CONNECT_PATH extends NodeError { + constructor() { + super( + "ERR_HTTP2_CONNECT_PATH", + `The :path header is forbidden for CONNECT requests`, + ); + } +} +export class ERR_HTTP2_CONNECT_SCHEME extends NodeError { + constructor() { + super( + "ERR_HTTP2_CONNECT_SCHEME", + `The :scheme header is forbidden for CONNECT requests`, + ); + } +} +export class ERR_HTTP2_GOAWAY_SESSION extends NodeError { + constructor() { + super( + "ERR_HTTP2_GOAWAY_SESSION", + `New streams cannot be created after receiving a GOAWAY`, + ); + } +} +export class ERR_HTTP2_HEADERS_AFTER_RESPOND extends NodeError { + constructor() { + super( + "ERR_HTTP2_HEADERS_AFTER_RESPOND", + `Cannot specify additional headers after response initiated`, + ); + } +} +export class ERR_HTTP2_HEADERS_SENT extends NodeError { + constructor() { + super("ERR_HTTP2_HEADERS_SENT", `Response has already been initiated.`); + } +} +export class ERR_HTTP2_HEADER_SINGLE_VALUE extends NodeTypeError { + constructor(x: string) { + super( + "ERR_HTTP2_HEADER_SINGLE_VALUE", + `Header field "${x}" must only have a single value`, + ); + } +} +export class ERR_HTTP2_INFO_STATUS_NOT_ALLOWED extends NodeRangeError { + constructor() { + super( + "ERR_HTTP2_INFO_STATUS_NOT_ALLOWED", + `Informational status codes cannot be used`, + ); + } +} +export class ERR_HTTP2_INVALID_CONNECTION_HEADERS extends NodeTypeError { + constructor(x: string) { + super( + "ERR_HTTP2_INVALID_CONNECTION_HEADERS", + `HTTP/1 Connection specific headers are forbidden: "${x}"`, + ); + } +} +export class ERR_HTTP2_INVALID_HEADER_VALUE extends NodeTypeError { + constructor(x: string, y: string) { + super( + "ERR_HTTP2_INVALID_HEADER_VALUE", + `Invalid value "${x}" for header "${y}"`, + ); + } +} +export class ERR_HTTP2_INVALID_INFO_STATUS extends NodeRangeError { + constructor(x: string) { + super( + "ERR_HTTP2_INVALID_INFO_STATUS", + `Invalid informational status code: ${x}`, + ); + } +} +export class ERR_HTTP2_INVALID_ORIGIN extends NodeTypeError { + constructor() { + super( + "ERR_HTTP2_INVALID_ORIGIN", + `HTTP/2 ORIGIN frames require a valid origin`, + ); + } +} +export class ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH extends NodeRangeError { + constructor() { + super( + "ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH", + `Packed settings length must be a multiple of six`, + ); + } +} +export class ERR_HTTP2_INVALID_PSEUDOHEADER extends NodeTypeError { + constructor(x: string) { + super( + "ERR_HTTP2_INVALID_PSEUDOHEADER", + `"${x}" is an invalid pseudoheader or is used incorrectly`, + ); + } +} +export class ERR_HTTP2_INVALID_SESSION extends NodeError { + constructor() { + super("ERR_HTTP2_INVALID_SESSION", `The session has been destroyed`); + } +} +export class ERR_HTTP2_INVALID_STREAM extends NodeError { + constructor() { + super("ERR_HTTP2_INVALID_STREAM", `The stream has been destroyed`); + } +} +export class ERR_HTTP2_MAX_PENDING_SETTINGS_ACK extends NodeError { + constructor() { + super( + "ERR_HTTP2_MAX_PENDING_SETTINGS_ACK", + `Maximum number of pending settings acknowledgements`, + ); + } +} +export class ERR_HTTP2_NESTED_PUSH extends NodeError { + constructor() { + super( + "ERR_HTTP2_NESTED_PUSH", + `A push stream cannot initiate another push stream.`, + ); + } +} +export class ERR_HTTP2_NO_SOCKET_MANIPULATION extends NodeError { + constructor() { + super( + "ERR_HTTP2_NO_SOCKET_MANIPULATION", + `HTTP/2 sockets should not be directly manipulated (e.g. read and written)`, + ); + } +} +export class ERR_HTTP2_ORIGIN_LENGTH extends NodeTypeError { + constructor() { + super( + "ERR_HTTP2_ORIGIN_LENGTH", + `HTTP/2 ORIGIN frames are limited to 16382 bytes`, + ); + } +} +export class ERR_HTTP2_OUT_OF_STREAMS extends NodeError { + constructor() { + super( + "ERR_HTTP2_OUT_OF_STREAMS", + `No stream ID is available because maximum stream ID has been reached`, + ); + } +} +export class ERR_HTTP2_PAYLOAD_FORBIDDEN extends NodeError { + constructor(x: string) { + super( + "ERR_HTTP2_PAYLOAD_FORBIDDEN", + `Responses with ${x} status must not have a payload`, + ); + } +} +export class ERR_HTTP2_PING_CANCEL extends NodeError { + constructor() { + super("ERR_HTTP2_PING_CANCEL", `HTTP2 ping cancelled`); + } +} +export class ERR_HTTP2_PING_LENGTH extends NodeRangeError { + constructor() { + super("ERR_HTTP2_PING_LENGTH", `HTTP2 ping payload must be 8 bytes`); + } +} +export class ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED extends NodeTypeError { + constructor() { + super( + "ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED", + `Cannot set HTTP/2 pseudo-headers`, + ); + } +} +export class ERR_HTTP2_PUSH_DISABLED extends NodeError { + constructor() { + super("ERR_HTTP2_PUSH_DISABLED", `HTTP/2 client has disabled push streams`); + } +} +export class ERR_HTTP2_SEND_FILE extends NodeError { + constructor() { + super("ERR_HTTP2_SEND_FILE", `Directories cannot be sent`); + } +} +export class ERR_HTTP2_SEND_FILE_NOSEEK extends NodeError { + constructor() { + super( + "ERR_HTTP2_SEND_FILE_NOSEEK", + `Offset or length can only be specified for regular files`, + ); + } +} +export class ERR_HTTP2_SESSION_ERROR extends NodeError { + constructor(x: string) { + super("ERR_HTTP2_SESSION_ERROR", `Session closed with error code ${x}`); + } +} +export class ERR_HTTP2_SETTINGS_CANCEL extends NodeError { + constructor() { + super("ERR_HTTP2_SETTINGS_CANCEL", `HTTP2 session settings canceled`); + } +} +export class ERR_HTTP2_SOCKET_BOUND extends NodeError { + constructor() { + super( + "ERR_HTTP2_SOCKET_BOUND", + `The socket is already bound to an Http2Session`, + ); + } +} +export class ERR_HTTP2_SOCKET_UNBOUND extends NodeError { + constructor() { + super( + "ERR_HTTP2_SOCKET_UNBOUND", + `The socket has been disconnected from the Http2Session`, + ); + } +} +export class ERR_HTTP2_STATUS_101 extends NodeError { + constructor() { + super( + "ERR_HTTP2_STATUS_101", + `HTTP status code 101 (Switching Protocols) is forbidden in HTTP/2`, + ); + } +} +export class ERR_HTTP2_STATUS_INVALID extends NodeRangeError { + constructor(x: string) { + super("ERR_HTTP2_STATUS_INVALID", `Invalid status code: ${x}`); + } +} +export class ERR_HTTP2_STREAM_ERROR extends NodeError { + constructor(x: string) { + super("ERR_HTTP2_STREAM_ERROR", `Stream closed with error code ${x}`); + } +} +export class ERR_HTTP2_STREAM_SELF_DEPENDENCY extends NodeError { + constructor() { + super( + "ERR_HTTP2_STREAM_SELF_DEPENDENCY", + `A stream cannot depend on itself`, + ); + } +} +export class ERR_HTTP2_TRAILERS_ALREADY_SENT extends NodeError { + constructor() { + super( + "ERR_HTTP2_TRAILERS_ALREADY_SENT", + `Trailing headers have already been sent`, + ); + } +} +export class ERR_HTTP2_TRAILERS_NOT_READY extends NodeError { + constructor() { + super( + "ERR_HTTP2_TRAILERS_NOT_READY", + `Trailing headers cannot be sent until after the wantTrailers event is emitted`, + ); + } +} +export class ERR_HTTP2_UNSUPPORTED_PROTOCOL extends NodeError { + constructor(x: string) { + super("ERR_HTTP2_UNSUPPORTED_PROTOCOL", `protocol "${x}" is unsupported.`); + } +} +export class ERR_HTTP_HEADERS_SENT extends NodeError { + constructor(x: string) { + super( + "ERR_HTTP_HEADERS_SENT", + `Cannot ${x} headers after they are sent to the client`, + ); + } +} +export class ERR_HTTP_INVALID_HEADER_VALUE extends NodeTypeError { + constructor(x: string, y: string) { + super( + "ERR_HTTP_INVALID_HEADER_VALUE", + `Invalid value "${x}" for header "${y}"`, + ); + } +} +export class ERR_HTTP_INVALID_STATUS_CODE extends NodeRangeError { + constructor(x: string) { + super("ERR_HTTP_INVALID_STATUS_CODE", `Invalid status code: ${x}`); + } +} +export class ERR_HTTP_SOCKET_ENCODING extends NodeError { + constructor() { + super( + "ERR_HTTP_SOCKET_ENCODING", + `Changing the socket encoding is not allowed per RFC7230 Section 3.`, + ); + } +} +export class ERR_HTTP_TRAILER_INVALID extends NodeError { + constructor() { + super( + "ERR_HTTP_TRAILER_INVALID", + `Trailers are invalid with this transfer encoding`, + ); + } +} +export class ERR_INCOMPATIBLE_OPTION_PAIR extends NodeTypeError { + constructor(x: string, y: string) { + super( + "ERR_INCOMPATIBLE_OPTION_PAIR", + `Option "${x}" cannot be used in combination with option "${y}"`, + ); + } +} +export class ERR_INPUT_TYPE_NOT_ALLOWED extends NodeError { + constructor() { + super( + "ERR_INPUT_TYPE_NOT_ALLOWED", + `--input-type can only be used with string input via --eval, --print, or STDIN`, + ); + } +} +export class ERR_INSPECTOR_ALREADY_ACTIVATED extends NodeError { + constructor() { + super( + "ERR_INSPECTOR_ALREADY_ACTIVATED", + `Inspector is already activated. Close it with inspector.close() before activating it again.`, + ); + } +} +export class ERR_INSPECTOR_ALREADY_CONNECTED extends NodeError { + constructor(x: string) { + super("ERR_INSPECTOR_ALREADY_CONNECTED", `${x} is already connected`); + } +} +export class ERR_INSPECTOR_CLOSED extends NodeError { + constructor() { + super("ERR_INSPECTOR_CLOSED", `Session was closed`); + } +} +export class ERR_INSPECTOR_COMMAND extends NodeError { + constructor(x: number, y: string) { + super("ERR_INSPECTOR_COMMAND", `Inspector error ${x}: ${y}`); + } +} +export class ERR_INSPECTOR_NOT_ACTIVE extends NodeError { + constructor() { + super("ERR_INSPECTOR_NOT_ACTIVE", `Inspector is not active`); + } +} +export class ERR_INSPECTOR_NOT_AVAILABLE extends NodeError { + constructor() { + super("ERR_INSPECTOR_NOT_AVAILABLE", `Inspector is not available`); + } +} +export class ERR_INSPECTOR_NOT_CONNECTED extends NodeError { + constructor() { + super("ERR_INSPECTOR_NOT_CONNECTED", `Session is not connected`); + } +} +export class ERR_INSPECTOR_NOT_WORKER extends NodeError { + constructor() { + super("ERR_INSPECTOR_NOT_WORKER", `Current thread is not a worker`); + } +} +export class ERR_INVALID_ASYNC_ID extends NodeRangeError { + constructor(x: string, y: string | number) { + super("ERR_INVALID_ASYNC_ID", `Invalid ${x} value: ${y}`); + } +} +export class ERR_INVALID_BUFFER_SIZE extends NodeRangeError { + constructor(x: string) { + super("ERR_INVALID_BUFFER_SIZE", `Buffer size must be a multiple of ${x}`); + } +} +export class ERR_INVALID_CURSOR_POS extends NodeTypeError { + constructor() { + super( + "ERR_INVALID_CURSOR_POS", + `Cannot set cursor row without setting its column`, + ); + } +} +export class ERR_INVALID_FD extends NodeRangeError { + constructor(x: string) { + super("ERR_INVALID_FD", `"fd" must be a positive integer: ${x}`); + } +} +export class ERR_INVALID_FD_TYPE extends NodeTypeError { + constructor(x: string) { + super("ERR_INVALID_FD_TYPE", `Unsupported fd type: ${x}`); + } +} +export class ERR_INVALID_FILE_URL_HOST extends NodeTypeError { + constructor(x: string) { + super( + "ERR_INVALID_FILE_URL_HOST", + `File URL host must be "localhost" or empty on ${x}`, + ); + } +} +export class ERR_INVALID_FILE_URL_PATH extends NodeTypeError { + constructor(x: string) { + super("ERR_INVALID_FILE_URL_PATH", `File URL path ${x}`); + } +} +export class ERR_INVALID_HANDLE_TYPE extends NodeTypeError { + constructor() { + super("ERR_INVALID_HANDLE_TYPE", `This handle type cannot be sent`); + } +} +export class ERR_INVALID_HTTP_TOKEN extends NodeTypeError { + constructor(x: string, y: string) { + super("ERR_INVALID_HTTP_TOKEN", `${x} must be a valid HTTP token ["${y}"]`); + } +} +export class ERR_INVALID_IP_ADDRESS extends NodeTypeError { + constructor(x: string) { + super("ERR_INVALID_IP_ADDRESS", `Invalid IP address: ${x}`); + } +} +export class ERR_INVALID_OPT_VALUE_ENCODING extends NodeTypeError { + constructor(x: string) { + super( + "ERR_INVALID_OPT_VALUE_ENCODING", + `The value "${x}" is invalid for option "encoding"`, + ); + } +} +export class ERR_INVALID_PERFORMANCE_MARK extends NodeError { + constructor(x: string) { + super( + "ERR_INVALID_PERFORMANCE_MARK", + `The "${x}" performance mark has not been set`, + ); + } +} +export class ERR_INVALID_PROTOCOL extends NodeTypeError { + constructor(x: string, y: string) { + super( + "ERR_INVALID_PROTOCOL", + `Protocol "${x}" not supported. Expected "${y}"`, + ); + } +} +export class ERR_INVALID_REPL_EVAL_CONFIG extends NodeTypeError { + constructor() { + super( + "ERR_INVALID_REPL_EVAL_CONFIG", + `Cannot specify both "breakEvalOnSigint" and "eval" for REPL`, + ); + } +} +export class ERR_INVALID_REPL_INPUT extends NodeTypeError { + constructor(x: string) { + super("ERR_INVALID_REPL_INPUT", `${x}`); + } +} +export class ERR_INVALID_SYNC_FORK_INPUT extends NodeTypeError { + constructor(x: string) { + super( + "ERR_INVALID_SYNC_FORK_INPUT", + `Asynchronous forks do not support Buffer, TypedArray, DataView or string input: ${x}`, + ); + } +} +export class ERR_INVALID_THIS extends NodeTypeError { + constructor(x: string) { + super("ERR_INVALID_THIS", `Value of "this" must be of type ${x}`); + } +} +export class ERR_INVALID_TUPLE extends NodeTypeError { + constructor(x: string, y: string) { + super("ERR_INVALID_TUPLE", `${x} must be an iterable ${y} tuple`); + } +} +export class ERR_INVALID_URI extends NodeURIError { + constructor() { + super("ERR_INVALID_URI", `URI malformed`); + } +} +export class ERR_IPC_CHANNEL_CLOSED extends NodeError { + constructor() { + super("ERR_IPC_CHANNEL_CLOSED", `Channel closed`); + } +} +export class ERR_IPC_DISCONNECTED extends NodeError { + constructor() { + super("ERR_IPC_DISCONNECTED", `IPC channel is already disconnected`); + } +} +export class ERR_IPC_ONE_PIPE extends NodeError { + constructor() { + super("ERR_IPC_ONE_PIPE", `Child process can have only one IPC pipe`); + } +} +export class ERR_IPC_SYNC_FORK extends NodeError { + constructor() { + super("ERR_IPC_SYNC_FORK", `IPC cannot be used with synchronous forks`); + } +} +export class ERR_MANIFEST_DEPENDENCY_MISSING extends NodeError { + constructor(x: string, y: string) { + super( + "ERR_MANIFEST_DEPENDENCY_MISSING", + `Manifest resource ${x} does not list ${y} as a dependency specifier`, + ); + } +} +export class ERR_MANIFEST_INTEGRITY_MISMATCH extends NodeSyntaxError { + constructor(x: string) { + super( + "ERR_MANIFEST_INTEGRITY_MISMATCH", + `Manifest resource ${x} has multiple entries but integrity lists do not match`, + ); + } +} +export class ERR_MANIFEST_INVALID_RESOURCE_FIELD extends NodeTypeError { + constructor(x: string, y: string) { + super( + "ERR_MANIFEST_INVALID_RESOURCE_FIELD", + `Manifest resource ${x} has invalid property value for ${y}`, + ); + } +} +export class ERR_MANIFEST_TDZ extends NodeError { + constructor() { + super("ERR_MANIFEST_TDZ", `Manifest initialization has not yet run`); + } +} +export class ERR_MANIFEST_UNKNOWN_ONERROR extends NodeSyntaxError { + constructor(x: string) { + super( + "ERR_MANIFEST_UNKNOWN_ONERROR", + `Manifest specified unknown error behavior "${x}".`, + ); + } +} +export class ERR_METHOD_NOT_IMPLEMENTED extends NodeError { + constructor(x: string) { + super("ERR_METHOD_NOT_IMPLEMENTED", `The ${x} method is not implemented`); + } +} +export class ERR_MISSING_ARGS extends NodeTypeError { + constructor(...args: (string | string[])[]) { + let msg = "The "; + + const len = args.length; + + const wrap = (a: unknown) => `"${a}"`; + + args = args.map((a) => + Array.isArray(a) ? a.map(wrap).join(" or ") : wrap(a) + ); + + switch (len) { + case 1: + msg += `${args[0]} argument`; + break; + case 2: + msg += `${args[0]} and ${args[1]} arguments`; + break; + default: + msg += args.slice(0, len - 1).join(", "); + msg += `, and ${args[len - 1]} arguments`; + break; + } + + super("ERR_MISSING_ARGS", `${msg} must be specified`); + } +} +export class ERR_MISSING_OPTION extends NodeTypeError { + constructor(x: string) { + super("ERR_MISSING_OPTION", `${x} is required`); + } +} +export class ERR_MULTIPLE_CALLBACK extends NodeError { + constructor() { + super("ERR_MULTIPLE_CALLBACK", `Callback called multiple times`); + } +} +export class ERR_NAPI_CONS_FUNCTION extends NodeTypeError { + constructor() { + super("ERR_NAPI_CONS_FUNCTION", `Constructor must be a function`); + } +} +export class ERR_NAPI_INVALID_DATAVIEW_ARGS extends NodeRangeError { + constructor() { + super( + "ERR_NAPI_INVALID_DATAVIEW_ARGS", + `byte_offset + byte_length should be less than or equal to the size in bytes of the array passed in`, + ); + } +} +export class ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT extends NodeRangeError { + constructor(x: string, y: string) { + super( + "ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT", + `start offset of ${x} should be a multiple of ${y}`, + ); + } +} +export class ERR_NAPI_INVALID_TYPEDARRAY_LENGTH extends NodeRangeError { + constructor() { + super("ERR_NAPI_INVALID_TYPEDARRAY_LENGTH", `Invalid typed array length`); + } +} +export class ERR_NO_CRYPTO extends NodeError { + constructor() { + super( + "ERR_NO_CRYPTO", + `Node.js is not compiled with OpenSSL crypto support`, + ); + } +} +export class ERR_NO_ICU extends NodeTypeError { + constructor(x: string) { + super( + "ERR_NO_ICU", + `${x} is not supported on Node.js compiled without ICU`, + ); + } +} +export class ERR_QUICCLIENTSESSION_FAILED extends NodeError { + constructor(x: string) { + super( + "ERR_QUICCLIENTSESSION_FAILED", + `Failed to create a new QuicClientSession: ${x}`, + ); + } +} +export class ERR_QUICCLIENTSESSION_FAILED_SETSOCKET extends NodeError { + constructor() { + super( + "ERR_QUICCLIENTSESSION_FAILED_SETSOCKET", + `Failed to set the QuicSocket`, + ); + } +} +export class ERR_QUICSESSION_DESTROYED extends NodeError { + constructor(x: string) { + super( + "ERR_QUICSESSION_DESTROYED", + `Cannot call ${x} after a QuicSession has been destroyed`, + ); + } +} +export class ERR_QUICSESSION_INVALID_DCID extends NodeError { + constructor(x: string) { + super("ERR_QUICSESSION_INVALID_DCID", `Invalid DCID value: ${x}`); + } +} +export class ERR_QUICSESSION_UPDATEKEY extends NodeError { + constructor() { + super("ERR_QUICSESSION_UPDATEKEY", `Unable to update QuicSession keys`); + } +} +export class ERR_QUICSOCKET_DESTROYED extends NodeError { + constructor(x: string) { + super( + "ERR_QUICSOCKET_DESTROYED", + `Cannot call ${x} after a QuicSocket has been destroyed`, + ); + } +} +export class ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH + extends NodeError { + constructor() { + super( + "ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH", + `The stateResetToken must be exactly 16-bytes in length`, + ); + } +} +export class ERR_QUICSOCKET_LISTENING extends NodeError { + constructor() { + super("ERR_QUICSOCKET_LISTENING", `This QuicSocket is already listening`); + } +} +export class ERR_QUICSOCKET_UNBOUND extends NodeError { + constructor(x: string) { + super( + "ERR_QUICSOCKET_UNBOUND", + `Cannot call ${x} before a QuicSocket has been bound`, + ); + } +} +export class ERR_QUICSTREAM_DESTROYED extends NodeError { + constructor(x: string) { + super( + "ERR_QUICSTREAM_DESTROYED", + `Cannot call ${x} after a QuicStream has been destroyed`, + ); + } +} +export class ERR_QUICSTREAM_INVALID_PUSH extends NodeError { + constructor() { + super( + "ERR_QUICSTREAM_INVALID_PUSH", + `Push streams are only supported on client-initiated, bidirectional streams`, + ); + } +} +export class ERR_QUICSTREAM_OPEN_FAILED extends NodeError { + constructor() { + super("ERR_QUICSTREAM_OPEN_FAILED", `Opening a new QuicStream failed`); + } +} +export class ERR_QUICSTREAM_UNSUPPORTED_PUSH extends NodeError { + constructor() { + super( + "ERR_QUICSTREAM_UNSUPPORTED_PUSH", + `Push streams are not supported on this QuicSession`, + ); + } +} +export class ERR_QUIC_TLS13_REQUIRED extends NodeError { + constructor() { + super("ERR_QUIC_TLS13_REQUIRED", `QUIC requires TLS version 1.3`); + } +} +export class ERR_SCRIPT_EXECUTION_INTERRUPTED extends NodeError { + constructor() { + super( + "ERR_SCRIPT_EXECUTION_INTERRUPTED", + "Script execution was interrupted by `SIGINT`", + ); + } +} +export class ERR_SERVER_ALREADY_LISTEN extends NodeError { + constructor() { + super( + "ERR_SERVER_ALREADY_LISTEN", + `Listen method has been called more than once without closing.`, + ); + } +} +export class ERR_SERVER_NOT_RUNNING extends NodeError { + constructor() { + super("ERR_SERVER_NOT_RUNNING", `Server is not running.`); + } +} +export class ERR_SOCKET_ALREADY_BOUND extends NodeError { + constructor() { + super("ERR_SOCKET_ALREADY_BOUND", `Socket is already bound`); + } +} +export class ERR_SOCKET_BAD_BUFFER_SIZE extends NodeTypeError { + constructor() { + super( + "ERR_SOCKET_BAD_BUFFER_SIZE", + `Buffer size must be a positive integer`, + ); + } +} +export class ERR_SOCKET_BAD_PORT extends NodeRangeError { + constructor(name: string, port: unknown, allowZero = true) { + assert( + typeof allowZero === "boolean", + "The 'allowZero' argument must be of type boolean.", + ); + + const operator = allowZero ? ">=" : ">"; + + super( + "ERR_SOCKET_BAD_PORT", + `${name} should be ${operator} 0 and < 65536. Received ${port}.`, + ); + } +} +export class ERR_SOCKET_BAD_TYPE extends NodeTypeError { + constructor() { + super( + "ERR_SOCKET_BAD_TYPE", + `Bad socket type specified. Valid types are: udp4, udp6`, + ); + } +} +export class ERR_SOCKET_BUFFER_SIZE extends NodeSystemError { + constructor(ctx: NodeSystemErrorCtx) { + super("ERR_SOCKET_BUFFER_SIZE", ctx, "Could not get or set buffer size"); + } +} +export class ERR_SOCKET_CLOSED extends NodeError { + constructor() { + super("ERR_SOCKET_CLOSED", `Socket is closed`); + } +} +export class ERR_SOCKET_DGRAM_IS_CONNECTED extends NodeError { + constructor() { + super("ERR_SOCKET_DGRAM_IS_CONNECTED", `Already connected`); + } +} +export class ERR_SOCKET_DGRAM_NOT_CONNECTED extends NodeError { + constructor() { + super("ERR_SOCKET_DGRAM_NOT_CONNECTED", `Not connected`); + } +} +export class ERR_SOCKET_DGRAM_NOT_RUNNING extends NodeError { + constructor() { + super("ERR_SOCKET_DGRAM_NOT_RUNNING", `Not running`); + } +} +export class ERR_SRI_PARSE extends NodeSyntaxError { + constructor(name: string, char: string, position: number) { + super( + "ERR_SRI_PARSE", + `Subresource Integrity string ${name} had an unexpected ${char} at position ${position}`, + ); + } +} +export class ERR_STREAM_ALREADY_FINISHED extends NodeError { + constructor(x: string) { + super( + "ERR_STREAM_ALREADY_FINISHED", + `Cannot call ${x} after a stream was finished`, + ); + } +} +export class ERR_STREAM_CANNOT_PIPE extends NodeError { + constructor() { + super("ERR_STREAM_CANNOT_PIPE", `Cannot pipe, not readable`); + } +} +export class ERR_STREAM_DESTROYED extends NodeError { + constructor(x: string) { + super( + "ERR_STREAM_DESTROYED", + `Cannot call ${x} after a stream was destroyed`, + ); + } +} +export class ERR_STREAM_NULL_VALUES extends NodeTypeError { + constructor() { + super("ERR_STREAM_NULL_VALUES", `May not write null values to stream`); + } +} +export class ERR_STREAM_PREMATURE_CLOSE extends NodeError { + constructor() { + super("ERR_STREAM_PREMATURE_CLOSE", `Premature close`); + } +} +export class ERR_STREAM_PUSH_AFTER_EOF extends NodeError { + constructor() { + super("ERR_STREAM_PUSH_AFTER_EOF", `stream.push() after EOF`); + } +} +export class ERR_STREAM_UNSHIFT_AFTER_END_EVENT extends NodeError { + constructor() { + super( + "ERR_STREAM_UNSHIFT_AFTER_END_EVENT", + `stream.unshift() after end event`, + ); + } +} +export class ERR_STREAM_WRAP extends NodeError { + constructor() { + super( + "ERR_STREAM_WRAP", + `Stream has StringDecoder set or is in objectMode`, + ); + } +} +export class ERR_STREAM_WRITE_AFTER_END extends NodeError { + constructor() { + super("ERR_STREAM_WRITE_AFTER_END", `write after end`); + } +} +export class ERR_SYNTHETIC extends NodeError { + constructor() { + super("ERR_SYNTHETIC", `JavaScript Callstack`); + } +} +export class ERR_TLS_CERT_ALTNAME_INVALID extends NodeError { + reason: string; + host: string; + cert: string; + + constructor(reason: string, host: string, cert: string) { + super( + "ERR_TLS_CERT_ALTNAME_INVALID", + `Hostname/IP does not match certificate's altnames: ${reason}`, + ); + this.reason = reason; + this.host = host; + this.cert = cert; + } +} +export class ERR_TLS_DH_PARAM_SIZE extends NodeError { + constructor(x: string) { + super("ERR_TLS_DH_PARAM_SIZE", `DH parameter size ${x} is less than 2048`); + } +} +export class ERR_TLS_HANDSHAKE_TIMEOUT extends NodeError { + constructor() { + super("ERR_TLS_HANDSHAKE_TIMEOUT", `TLS handshake timeout`); + } +} +export class ERR_TLS_INVALID_CONTEXT extends NodeTypeError { + constructor(x: string) { + super("ERR_TLS_INVALID_CONTEXT", `${x} must be a SecureContext`); + } +} +export class ERR_TLS_INVALID_STATE extends NodeError { + constructor() { + super( + "ERR_TLS_INVALID_STATE", + `TLS socket connection must be securely established`, + ); + } +} +export class ERR_TLS_INVALID_PROTOCOL_VERSION extends NodeTypeError { + constructor(protocol: string, x: string) { + super( + "ERR_TLS_INVALID_PROTOCOL_VERSION", + `${protocol} is not a valid ${x} TLS protocol version`, + ); + } +} +export class ERR_TLS_PROTOCOL_VERSION_CONFLICT extends NodeTypeError { + constructor(prevProtocol: string, protocol: string) { + super( + "ERR_TLS_PROTOCOL_VERSION_CONFLICT", + `TLS protocol version ${prevProtocol} conflicts with secureProtocol ${protocol}`, + ); + } +} +export class ERR_TLS_RENEGOTIATION_DISABLED extends NodeError { + constructor() { + super( + "ERR_TLS_RENEGOTIATION_DISABLED", + `TLS session renegotiation disabled for this socket`, + ); + } +} +export class ERR_TLS_REQUIRED_SERVER_NAME extends NodeError { + constructor() { + super( + "ERR_TLS_REQUIRED_SERVER_NAME", + `"servername" is required parameter for Server.addContext`, + ); + } +} +export class ERR_TLS_SESSION_ATTACK extends NodeError { + constructor() { + super( + "ERR_TLS_SESSION_ATTACK", + `TLS session renegotiation attack detected`, + ); + } +} +export class ERR_TLS_SNI_FROM_SERVER extends NodeError { + constructor() { + super( + "ERR_TLS_SNI_FROM_SERVER", + `Cannot issue SNI from a TLS server-side socket`, + ); + } +} +export class ERR_TRACE_EVENTS_CATEGORY_REQUIRED extends NodeTypeError { + constructor() { + super( + "ERR_TRACE_EVENTS_CATEGORY_REQUIRED", + `At least one category is required`, + ); + } +} +export class ERR_TRACE_EVENTS_UNAVAILABLE extends NodeError { + constructor() { + super("ERR_TRACE_EVENTS_UNAVAILABLE", `Trace events are unavailable`); + } +} +export class ERR_UNAVAILABLE_DURING_EXIT extends NodeError { + constructor() { + super( + "ERR_UNAVAILABLE_DURING_EXIT", + `Cannot call function in process exit handler`, + ); + } +} +export class ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET extends NodeError { + constructor() { + super( + "ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET", + "`process.setupUncaughtExceptionCapture()` was called while a capture callback was already active", + ); + } +} +export class ERR_UNESCAPED_CHARACTERS extends NodeTypeError { + constructor(x: string) { + super("ERR_UNESCAPED_CHARACTERS", `${x} contains unescaped characters`); + } +} +export class ERR_UNHANDLED_ERROR extends NodeError { + constructor(x: string) { + super("ERR_UNHANDLED_ERROR", `Unhandled error. (${x})`); + } +} +export class ERR_UNKNOWN_BUILTIN_MODULE extends NodeError { + constructor(x: string) { + super("ERR_UNKNOWN_BUILTIN_MODULE", `No such built-in module: ${x}`); + } +} +export class ERR_UNKNOWN_CREDENTIAL extends NodeError { + constructor(x: string, y: string) { + super("ERR_UNKNOWN_CREDENTIAL", `${x} identifier does not exist: ${y}`); + } +} +export class ERR_UNKNOWN_ENCODING extends NodeTypeError { + constructor(x: string) { + super("ERR_UNKNOWN_ENCODING", `Unknown encoding: ${x}`); + } +} +export class ERR_UNKNOWN_FILE_EXTENSION extends NodeTypeError { + constructor(x: string, y: string) { + super( + "ERR_UNKNOWN_FILE_EXTENSION", + `Unknown file extension "${x}" for ${y}`, + ); + } +} +export class ERR_UNKNOWN_MODULE_FORMAT extends NodeRangeError { + constructor(x: string) { + super("ERR_UNKNOWN_MODULE_FORMAT", `Unknown module format: ${x}`); + } +} +export class ERR_UNKNOWN_SIGNAL extends NodeTypeError { + constructor(x: string) { + super("ERR_UNKNOWN_SIGNAL", `Unknown signal: ${x}`); + } +} +export class ERR_UNSUPPORTED_DIR_IMPORT extends NodeError { + constructor(x: string, y: string) { + super( + "ERR_UNSUPPORTED_DIR_IMPORT", + `Directory import '${x}' is not supported resolving ES modules, imported from ${y}`, + ); + } +} +export class ERR_UNSUPPORTED_ESM_URL_SCHEME extends NodeError { + constructor() { + super( + "ERR_UNSUPPORTED_ESM_URL_SCHEME", + `Only file and data URLs are supported by the default ESM loader`, + ); + } +} +export class ERR_USE_AFTER_CLOSE extends NodeError { + constructor(x: string) { + super( + "ERR_USE_AFTER_CLOSE", + `${x} was closed`, + ); + } +} +export class ERR_V8BREAKITERATOR extends NodeError { + constructor() { + super( + "ERR_V8BREAKITERATOR", + `Full ICU data not installed. See https://github.com/nodejs/node/wiki/Intl`, + ); + } +} +export class ERR_VALID_PERFORMANCE_ENTRY_TYPE extends NodeError { + constructor() { + super( + "ERR_VALID_PERFORMANCE_ENTRY_TYPE", + `At least one valid performance entry type is required`, + ); + } +} +export class ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING extends NodeTypeError { + constructor() { + super( + "ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING", + `A dynamic import callback was not specified.`, + ); + } +} +export class ERR_VM_MODULE_ALREADY_LINKED extends NodeError { + constructor() { + super("ERR_VM_MODULE_ALREADY_LINKED", `Module has already been linked`); + } +} +export class ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA extends NodeError { + constructor() { + super( + "ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA", + `Cached data cannot be created for a module which has been evaluated`, + ); + } +} +export class ERR_VM_MODULE_DIFFERENT_CONTEXT extends NodeError { + constructor() { + super( + "ERR_VM_MODULE_DIFFERENT_CONTEXT", + `Linked modules must use the same context`, + ); + } +} +export class ERR_VM_MODULE_LINKING_ERRORED extends NodeError { + constructor() { + super( + "ERR_VM_MODULE_LINKING_ERRORED", + `Linking has already failed for the provided module`, + ); + } +} +export class ERR_VM_MODULE_NOT_MODULE extends NodeError { + constructor() { + super( + "ERR_VM_MODULE_NOT_MODULE", + `Provided module is not an instance of Module`, + ); + } +} +export class ERR_VM_MODULE_STATUS extends NodeError { + constructor(x: string) { + super("ERR_VM_MODULE_STATUS", `Module status ${x}`); + } +} +export class ERR_WASI_ALREADY_STARTED extends NodeError { + constructor() { + super("ERR_WASI_ALREADY_STARTED", `WASI instance has already started`); + } +} +export class ERR_WORKER_INIT_FAILED extends NodeError { + constructor(x: string) { + super("ERR_WORKER_INIT_FAILED", `Worker initialization failure: ${x}`); + } +} +export class ERR_WORKER_NOT_RUNNING extends NodeError { + constructor() { + super("ERR_WORKER_NOT_RUNNING", `Worker instance not running`); + } +} +export class ERR_WORKER_OUT_OF_MEMORY extends NodeError { + constructor(x: string) { + super( + "ERR_WORKER_OUT_OF_MEMORY", + `Worker terminated due to reaching memory limit: ${x}`, + ); + } +} +export class ERR_WORKER_UNSERIALIZABLE_ERROR extends NodeError { + constructor() { + super( + "ERR_WORKER_UNSERIALIZABLE_ERROR", + `Serializing an uncaught exception failed`, + ); + } +} +export class ERR_WORKER_UNSUPPORTED_EXTENSION extends NodeTypeError { + constructor(x: string) { + super( + "ERR_WORKER_UNSUPPORTED_EXTENSION", + `The worker script extension must be ".js", ".mjs", or ".cjs". Received "${x}"`, + ); + } +} +export class ERR_WORKER_UNSUPPORTED_OPERATION extends NodeTypeError { + constructor(x: string) { + super( + "ERR_WORKER_UNSUPPORTED_OPERATION", + `${x} is not supported in workers`, + ); + } +} +export class ERR_ZLIB_INITIALIZATION_FAILED extends NodeError { + constructor() { + super("ERR_ZLIB_INITIALIZATION_FAILED", `Initialization failed`); + } +} +export class ERR_FALSY_VALUE_REJECTION extends NodeError { + reason: string; + constructor(reason: string) { + super("ERR_FALSY_VALUE_REJECTION", "Promise was rejected with falsy value"); + this.reason = reason; + } +} +export class ERR_HTTP2_INVALID_SETTING_VALUE extends NodeRangeError { + actual: unknown; + min?: number; + max?: number; + + constructor(name: string, actual: unknown, min?: number, max?: number) { + super( + "ERR_HTTP2_INVALID_SETTING_VALUE", + `Invalid value for setting "${name}": ${actual}`, + ); + this.actual = actual; + if (min !== undefined) { + this.min = min; + this.max = max; + } + } +} +export class ERR_HTTP2_STREAM_CANCEL extends NodeError { + override cause?: Error; + constructor(error: Error) { + super( + "ERR_HTTP2_STREAM_CANCEL", + typeof error.message === "string" + ? `The pending stream has been canceled (caused by: ${error.message})` + : "The pending stream has been canceled", + ); + if (error) { + this.cause = error; + } + } +} + +export class ERR_INVALID_ADDRESS_FAMILY extends NodeRangeError { + host: string; + port: number; + constructor(addressType: string, host: string, port: number) { + super( + "ERR_INVALID_ADDRESS_FAMILY", + `Invalid address family: ${addressType} ${host}:${port}`, + ); + this.host = host; + this.port = port; + } +} + +export class ERR_INVALID_CHAR extends NodeTypeError { + constructor(name: string, field?: string) { + super( + "ERR_INVALID_CHAR", + field + ? `Invalid character in ${name}` + : `Invalid character in ${name} ["${field}"]`, + ); + } +} + +export class ERR_INVALID_OPT_VALUE extends NodeTypeError { + constructor(name: string, value: unknown) { + super( + "ERR_INVALID_OPT_VALUE", + `The value "${value}" is invalid for option "${name}"`, + ); + } +} + +export class ERR_INVALID_RETURN_PROPERTY extends NodeTypeError { + constructor(input: string, name: string, prop: string, value: string) { + super( + "ERR_INVALID_RETURN_PROPERTY", + `Expected a valid ${input} to be returned for the "${prop}" from the "${name}" function but got ${value}.`, + ); + } +} + +// deno-lint-ignore no-explicit-any +function buildReturnPropertyType(value: any) { + if (value && value.constructor && value.constructor.name) { + return `instance of ${value.constructor.name}`; + } else { + return `type ${typeof value}`; + } +} + +export class ERR_INVALID_RETURN_PROPERTY_VALUE extends NodeTypeError { + constructor(input: string, name: string, prop: string, value: unknown) { + super( + "ERR_INVALID_RETURN_PROPERTY_VALUE", + `Expected ${input} to be returned for the "${prop}" from the "${name}" function but got ${ + buildReturnPropertyType( + value, + ) + }.`, + ); + } +} + +export class ERR_INVALID_RETURN_VALUE extends NodeTypeError { + constructor(input: string, name: string, value: unknown) { + super( + "ERR_INVALID_RETURN_VALUE", + `Expected ${input} to be returned from the "${name}" function but got ${ + determineSpecificType( + value, + ) + }.`, + ); + } +} + +export class ERR_INVALID_URL extends NodeTypeError { + input: string; + constructor(input: string) { + super("ERR_INVALID_URL", `Invalid URL: ${input}`); + this.input = input; + } +} + +export class ERR_INVALID_URL_SCHEME extends NodeTypeError { + constructor(expected: string | [string] | [string, string]) { + expected = Array.isArray(expected) ? expected : [expected]; + const res = expected.length === 2 + ? `one of scheme ${expected[0]} or ${expected[1]}` + : `of scheme ${expected[0]}`; + super("ERR_INVALID_URL_SCHEME", `The URL must be ${res}`); + } +} + +export class ERR_MODULE_NOT_FOUND extends NodeError { + constructor(path: string, base: string, type: string = "package") { + super( + "ERR_MODULE_NOT_FOUND", + `Cannot find ${type} '${path}' imported from ${base}`, + ); + } +} + +export class ERR_INVALID_PACKAGE_CONFIG extends NodeError { + constructor(path: string, base?: string, message?: string) { + const msg = `Invalid package config ${path}${ + base ? ` while importing ${base}` : "" + }${message ? `. ${message}` : ""}`; + super("ERR_INVALID_PACKAGE_CONFIG", msg); + } +} + +export class ERR_INVALID_MODULE_SPECIFIER extends NodeTypeError { + constructor(request: string, reason: string, base?: string) { + super( + "ERR_INVALID_MODULE_SPECIFIER", + `Invalid module "${request}" ${reason}${ + base ? ` imported from ${base}` : "" + }`, + ); + } +} + +export class ERR_INVALID_PACKAGE_TARGET extends NodeError { + constructor( + pkgPath: string, + key: string, + // deno-lint-ignore no-explicit-any + target: any, + isImport?: boolean, + base?: string, + ) { + let msg: string; + const relError = typeof target === "string" && + !isImport && + target.length && + !target.startsWith("./"); + if (key === ".") { + assert(isImport === false); + msg = `Invalid "exports" main target ${JSON.stringify(target)} defined ` + + `in the package config ${pkgPath}package.json${ + base ? ` imported from ${base}` : "" + }${relError ? '; targets must start with "./"' : ""}`; + } else { + msg = `Invalid "${isImport ? "imports" : "exports"}" target ${ + JSON.stringify( + target, + ) + } defined for '${key}' in the package config ${pkgPath}package.json${ + base ? ` imported from ${base}` : "" + }${relError ? '; targets must start with "./"' : ""}`; + } + super("ERR_INVALID_PACKAGE_TARGET", msg); + } +} + +export class ERR_PACKAGE_IMPORT_NOT_DEFINED extends NodeTypeError { + constructor( + specifier: string, + packagePath: string | undefined, + base: string, + ) { + const msg = `Package import specifier "${specifier}" is not defined${ + packagePath ? ` in package ${packagePath}package.json` : "" + } imported from ${base}`; + + super("ERR_PACKAGE_IMPORT_NOT_DEFINED", msg); + } +} + +export class ERR_PACKAGE_PATH_NOT_EXPORTED extends NodeError { + constructor(subpath: string, pkgPath: string, basePath?: string) { + let msg: string; + if (subpath === ".") { + msg = `No "exports" main defined in ${pkgPath}package.json${ + basePath ? ` imported from ${basePath}` : "" + }`; + } else { + msg = + `Package subpath '${subpath}' is not defined by "exports" in ${pkgPath}package.json${ + basePath ? ` imported from ${basePath}` : "" + }`; + } + + super("ERR_PACKAGE_PATH_NOT_EXPORTED", msg); + } +} + +export class ERR_INTERNAL_ASSERTION extends NodeError { + constructor(message?: string) { + const suffix = "This is caused by either a bug in Node.js " + + "or incorrect usage of Node.js internals.\n" + + "Please open an issue with this stack trace at " + + "https://github.com/nodejs/node/issues\n"; + super( + "ERR_INTERNAL_ASSERTION", + message === undefined ? suffix : `${message}\n${suffix}`, + ); + } +} + +// Using `fs.rmdir` on a path that is a file results in an ENOENT error on Windows and an ENOTDIR error on POSIX. +export class ERR_FS_RMDIR_ENOTDIR extends NodeSystemError { + constructor(path: string) { + const code = isWindows ? "ENOENT" : "ENOTDIR"; + const ctx: NodeSystemErrorCtx = { + message: "not a directory", + path, + syscall: "rmdir", + code, + errno: isWindows ? osConstants.errno.ENOENT : osConstants.errno.ENOTDIR, + }; + super(code, ctx, "Path is not a directory"); + } +} + +interface UvExceptionContext { + syscall: string; + path?: string; +} +export function denoErrorToNodeError(e: Error, ctx: UvExceptionContext) { + const errno = extractOsErrorNumberFromErrorMessage(e); + if (typeof errno === "undefined") { + return e; + } + + const ex = uvException({ + errno: mapSysErrnoToUvErrno(errno), + ...ctx, + }); + return ex; +} + +function extractOsErrorNumberFromErrorMessage(e: unknown): number | undefined { + const match = e instanceof Error + ? e.message.match(/\(os error (\d+)\)/) + : false; + + if (match) { + return +match[1]; + } + + return undefined; +} + +export function connResetException(msg: string) { + const ex = new Error(msg); + // deno-lint-ignore no-explicit-any + (ex as any).code = "ECONNRESET"; + return ex; +} + +export function aggregateTwoErrors( + innerError: AggregateError, + outerError: AggregateError & { code: string }, +) { + if (innerError && outerError && innerError !== outerError) { + if (Array.isArray(outerError.errors)) { + // If `outerError` is already an `AggregateError`. + outerError.errors.push(innerError); + return outerError; + } + // eslint-disable-next-line no-restricted-syntax + const err = new AggregateError( + [ + outerError, + innerError, + ], + outerError.message, + ); + // deno-lint-ignore no-explicit-any + (err as any).code = outerError.code; + return err; + } + return innerError || outerError; +} +codes.ERR_IPC_CHANNEL_CLOSED = ERR_IPC_CHANNEL_CLOSED; +codes.ERR_INVALID_ARG_TYPE = ERR_INVALID_ARG_TYPE; +codes.ERR_INVALID_ARG_VALUE = ERR_INVALID_ARG_VALUE; +codes.ERR_OUT_OF_RANGE = ERR_OUT_OF_RANGE; +codes.ERR_SOCKET_BAD_PORT = ERR_SOCKET_BAD_PORT; +codes.ERR_BUFFER_OUT_OF_BOUNDS = ERR_BUFFER_OUT_OF_BOUNDS; +codes.ERR_UNKNOWN_ENCODING = ERR_UNKNOWN_ENCODING; +// TODO(kt3k): assign all error classes here. + +/** + * This creates a generic Node.js error. + * + * @param message The error message. + * @param errorProperties Object with additional properties to be added to the error. + * @returns + */ +const genericNodeError = hideStackFrames( + function genericNodeError(message, errorProperties) { + // eslint-disable-next-line no-restricted-syntax + const err = new Error(message); + Object.assign(err, errorProperties); + + return err; + }, +); + +/** + * Determine the specific type of a value for type-mismatch errors. + * @param {*} value + * @returns {string} + */ +// deno-lint-ignore no-explicit-any +function determineSpecificType(value: any) { + if (value == null) { + return "" + value; + } + if (typeof value === "function" && value.name) { + return `function ${value.name}`; + } + if (typeof value === "object") { + if (value.constructor?.name) { + return `an instance of ${value.constructor.name}`; + } + return `${inspect(value, { depth: -1 })}`; + } + let inspected = inspect(value, { colors: false }); + if (inspected.length > 28) inspected = `${inspected.slice(0, 25)}...`; + + return `type ${typeof value} (${inspected})`; +} + +export { codes, genericNodeError, hideStackFrames }; + +export default { + AbortError, + ERR_AMBIGUOUS_ARGUMENT, + ERR_ARG_NOT_ITERABLE, + ERR_ASSERTION, + ERR_ASYNC_CALLBACK, + ERR_ASYNC_TYPE, + ERR_BROTLI_INVALID_PARAM, + ERR_BUFFER_OUT_OF_BOUNDS, + ERR_BUFFER_TOO_LARGE, + ERR_CANNOT_WATCH_SIGINT, + ERR_CHILD_CLOSED_BEFORE_REPLY, + ERR_CHILD_PROCESS_IPC_REQUIRED, + ERR_CHILD_PROCESS_STDIO_MAXBUFFER, + ERR_CONSOLE_WRITABLE_STREAM, + ERR_CONTEXT_NOT_INITIALIZED, + ERR_CPU_USAGE, + ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED, + ERR_CRYPTO_ECDH_INVALID_FORMAT, + ERR_CRYPTO_ECDH_INVALID_PUBLIC_KEY, + ERR_CRYPTO_ENGINE_UNKNOWN, + ERR_CRYPTO_FIPS_FORCED, + ERR_CRYPTO_FIPS_UNAVAILABLE, + ERR_CRYPTO_HASH_FINALIZED, + ERR_CRYPTO_HASH_UPDATE_FAILED, + ERR_CRYPTO_INCOMPATIBLE_KEY, + ERR_CRYPTO_INCOMPATIBLE_KEY_OPTIONS, + ERR_CRYPTO_INVALID_DIGEST, + ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE, + ERR_CRYPTO_INVALID_STATE, + ERR_CRYPTO_PBKDF2_ERROR, + ERR_CRYPTO_SCRYPT_INVALID_PARAMETER, + ERR_CRYPTO_SCRYPT_NOT_SUPPORTED, + ERR_CRYPTO_SIGN_KEY_REQUIRED, + ERR_DIR_CLOSED, + ERR_DIR_CONCURRENT_OPERATION, + ERR_DNS_SET_SERVERS_FAILED, + ERR_DOMAIN_CALLBACK_NOT_AVAILABLE, + ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE, + ERR_ENCODING_INVALID_ENCODED_DATA, + ERR_ENCODING_NOT_SUPPORTED, + ERR_EVAL_ESM_CANNOT_PRINT, + ERR_EVENT_RECURSION, + ERR_FALSY_VALUE_REJECTION, + ERR_FEATURE_UNAVAILABLE_ON_PLATFORM, + ERR_FS_EISDIR, + ERR_FS_FILE_TOO_LARGE, + ERR_FS_INVALID_SYMLINK_TYPE, + ERR_FS_RMDIR_ENOTDIR, + ERR_HTTP2_ALTSVC_INVALID_ORIGIN, + ERR_HTTP2_ALTSVC_LENGTH, + ERR_HTTP2_CONNECT_AUTHORITY, + ERR_HTTP2_CONNECT_PATH, + ERR_HTTP2_CONNECT_SCHEME, + ERR_HTTP2_GOAWAY_SESSION, + ERR_HTTP2_HEADERS_AFTER_RESPOND, + ERR_HTTP2_HEADERS_SENT, + ERR_HTTP2_HEADER_SINGLE_VALUE, + ERR_HTTP2_INFO_STATUS_NOT_ALLOWED, + ERR_HTTP2_INVALID_CONNECTION_HEADERS, + ERR_HTTP2_INVALID_HEADER_VALUE, + ERR_HTTP2_INVALID_INFO_STATUS, + ERR_HTTP2_INVALID_ORIGIN, + ERR_HTTP2_INVALID_PACKED_SETTINGS_LENGTH, + ERR_HTTP2_INVALID_PSEUDOHEADER, + ERR_HTTP2_INVALID_SESSION, + ERR_HTTP2_INVALID_SETTING_VALUE, + ERR_HTTP2_INVALID_STREAM, + ERR_HTTP2_MAX_PENDING_SETTINGS_ACK, + ERR_HTTP2_NESTED_PUSH, + ERR_HTTP2_NO_SOCKET_MANIPULATION, + ERR_HTTP2_ORIGIN_LENGTH, + ERR_HTTP2_OUT_OF_STREAMS, + ERR_HTTP2_PAYLOAD_FORBIDDEN, + ERR_HTTP2_PING_CANCEL, + ERR_HTTP2_PING_LENGTH, + ERR_HTTP2_PSEUDOHEADER_NOT_ALLOWED, + ERR_HTTP2_PUSH_DISABLED, + ERR_HTTP2_SEND_FILE, + ERR_HTTP2_SEND_FILE_NOSEEK, + ERR_HTTP2_SESSION_ERROR, + ERR_HTTP2_SETTINGS_CANCEL, + ERR_HTTP2_SOCKET_BOUND, + ERR_HTTP2_SOCKET_UNBOUND, + ERR_HTTP2_STATUS_101, + ERR_HTTP2_STATUS_INVALID, + ERR_HTTP2_STREAM_CANCEL, + ERR_HTTP2_STREAM_ERROR, + ERR_HTTP2_STREAM_SELF_DEPENDENCY, + ERR_HTTP2_TRAILERS_ALREADY_SENT, + ERR_HTTP2_TRAILERS_NOT_READY, + ERR_HTTP2_UNSUPPORTED_PROTOCOL, + ERR_HTTP_HEADERS_SENT, + ERR_HTTP_INVALID_HEADER_VALUE, + ERR_HTTP_INVALID_STATUS_CODE, + ERR_HTTP_SOCKET_ENCODING, + ERR_HTTP_TRAILER_INVALID, + ERR_INCOMPATIBLE_OPTION_PAIR, + ERR_INPUT_TYPE_NOT_ALLOWED, + ERR_INSPECTOR_ALREADY_ACTIVATED, + ERR_INSPECTOR_ALREADY_CONNECTED, + ERR_INSPECTOR_CLOSED, + ERR_INSPECTOR_COMMAND, + ERR_INSPECTOR_NOT_ACTIVE, + ERR_INSPECTOR_NOT_AVAILABLE, + ERR_INSPECTOR_NOT_CONNECTED, + ERR_INSPECTOR_NOT_WORKER, + ERR_INTERNAL_ASSERTION, + ERR_INVALID_ADDRESS_FAMILY, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_TYPE_RANGE, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_ARG_VALUE_RANGE, + ERR_INVALID_ASYNC_ID, + ERR_INVALID_BUFFER_SIZE, + ERR_INVALID_CHAR, + ERR_INVALID_CURSOR_POS, + ERR_INVALID_FD, + ERR_INVALID_FD_TYPE, + ERR_INVALID_FILE_URL_HOST, + ERR_INVALID_FILE_URL_PATH, + ERR_INVALID_HANDLE_TYPE, + ERR_INVALID_HTTP_TOKEN, + ERR_INVALID_IP_ADDRESS, + ERR_INVALID_MODULE_SPECIFIER, + ERR_INVALID_OPT_VALUE, + ERR_INVALID_OPT_VALUE_ENCODING, + ERR_INVALID_PACKAGE_CONFIG, + ERR_INVALID_PACKAGE_TARGET, + ERR_INVALID_PERFORMANCE_MARK, + ERR_INVALID_PROTOCOL, + ERR_INVALID_REPL_EVAL_CONFIG, + ERR_INVALID_REPL_INPUT, + ERR_INVALID_RETURN_PROPERTY, + ERR_INVALID_RETURN_PROPERTY_VALUE, + ERR_INVALID_RETURN_VALUE, + ERR_INVALID_SYNC_FORK_INPUT, + ERR_INVALID_THIS, + ERR_INVALID_TUPLE, + ERR_INVALID_URI, + ERR_INVALID_URL, + ERR_INVALID_URL_SCHEME, + ERR_IPC_CHANNEL_CLOSED, + ERR_IPC_DISCONNECTED, + ERR_IPC_ONE_PIPE, + ERR_IPC_SYNC_FORK, + ERR_MANIFEST_DEPENDENCY_MISSING, + ERR_MANIFEST_INTEGRITY_MISMATCH, + ERR_MANIFEST_INVALID_RESOURCE_FIELD, + ERR_MANIFEST_TDZ, + ERR_MANIFEST_UNKNOWN_ONERROR, + ERR_METHOD_NOT_IMPLEMENTED, + ERR_MISSING_ARGS, + ERR_MISSING_OPTION, + ERR_MODULE_NOT_FOUND, + ERR_MULTIPLE_CALLBACK, + ERR_NAPI_CONS_FUNCTION, + ERR_NAPI_INVALID_DATAVIEW_ARGS, + ERR_NAPI_INVALID_TYPEDARRAY_ALIGNMENT, + ERR_NAPI_INVALID_TYPEDARRAY_LENGTH, + ERR_NO_CRYPTO, + ERR_NO_ICU, + ERR_OUT_OF_RANGE, + ERR_PACKAGE_IMPORT_NOT_DEFINED, + ERR_PACKAGE_PATH_NOT_EXPORTED, + ERR_QUICCLIENTSESSION_FAILED, + ERR_QUICCLIENTSESSION_FAILED_SETSOCKET, + ERR_QUICSESSION_DESTROYED, + ERR_QUICSESSION_INVALID_DCID, + ERR_QUICSESSION_UPDATEKEY, + ERR_QUICSOCKET_DESTROYED, + ERR_QUICSOCKET_INVALID_STATELESS_RESET_SECRET_LENGTH, + ERR_QUICSOCKET_LISTENING, + ERR_QUICSOCKET_UNBOUND, + ERR_QUICSTREAM_DESTROYED, + ERR_QUICSTREAM_INVALID_PUSH, + ERR_QUICSTREAM_OPEN_FAILED, + ERR_QUICSTREAM_UNSUPPORTED_PUSH, + ERR_QUIC_TLS13_REQUIRED, + ERR_SCRIPT_EXECUTION_INTERRUPTED, + ERR_SERVER_ALREADY_LISTEN, + ERR_SERVER_NOT_RUNNING, + ERR_SOCKET_ALREADY_BOUND, + ERR_SOCKET_BAD_BUFFER_SIZE, + ERR_SOCKET_BAD_PORT, + ERR_SOCKET_BAD_TYPE, + ERR_SOCKET_BUFFER_SIZE, + ERR_SOCKET_CLOSED, + ERR_SOCKET_DGRAM_IS_CONNECTED, + ERR_SOCKET_DGRAM_NOT_CONNECTED, + ERR_SOCKET_DGRAM_NOT_RUNNING, + ERR_SRI_PARSE, + ERR_STREAM_ALREADY_FINISHED, + ERR_STREAM_CANNOT_PIPE, + ERR_STREAM_DESTROYED, + ERR_STREAM_NULL_VALUES, + ERR_STREAM_PREMATURE_CLOSE, + ERR_STREAM_PUSH_AFTER_EOF, + ERR_STREAM_UNSHIFT_AFTER_END_EVENT, + ERR_STREAM_WRAP, + ERR_STREAM_WRITE_AFTER_END, + ERR_SYNTHETIC, + ERR_TLS_CERT_ALTNAME_INVALID, + ERR_TLS_DH_PARAM_SIZE, + ERR_TLS_HANDSHAKE_TIMEOUT, + ERR_TLS_INVALID_CONTEXT, + ERR_TLS_INVALID_PROTOCOL_VERSION, + ERR_TLS_INVALID_STATE, + ERR_TLS_PROTOCOL_VERSION_CONFLICT, + ERR_TLS_RENEGOTIATION_DISABLED, + ERR_TLS_REQUIRED_SERVER_NAME, + ERR_TLS_SESSION_ATTACK, + ERR_TLS_SNI_FROM_SERVER, + ERR_TRACE_EVENTS_CATEGORY_REQUIRED, + ERR_TRACE_EVENTS_UNAVAILABLE, + ERR_UNAVAILABLE_DURING_EXIT, + ERR_UNCAUGHT_EXCEPTION_CAPTURE_ALREADY_SET, + ERR_UNESCAPED_CHARACTERS, + ERR_UNHANDLED_ERROR, + ERR_UNKNOWN_BUILTIN_MODULE, + ERR_UNKNOWN_CREDENTIAL, + ERR_UNKNOWN_ENCODING, + ERR_UNKNOWN_FILE_EXTENSION, + ERR_UNKNOWN_MODULE_FORMAT, + ERR_UNKNOWN_SIGNAL, + ERR_UNSUPPORTED_DIR_IMPORT, + ERR_UNSUPPORTED_ESM_URL_SCHEME, + ERR_USE_AFTER_CLOSE, + ERR_V8BREAKITERATOR, + ERR_VALID_PERFORMANCE_ENTRY_TYPE, + ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING, + ERR_VM_MODULE_ALREADY_LINKED, + ERR_VM_MODULE_CANNOT_CREATE_CACHED_DATA, + ERR_VM_MODULE_DIFFERENT_CONTEXT, + ERR_VM_MODULE_LINKING_ERRORED, + ERR_VM_MODULE_NOT_MODULE, + ERR_VM_MODULE_STATUS, + ERR_WASI_ALREADY_STARTED, + ERR_WORKER_INIT_FAILED, + ERR_WORKER_NOT_RUNNING, + ERR_WORKER_OUT_OF_MEMORY, + ERR_WORKER_UNSERIALIZABLE_ERROR, + ERR_WORKER_UNSUPPORTED_EXTENSION, + ERR_WORKER_UNSUPPORTED_OPERATION, + ERR_ZLIB_INITIALIZATION_FAILED, + NodeError, + NodeErrorAbstraction, + NodeRangeError, + NodeSyntaxError, + NodeTypeError, + NodeURIError, + aggregateTwoErrors, + codes, + connResetException, + denoErrorToNodeError, + dnsException, + errnoException, + errorMap, + exceptionWithHostPort, + genericNodeError, + hideStackFrames, + isStackOverflowError, + uvException, + uvExceptionWithHostPort, +}; diff --git a/ext/node/polyfills/internal/event_target.mjs b/ext/node/polyfills/internal/event_target.mjs new file mode 100644 index 0000000000..d542fba944 --- /dev/null +++ b/ext/node/polyfills/internal/event_target.mjs @@ -0,0 +1,1111 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Node.js contributors. All rights reserved. MIT License. + +import { + ERR_EVENT_RECURSION, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_THIS, + ERR_MISSING_ARGS, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { validateObject, validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { emitWarning } from "internal:deno_node/polyfills/process.ts"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +import { Event as WebEvent, EventTarget as WebEventTarget } from "internal:deno_web/02_event.js"; + +import { + customInspectSymbol, + kEmptyObject, + kEnumerableProperty, +} from "internal:deno_node/polyfills/internal/util.mjs"; +import { inspect } from "internal:deno_node/polyfills/util.ts"; + +const kIsEventTarget = Symbol.for("nodejs.event_target"); +const kIsNodeEventTarget = Symbol("kIsNodeEventTarget"); + +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +const { + kMaxEventTargetListeners, + kMaxEventTargetListenersWarned, +} = EventEmitter; + +const kEvents = Symbol("kEvents"); +const kIsBeingDispatched = Symbol("kIsBeingDispatched"); +const kStop = Symbol("kStop"); +const kTarget = Symbol("kTarget"); +const kHandlers = Symbol("khandlers"); +const kWeakHandler = Symbol("kWeak"); + +const kHybridDispatch = Symbol.for("nodejs.internal.kHybridDispatch"); +const kCreateEvent = Symbol("kCreateEvent"); +const kNewListener = Symbol("kNewListener"); +const kRemoveListener = Symbol("kRemoveListener"); +const kIsNodeStyleListener = Symbol("kIsNodeStyleListener"); +const kTrustEvent = Symbol("kTrustEvent"); + +const kType = Symbol("type"); +const kDetail = Symbol("detail"); +const kDefaultPrevented = Symbol("defaultPrevented"); +const kCancelable = Symbol("cancelable"); +const kTimestamp = Symbol("timestamp"); +const kBubbles = Symbol("bubbles"); +const kComposed = Symbol("composed"); +const kPropagationStopped = Symbol("propagationStopped"); + +function isEvent(value) { + return typeof value?.[kType] === "string"; +} + +class Event extends WebEvent { + /** + * @param {string} type + * @param {{ + * bubbles?: boolean, + * cancelable?: boolean, + * composed?: boolean, + * }} [options] + */ + constructor(type, options = null) { + super(type, options); + if (arguments.length === 0) { + throw new ERR_MISSING_ARGS("type"); + } + validateObject(options, "options", { + allowArray: true, + allowFunction: true, + nullable: true, + }); + const { cancelable, bubbles, composed } = { ...options }; + this[kCancelable] = !!cancelable; + this[kBubbles] = !!bubbles; + this[kComposed] = !!composed; + this[kType] = `${type}`; + this[kDefaultPrevented] = false; + this[kTimestamp] = performance.now(); + this[kPropagationStopped] = false; + this[kTarget] = null; + this[kIsBeingDispatched] = false; + } + + [customInspectSymbol](depth, options) { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + const name = this.constructor.name; + if (depth < 0) { + return name; + } + + const opts = Object.assign({}, options, { + depth: NumberIsInteger(options.depth) ? options.depth - 1 : options.depth, + }); + + return `${name} ${ + inspect({ + type: this[kType], + defaultPrevented: this[kDefaultPrevented], + cancelable: this[kCancelable], + timeStamp: this[kTimestamp], + }, opts) + }`; + } + + stopImmediatePropagation() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + this[kStop] = true; + } + + preventDefault() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + this[kDefaultPrevented] = true; + } + + /** + * @type {EventTarget} + */ + get target() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kTarget]; + } + + /** + * @type {EventTarget} + */ + get currentTarget() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kTarget]; + } + + /** + * @type {EventTarget} + */ + get srcElement() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kTarget]; + } + + /** + * @type {string} + */ + get type() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kType]; + } + + /** + * @type {boolean} + */ + get cancelable() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kCancelable]; + } + + /** + * @type {boolean} + */ + get defaultPrevented() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kCancelable] && this[kDefaultPrevented]; + } + + /** + * @type {number} + */ + get timeStamp() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kTimestamp]; + } + + // The following are non-op and unused properties/methods from Web API Event. + // These are not supported in Node.js and are provided purely for + // API completeness. + /** + * @returns {EventTarget[]} + */ + composedPath() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kIsBeingDispatched] ? [this[kTarget]] : []; + } + + /** + * @type {boolean} + */ + get returnValue() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return !this.defaultPrevented; + } + + /** + * @type {boolean} + */ + get bubbles() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kBubbles]; + } + + /** + * @type {boolean} + */ + get composed() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kComposed]; + } + + /** + * @type {number} + */ + get eventPhase() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kIsBeingDispatched] ? Event.AT_TARGET : Event.NONE; + } + + /** + * @type {boolean} + */ + get cancelBubble() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + return this[kPropagationStopped]; + } + + /** + * @type {boolean} + */ + set cancelBubble(value) { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + if (value) { + this.stopPropagation(); + } + } + + stopPropagation() { + if (!isEvent(this)) { + throw new ERR_INVALID_THIS("Event"); + } + this[kPropagationStopped] = true; + } + + static NONE = 0; + static CAPTURING_PHASE = 1; + static AT_TARGET = 2; + static BUBBLING_PHASE = 3; +} + +Object.defineProperties( + Event.prototype, + { + [Symbol.toStringTag]: { + writable: true, + enumerable: false, + configurable: true, + value: "Event", + }, + stopImmediatePropagation: kEnumerableProperty, + preventDefault: kEnumerableProperty, + target: kEnumerableProperty, + currentTarget: kEnumerableProperty, + srcElement: kEnumerableProperty, + type: kEnumerableProperty, + cancelable: kEnumerableProperty, + defaultPrevented: kEnumerableProperty, + timeStamp: kEnumerableProperty, + composedPath: kEnumerableProperty, + returnValue: kEnumerableProperty, + bubbles: kEnumerableProperty, + composed: kEnumerableProperty, + eventPhase: kEnumerableProperty, + cancelBubble: kEnumerableProperty, + stopPropagation: kEnumerableProperty, + }, +); + +function isCustomEvent(value) { + return isEvent(value) && (value?.[kDetail] !== undefined); +} + +class CustomEvent extends Event { + /** + * @constructor + * @param {string} type + * @param {{ + * bubbles?: boolean, + * cancelable?: boolean, + * composed?: boolean, + * detail?: any, + * }} [options] + */ + constructor(type, options = kEmptyObject) { + if (arguments.length === 0) { + throw new ERR_MISSING_ARGS("type"); + } + super(type, options); + this[kDetail] = options?.detail ?? null; + } + + /** + * @type {any} + */ + get detail() { + if (!isCustomEvent(this)) { + throw new ERR_INVALID_THIS("CustomEvent"); + } + return this[kDetail]; + } +} + +Object.defineProperties(CustomEvent.prototype, { + [Symbol.toStringTag]: { + __proto__: null, + writable: false, + enumerable: false, + configurable: true, + value: "CustomEvent", + }, + detail: kEnumerableProperty, +}); + +class NodeCustomEvent extends Event { + constructor(type, options) { + super(type, options); + if (options?.detail) { + this.detail = options.detail; + } + } +} + +// Weak listener cleanup +// This has to be lazy for snapshots to work +let weakListenersState = null; +// The resource needs to retain the callback so that it doesn't +// get garbage collected now that it's weak. +let objectToWeakListenerMap = null; +function weakListeners() { + weakListenersState ??= new FinalizationRegistry( + (listener) => listener.remove(), + ); + objectToWeakListenerMap ??= new WeakMap(); + return { registry: weakListenersState, map: objectToWeakListenerMap }; +} + +// The listeners for an EventTarget are maintained as a linked list. +// Unfortunately, the way EventTarget is defined, listeners are accounted +// using the tuple [handler,capture], and even if we don't actually make +// use of capture or bubbling, in order to be spec compliant we have to +// take on the additional complexity of supporting it. Fortunately, using +// the linked list makes dispatching faster, even if adding/removing is +// slower. +class Listener { + constructor( + previous, + listener, + once, + capture, + passive, + isNodeStyleListener, + weak, + ) { + this.next = undefined; + if (previous !== undefined) { + previous.next = this; + } + this.previous = previous; + this.listener = listener; + // TODO(benjamingr) these 4 can be 'flags' to save 3 slots + this.once = once; + this.capture = capture; + this.passive = passive; + this.isNodeStyleListener = isNodeStyleListener; + this.removed = false; + this.weak = Boolean(weak); // Don't retain the object + + if (this.weak) { + this.callback = new WeakRef(listener); + weakListeners().registry.register(listener, this, this); + // Make the retainer retain the listener in a WeakMap + weakListeners().map.set(weak, listener); + this.listener = this.callback; + } else if (typeof listener === "function") { + this.callback = listener; + this.listener = listener; + } else { + this.callback = Function.prototype.bind.call( + listener.handleEvent, + listener, + ); + this.listener = listener; + } + } + + same(listener, capture) { + const myListener = this.weak ? this.listener.deref() : this.listener; + return myListener === listener && this.capture === capture; + } + + remove() { + if (this.previous !== undefined) { + this.previous.next = this.next; + } + if (this.next !== undefined) { + this.next.previous = this.previous; + } + this.removed = true; + if (this.weak) { + weakListeners().registry.unregister(this); + } + } +} + +function initEventTarget(self) { + self[kEvents] = new Map(); + self[kMaxEventTargetListeners] = EventEmitter.defaultMaxListeners; + self[kMaxEventTargetListenersWarned] = false; +} + +class EventTarget extends WebEventTarget { + // Used in checking whether an object is an EventTarget. This is a well-known + // symbol as EventTarget may be used cross-realm. + // Ref: https://github.com/nodejs/node/pull/33661 + static [kIsEventTarget] = true; + + constructor() { + super(); + initEventTarget(this); + } + + [kNewListener](size, type, _listener, _once, _capture, _passive, _weak) { + if ( + this[kMaxEventTargetListeners] > 0 && + size > this[kMaxEventTargetListeners] && + !this[kMaxEventTargetListenersWarned] + ) { + this[kMaxEventTargetListenersWarned] = true; + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + const w = new Error( + "Possible EventTarget memory leak detected. " + + `${size} ${type} listeners ` + + `added to ${inspect(this, { depth: -1 })}. Use ` + + "events.setMaxListeners() to increase limit", + ); + w.name = "MaxListenersExceededWarning"; + w.target = this; + w.type = type; + w.count = size; + emitWarning(w); + } + } + [kRemoveListener](_size, _type, _listener, _capture) {} + + /** + * @callback EventTargetCallback + * @param {Event} event + */ + + /** + * @typedef {{ handleEvent: EventTargetCallback }} EventListener + */ + + /** + * @param {string} type + * @param {EventTargetCallback|EventListener} listener + * @param {{ + * capture?: boolean, + * once?: boolean, + * passive?: boolean, + * signal?: AbortSignal + * }} [options] + */ + addEventListener(type, listener, options = {}) { + if (!isEventTarget(this)) { + throw new ERR_INVALID_THIS("EventTarget"); + } + if (arguments.length < 2) { + throw new ERR_MISSING_ARGS("type", "listener"); + } + + // We validateOptions before the shouldAddListeners check because the spec + // requires us to hit getters. + const { + once, + capture, + passive, + signal, + isNodeStyleListener, + weak, + } = validateEventListenerOptions(options); + + if (!shouldAddListener(listener)) { + // The DOM silently allows passing undefined as a second argument + // No error code for this since it is a Warning + // eslint-disable-next-line no-restricted-syntax + const w = new Error( + `addEventListener called with ${listener}` + + " which has no effect.", + ); + w.name = "AddEventListenerArgumentTypeWarning"; + w.target = this; + w.type = type; + emitWarning(w); + return; + } + type = String(type); + + if (signal) { + if (signal.aborted) { + return; + } + // TODO(benjamingr) make this weak somehow? ideally the signal would + // not prevent the event target from GC. + signal.addEventListener("abort", () => { + this.removeEventListener(type, listener, options); + }, { once: true, [kWeakHandler]: this }); + } + + let root = this[kEvents].get(type); + + if (root === undefined) { + root = { size: 1, next: undefined }; + // This is the first handler in our linked list. + new Listener( + root, + listener, + once, + capture, + passive, + isNodeStyleListener, + weak, + ); + this[kNewListener]( + root.size, + type, + listener, + once, + capture, + passive, + weak, + ); + this[kEvents].set(type, root); + return; + } + + let handler = root.next; + let previous = root; + + // We have to walk the linked list to see if we have a match + while (handler !== undefined && !handler.same(listener, capture)) { + previous = handler; + handler = handler.next; + } + + if (handler !== undefined) { // Duplicate! Ignore + return; + } + + new Listener( + previous, + listener, + once, + capture, + passive, + isNodeStyleListener, + weak, + ); + root.size++; + this[kNewListener](root.size, type, listener, once, capture, passive, weak); + } + + /** + * @param {string} type + * @param {EventTargetCallback|EventListener} listener + * @param {{ + * capture?: boolean, + * }} [options] + */ + removeEventListener(type, listener, options = {}) { + if (!isEventTarget(this)) { + throw new ERR_INVALID_THIS("EventTarget"); + } + if (!shouldAddListener(listener)) { + return; + } + + type = String(type); + const capture = options?.capture === true; + + const root = this[kEvents].get(type); + if (root === undefined || root.next === undefined) { + return; + } + + let handler = root.next; + while (handler !== undefined) { + if (handler.same(listener, capture)) { + handler.remove(); + root.size--; + if (root.size === 0) { + this[kEvents].delete(type); + } + this[kRemoveListener](root.size, type, listener, capture); + break; + } + handler = handler.next; + } + } + + /** + * @param {Event} event + */ + dispatchEvent(event) { + if (!isEventTarget(this)) { + throw new ERR_INVALID_THIS("EventTarget"); + } + + if (!(event instanceof globalThis.Event)) { + throw new ERR_INVALID_ARG_TYPE("event", "Event", event); + } + + if (event[kIsBeingDispatched]) { + throw new ERR_EVENT_RECURSION(event.type); + } + + this[kHybridDispatch](event, event.type, event); + + return event.defaultPrevented !== true; + } + + [kHybridDispatch](nodeValue, type, event) { + const createEvent = () => { + if (event === undefined) { + event = this[kCreateEvent](nodeValue, type); + event[kTarget] = this; + event[kIsBeingDispatched] = true; + } + return event; + }; + if (event !== undefined) { + event[kTarget] = this; + event[kIsBeingDispatched] = true; + } + + const root = this[kEvents].get(type); + if (root === undefined || root.next === undefined) { + if (event !== undefined) { + event[kIsBeingDispatched] = false; + } + return true; + } + + let handler = root.next; + let next; + + while ( + handler !== undefined && + (handler.passive || event?.[kStop] !== true) + ) { + // Cache the next item in case this iteration removes the current one + next = handler.next; + + if (handler.removed) { + // Deal with the case an event is removed while event handlers are + // Being processed (removeEventListener called from a listener) + handler = next; + continue; + } + if (handler.once) { + handler.remove(); + root.size--; + const { listener, capture } = handler; + this[kRemoveListener](root.size, type, listener, capture); + } + + try { + let arg; + if (handler.isNodeStyleListener) { + arg = nodeValue; + } else { + arg = createEvent(); + } + const callback = handler.weak + ? handler.callback.deref() + : handler.callback; + let result; + if (callback) { + result = callback.call(this, arg); + if (!handler.isNodeStyleListener) { + arg[kIsBeingDispatched] = false; + } + } + if (result !== undefined && result !== null) { + addCatch(result); + } + } catch (err) { + emitUncaughtException(err); + } + + handler = next; + } + + if (event !== undefined) { + event[kIsBeingDispatched] = false; + } + } + + [kCreateEvent](nodeValue, type) { + return new NodeCustomEvent(type, { detail: nodeValue }); + } + [customInspectSymbol](depth, options) { + if (!isEventTarget(this)) { + throw new ERR_INVALID_THIS("EventTarget"); + } + const name = this.constructor.name; + if (depth < 0) { + return name; + } + + const opts = ObjectAssign({}, options, { + depth: Number.isInteger(options.depth) + ? options.depth - 1 + : options.depth, + }); + + return `${name} ${inspect({}, opts)}`; + } +} + +Object.defineProperties(EventTarget.prototype, { + addEventListener: kEnumerableProperty, + removeEventListener: kEnumerableProperty, + dispatchEvent: kEnumerableProperty, + [Symbol.toStringTag]: { + writable: true, + enumerable: false, + configurable: true, + value: "EventTarget", + }, +}); + +function initNodeEventTarget(self) { + initEventTarget(self); +} + +class NodeEventTarget extends EventTarget { + static [kIsNodeEventTarget] = true; + static defaultMaxListeners = 10; + + constructor() { + super(); + initNodeEventTarget(this); + } + + /** + * @param {number} n + */ + setMaxListeners(n) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + EventEmitter.setMaxListeners(n, this); + } + + /** + * @returns {number} + */ + getMaxListeners() { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + return this[kMaxEventTargetListeners]; + } + + /** + * @returns {string[]} + */ + eventNames() { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + return Array.from(this[kEvents].keys()); + } + + /** + * @param {string} [type] + * @returns {number} + */ + listenerCount(type) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + const root = this[kEvents].get(String(type)); + return root !== undefined ? root.size : 0; + } + + /** + * @param {string} type + * @param {EventTargetCallback|EventListener} listener + * @param {{ + * capture?: boolean, + * }} [options] + * @returns {NodeEventTarget} + */ + off(type, listener, options) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + this.removeEventListener(type, listener, options); + return this; + } + + /** + * @param {string} type + * @param {EventTargetCallback|EventListener} listener + * @param {{ + * capture?: boolean, + * }} [options] + * @returns {NodeEventTarget} + */ + removeListener(type, listener, options) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + this.removeEventListener(type, listener, options); + return this; + } + + /** + * @param {string} type + * @param {EventTargetCallback|EventListener} listener + * @returns {NodeEventTarget} + */ + on(type, listener) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + this.addEventListener(type, listener, { [kIsNodeStyleListener]: true }); + return this; + } + + /** + * @param {string} type + * @param {EventTargetCallback|EventListener} listener + * @returns {NodeEventTarget} + */ + addListener(type, listener) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + this.addEventListener(type, listener, { [kIsNodeStyleListener]: true }); + return this; + } + + /** + * @param {string} type + * @param {any} arg + * @returns {boolean} + */ + emit(type, arg) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + validateString(type, "type"); + const hadListeners = this.listenerCount(type) > 0; + this[kHybridDispatch](arg, type); + return hadListeners; + } + + /** + * @param {string} type + * @param {EventTargetCallback|EventListener} listener + * @returns {NodeEventTarget} + */ + once(type, listener) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + this.addEventListener(type, listener, { + once: true, + [kIsNodeStyleListener]: true, + }); + return this; + } + + /** + * @param {string} type + * @returns {NodeEventTarget} + */ + removeAllListeners(type) { + if (!isNodeEventTarget(this)) { + throw new ERR_INVALID_THIS("NodeEventTarget"); + } + if (type !== undefined) { + this[kEvents].delete(String(type)); + } else { + this[kEvents].clear(); + } + + return this; + } +} + +Object.defineProperties(NodeEventTarget.prototype, { + setMaxListeners: kEnumerableProperty, + getMaxListeners: kEnumerableProperty, + eventNames: kEnumerableProperty, + listenerCount: kEnumerableProperty, + off: kEnumerableProperty, + removeListener: kEnumerableProperty, + on: kEnumerableProperty, + addListener: kEnumerableProperty, + once: kEnumerableProperty, + emit: kEnumerableProperty, + removeAllListeners: kEnumerableProperty, +}); + +// EventTarget API + +function shouldAddListener(listener) { + if ( + typeof listener === "function" || + typeof listener?.handleEvent === "function" + ) { + return true; + } + + if (listener == null) { + return false; + } + + throw new ERR_INVALID_ARG_TYPE("listener", "EventListener", listener); +} + +function validateEventListenerOptions(options) { + if (typeof options === "boolean") { + return { capture: options }; + } + + if (options === null) { + return {}; + } + validateObject(options, "options", { + allowArray: true, + allowFunction: true, + }); + return { + once: Boolean(options.once), + capture: Boolean(options.capture), + passive: Boolean(options.passive), + signal: options.signal, + weak: options[kWeakHandler], + isNodeStyleListener: Boolean(options[kIsNodeStyleListener]), + }; +} + +function isEventTarget(obj) { + return obj instanceof globalThis.EventTarget; +} + +function isNodeEventTarget(obj) { + return obj?.constructor?.[kIsNodeEventTarget]; +} + +function addCatch(promise) { + const then = promise.then; + if (typeof then === "function") { + then.call(promise, undefined, function (err) { + // The callback is called with nextTick to avoid a follow-up + // rejection from this promise. + emitUncaughtException(err); + }); + } +} + +function emitUncaughtException(err) { + nextTick(() => { + throw err; + }); +} + +function makeEventHandler(handler) { + // Event handlers are dispatched in the order they were first set + // See https://github.com/nodejs/node/pull/35949#issuecomment-722496598 + function eventHandler(...args) { + if (typeof eventHandler.handler !== "function") { + return; + } + return Reflect.apply(eventHandler.handler, this, args); + } + eventHandler.handler = handler; + return eventHandler; +} + +function defineEventHandler(emitter, name) { + // 8.1.5.1 Event handlers - basically `on[eventName]` attributes + Object.defineProperty(emitter, `on${name}`, { + get() { + return this[kHandlers]?.get(name)?.handler ?? null; + }, + set(value) { + if (!this[kHandlers]) { + this[kHandlers] = new Map(); + } + let wrappedHandler = this[kHandlers]?.get(name); + if (wrappedHandler) { + if (typeof wrappedHandler.handler === "function") { + this[kEvents].get(name).size--; + const size = this[kEvents].get(name).size; + this[kRemoveListener](size, name, wrappedHandler.handler, false); + } + wrappedHandler.handler = value; + if (typeof wrappedHandler.handler === "function") { + this[kEvents].get(name).size++; + const size = this[kEvents].get(name).size; + this[kNewListener](size, name, value, false, false, false, false); + } + } else { + wrappedHandler = makeEventHandler(value); + this.addEventListener(name, wrappedHandler); + } + this[kHandlers].set(name, wrappedHandler); + }, + configurable: true, + enumerable: true, + }); +} + +const EventEmitterMixin = (Superclass) => { + class MixedEventEmitter extends Superclass { + constructor(...args) { + super(...args); + EventEmitter.call(this); + } + } + const protoProps = Object.getOwnPropertyDescriptors(EventEmitter.prototype); + delete protoProps.constructor; + Object.defineProperties(MixedEventEmitter.prototype, protoProps); + return MixedEventEmitter; +}; + +export { + CustomEvent, + defineEventHandler, + Event, + EventEmitterMixin, + EventTarget, + initEventTarget, + initNodeEventTarget, + isEventTarget, + kCreateEvent, + kEvents, + kNewListener, + kRemoveListener, + kTrustEvent, + kWeakHandler, + NodeEventTarget, +}; + +export default { + CustomEvent, + Event, + EventEmitterMixin, + EventTarget, + NodeEventTarget, + defineEventHandler, + initEventTarget, + initNodeEventTarget, + kCreateEvent, + kNewListener, + kTrustEvent, + kRemoveListener, + kEvents, + kWeakHandler, + isEventTarget, +}; diff --git a/ext/node/polyfills/internal/fixed_queue.ts b/ext/node/polyfills/internal/fixed_queue.ts new file mode 100644 index 0000000000..e6b8db70fe --- /dev/null +++ b/ext/node/polyfills/internal/fixed_queue.ts @@ -0,0 +1,123 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. + +// Currently optimal queue size, tested on V8 6.0 - 6.6. Must be power of two. +const kSize = 2048; +const kMask = kSize - 1; + +// The FixedQueue is implemented as a singly-linked list of fixed-size +// circular buffers. It looks something like this: +// +// head tail +// | | +// v v +// +-----------+ <-----\ +-----------+ <------\ +-----------+ +// | [null] | \----- | next | \------- | next | +// +-----------+ +-----------+ +-----------+ +// | item | <-- bottom | item | <-- bottom | [empty] | +// | item | | item | | [empty] | +// | item | | item | | [empty] | +// | item | | item | | [empty] | +// | item | | item | bottom --> | item | +// | item | | item | | item | +// | ... | | ... | | ... | +// | item | | item | | item | +// | item | | item | | item | +// | [empty] | <-- top | item | | item | +// | [empty] | | item | | item | +// | [empty] | | [empty] | <-- top top --> | [empty] | +// +-----------+ +-----------+ +-----------+ +// +// Or, if there is only one circular buffer, it looks something +// like either of these: +// +// head tail head tail +// | | | | +// v v v v +// +-----------+ +-----------+ +// | [null] | | [null] | +// +-----------+ +-----------+ +// | [empty] | | item | +// | [empty] | | item | +// | item | <-- bottom top --> | [empty] | +// | item | | [empty] | +// | [empty] | <-- top bottom --> | item | +// | [empty] | | item | +// +-----------+ +-----------+ +// +// Adding a value means moving `top` forward by one, removing means +// moving `bottom` forward by one. After reaching the end, the queue +// wraps around. +// +// When `top === bottom` the current queue is empty and when +// `top + 1 === bottom` it's full. This wastes a single space of storage +// but allows much quicker checks. + +class FixedCircularBuffer { + bottom: number; + top: number; + list: undefined | Array; + next: FixedCircularBuffer | null; + + constructor() { + this.bottom = 0; + this.top = 0; + this.list = new Array(kSize); + this.next = null; + } + + isEmpty() { + return this.top === this.bottom; + } + + isFull() { + return ((this.top + 1) & kMask) === this.bottom; + } + + push(data: unknown) { + this.list![this.top] = data; + this.top = (this.top + 1) & kMask; + } + + shift() { + const nextItem = this.list![this.bottom]; + if (nextItem === undefined) { + return null; + } + this.list![this.bottom] = undefined; + this.bottom = (this.bottom + 1) & kMask; + return nextItem; + } +} + +export class FixedQueue { + head: FixedCircularBuffer; + tail: FixedCircularBuffer; + + constructor() { + this.head = this.tail = new FixedCircularBuffer(); + } + + isEmpty() { + return this.head.isEmpty(); + } + + push(data: unknown) { + if (this.head.isFull()) { + // Head is full: Creates a new queue, sets the old queue's `.next` to it, + // and sets it as the new main queue. + this.head = this.head.next = new FixedCircularBuffer(); + } + this.head.push(data); + } + + shift() { + const tail = this.tail; + const next = tail.shift(); + if (tail.isEmpty() && tail.next !== null) { + // If there is another queue, it forms the new tail. + this.tail = tail.next; + } + return next; + } +} diff --git a/ext/node/polyfills/internal/freelist.ts b/ext/node/polyfills/internal/freelist.ts new file mode 100644 index 0000000000..8faba8e680 --- /dev/null +++ b/ext/node/polyfills/internal/freelist.ts @@ -0,0 +1,30 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +type Fn = (...args: unknown[]) => T; +export class FreeList { + name: string; + ctor: Fn; + max: number; + list: Array; + constructor(name: string, max: number, ctor: Fn) { + this.name = name; + this.ctor = ctor; + this.max = max; + this.list = []; + } + + alloc(): T { + return this.list.length > 0 + ? this.list.pop() + : Reflect.apply(this.ctor, this, arguments); + } + + free(obj: T) { + if (this.list.length < this.max) { + this.list.push(obj); + return true; + } + return false; + } +} diff --git a/ext/node/polyfills/internal/fs/streams.d.ts b/ext/node/polyfills/internal/fs/streams.d.ts new file mode 100644 index 0000000000..9e70c2431f --- /dev/null +++ b/ext/node/polyfills/internal/fs/streams.d.ts @@ -0,0 +1,326 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright DefinitelyTyped contributors. All rights reserved. MIT license. +// deno-lint-ignore-file no-explicit-any + +import * as stream from "internal:deno_node/polyfills/_stream.d.ts"; +import * as promises from "internal:deno_node/polyfills/fs/promises.ts"; + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + BufferEncoding, + ErrnoException, +} from "internal:deno_node/polyfills/_global.d.ts"; + +type PathLike = string | Buffer | URL; + +/** + * Instances of `fs.ReadStream` are created and returned using the {@link createReadStream} function. + * @since v0.1.93 + */ +export class ReadStream extends stream.Readable { + close(callback?: (err?: ErrnoException | null) => void): void; + /** + * The number of bytes that have been read so far. + * @since v6.4.0 + */ + bytesRead: number; + /** + * The path to the file the stream is reading from as specified in the first + * argument to `fs.createReadStream()`. If `path` is passed as a string, then`readStream.path` will be a string. If `path` is passed as a `Buffer`, then`readStream.path` will be a + * `Buffer`. If `fd` is specified, then`readStream.path` will be `undefined`. + * @since v0.1.93 + */ + path: string | Buffer; + /** + * This property is `true` if the underlying file has not been opened yet, + * i.e. before the `'ready'` event is emitted. + * @since v11.2.0, v10.16.0 + */ + pending: boolean; + /** + * events.EventEmitter + * 1. open + * 2. close + * 3. ready + */ + addListener(event: "close", listener: () => void): this; + addListener(event: "data", listener: (chunk: Buffer | string) => void): this; + addListener(event: "end", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "open", listener: (fd: number) => void): this; + addListener(event: "pause", listener: () => void): this; + addListener(event: "readable", listener: () => void): this; + addListener(event: "ready", listener: () => void): this; + addListener(event: "resume", listener: () => void): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "data", listener: (chunk: Buffer | string) => void): this; + on(event: "end", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "open", listener: (fd: number) => void): this; + on(event: "pause", listener: () => void): this; + on(event: "readable", listener: () => void): this; + on(event: "ready", listener: () => void): this; + on(event: "resume", listener: () => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "data", listener: (chunk: Buffer | string) => void): this; + once(event: "end", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "open", listener: (fd: number) => void): this; + once(event: "pause", listener: () => void): this; + once(event: "readable", listener: () => void): this; + once(event: "ready", listener: () => void): this; + once(event: "resume", listener: () => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener( + event: "data", + listener: (chunk: Buffer | string) => void, + ): this; + prependListener(event: "end", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "open", listener: (fd: number) => void): this; + prependListener(event: "pause", listener: () => void): this; + prependListener(event: "readable", listener: () => void): this; + prependListener(event: "ready", listener: () => void): this; + prependListener(event: "resume", listener: () => void): this; + prependListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener( + event: "data", + listener: (chunk: Buffer | string) => void, + ): this; + prependOnceListener(event: "end", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "open", listener: (fd: number) => void): this; + prependOnceListener(event: "pause", listener: () => void): this; + prependOnceListener(event: "readable", listener: () => void): this; + prependOnceListener(event: "ready", listener: () => void): this; + prependOnceListener(event: "resume", listener: () => void): this; + prependOnceListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; +} +/** + * * Extends `stream.Writable` + * + * Instances of `fs.WriteStream` are created and returned using the {@link createWriteStream} function. + * @since v0.1.93 + */ +export class WriteStream extends stream.Writable { + /** + * Closes `writeStream`. Optionally accepts a + * callback that will be executed once the `writeStream`is closed. + * @since v0.9.4 + */ + close(callback?: (err?: ErrnoException | null) => void): void; + /** + * The number of bytes written so far. Does not include data that is still queued + * for writing. + * @since v0.4.7 + */ + bytesWritten: number; + /** + * The path to the file the stream is writing to as specified in the first + * argument to {@link createWriteStream}. If `path` is passed as a string, then`writeStream.path` will be a string. If `path` is passed as a `Buffer`, then`writeStream.path` will be a + * `Buffer`. + * @since v0.1.93 + */ + path: string | Buffer; + /** + * This property is `true` if the underlying file has not been opened yet, + * i.e. before the `'ready'` event is emitted. + * @since v11.2.0 + */ + pending: boolean; + /** + * events.EventEmitter + * 1. open + * 2. close + * 3. ready + */ + addListener(event: "close", listener: () => void): this; + addListener(event: "drain", listener: () => void): this; + addListener(event: "error", listener: (err: Error) => void): this; + addListener(event: "finish", listener: () => void): this; + addListener(event: "open", listener: (fd: number) => void): this; + addListener(event: "pipe", listener: (src: stream.Readable) => void): this; + addListener(event: "ready", listener: () => void): this; + addListener(event: "unpipe", listener: (src: stream.Readable) => void): this; + addListener(event: string | symbol, listener: (...args: any[]) => void): this; + on(event: "close", listener: () => void): this; + on(event: "drain", listener: () => void): this; + on(event: "error", listener: (err: Error) => void): this; + on(event: "finish", listener: () => void): this; + on(event: "open", listener: (fd: number) => void): this; + on(event: "pipe", listener: (src: stream.Readable) => void): this; + on(event: "ready", listener: () => void): this; + on(event: "unpipe", listener: (src: stream.Readable) => void): this; + on(event: string | symbol, listener: (...args: any[]) => void): this; + once(event: "close", listener: () => void): this; + once(event: "drain", listener: () => void): this; + once(event: "error", listener: (err: Error) => void): this; + once(event: "finish", listener: () => void): this; + once(event: "open", listener: (fd: number) => void): this; + once(event: "pipe", listener: (src: stream.Readable) => void): this; + once(event: "ready", listener: () => void): this; + once(event: "unpipe", listener: (src: stream.Readable) => void): this; + once(event: string | symbol, listener: (...args: any[]) => void): this; + prependListener(event: "close", listener: () => void): this; + prependListener(event: "drain", listener: () => void): this; + prependListener(event: "error", listener: (err: Error) => void): this; + prependListener(event: "finish", listener: () => void): this; + prependListener(event: "open", listener: (fd: number) => void): this; + prependListener( + event: "pipe", + listener: (src: stream.Readable) => void, + ): this; + prependListener(event: "ready", listener: () => void): this; + prependListener( + event: "unpipe", + listener: (src: stream.Readable) => void, + ): this; + prependListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; + prependOnceListener(event: "close", listener: () => void): this; + prependOnceListener(event: "drain", listener: () => void): this; + prependOnceListener(event: "error", listener: (err: Error) => void): this; + prependOnceListener(event: "finish", listener: () => void): this; + prependOnceListener(event: "open", listener: (fd: number) => void): this; + prependOnceListener( + event: "pipe", + listener: (src: stream.Readable) => void, + ): this; + prependOnceListener(event: "ready", listener: () => void): this; + prependOnceListener( + event: "unpipe", + listener: (src: stream.Readable) => void, + ): this; + prependOnceListener( + event: string | symbol, + listener: (...args: any[]) => void, + ): this; +} +interface StreamOptions { + flags?: string | undefined; + encoding?: BufferEncoding | undefined; + // @ts-ignore promises.FileHandle is not implemented + fd?: number | promises.FileHandle | undefined; + mode?: number | undefined; + autoClose?: boolean | undefined; + /** + * @default false + */ + emitClose?: boolean | undefined; + start?: number | undefined; + highWaterMark?: number | undefined; +} +interface ReadStreamOptions extends StreamOptions { + end?: number | undefined; +} +/** + * Unlike the 16 kb default `highWaterMark` for a `stream.Readable`, the stream + * returned by this method has a default `highWaterMark` of 64 kb. + * + * `options` can include `start` and `end` values to read a range of bytes from + * the file instead of the entire file. Both `start` and `end` are inclusive and + * start counting at 0, allowed values are in the + * \[0, [`Number.MAX_SAFE_INTEGER`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)\] range. If `fd` is specified and `start` is + * omitted or `undefined`, `fs.createReadStream()` reads sequentially from the + * current file position. The `encoding` can be any one of those accepted by `Buffer`. + * + * If `fd` is specified, `ReadStream` will ignore the `path` argument and will use + * the specified file descriptor. This means that no `'open'` event will be + * emitted. `fd` should be blocking; non-blocking `fd`s should be passed to `net.Socket`. + * + * If `fd` points to a character device that only supports blocking reads + * (such as keyboard or sound card), read operations do not finish until data is + * available. This can prevent the process from exiting and the stream from + * closing naturally. + * + * By default, the stream will emit a `'close'` event after it has been + * destroyed. Set the `emitClose` option to `false` to change this behavior. + * + * By providing the `fs` option, it is possible to override the corresponding `fs`implementations for `open`, `read`, and `close`. When providing the `fs` option, + * an override for `read` is required. If no `fd` is provided, an override for`open` is also required. If `autoClose` is `true`, an override for `close` is + * also required. + * + * ```js + * import { createReadStream } from "internal:deno_node/polyfills/internal/fs/fs"; + * + * // Create a stream from some character device. + * const stream = createReadStream('/dev/input/event0'); + * setTimeout(() => { + * stream.close(); // This may not close the stream. + * // Artificially marking end-of-stream, as if the underlying resource had + * // indicated end-of-file by itself, allows the stream to close. + * // This does not cancel pending read operations, and if there is such an + * // operation, the process may still not be able to exit successfully + * // until it finishes. + * stream.push(null); + * stream.read(0); + * }, 100); + * ``` + * + * If `autoClose` is false, then the file descriptor won't be closed, even if + * there's an error. It is the application's responsibility to close it and make + * sure there's no file descriptor leak. If `autoClose` is set to true (default + * behavior), on `'error'` or `'end'` the file descriptor will be closed + * automatically. + * + * `mode` sets the file mode (permission and sticky bits), but only if the + * file was created. + * + * An example to read the last 10 bytes of a file which is 100 bytes long: + * + * ```js + * import { createReadStream } from "internal:deno_node/polyfills/internal/fs/fs"; + * + * createReadStream('sample.txt', { start: 90, end: 99 }); + * ``` + * + * If `options` is a string, then it specifies the encoding. + * @since v0.1.31 + */ +export function createReadStream( + path: PathLike, + options?: BufferEncoding | ReadStreamOptions, +): ReadStream; +/** + * `options` may also include a `start` option to allow writing data at some + * position past the beginning of the file, allowed values are in the + * \[0, [`Number.MAX_SAFE_INTEGER`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER)\] range. Modifying a file rather than + * replacing it may require the `flags` option to be set to `r+` rather than the + * default `w`. The `encoding` can be any one of those accepted by `Buffer`. + * + * If `autoClose` is set to true (default behavior) on `'error'` or `'finish'`the file descriptor will be closed automatically. If `autoClose` is false, + * then the file descriptor won't be closed, even if there's an error. + * It is the application's responsibility to close it and make sure there's no + * file descriptor leak. + * + * By default, the stream will emit a `'close'` event after it has been + * destroyed. Set the `emitClose` option to `false` to change this behavior. + * + * By providing the `fs` option it is possible to override the corresponding `fs`implementations for `open`, `write`, `writev` and `close`. Overriding `write()`without `writev()` can reduce + * performance as some optimizations (`_writev()`) + * will be disabled. When providing the `fs` option, overrides for at least one of`write` and `writev` are required. If no `fd` option is supplied, an override + * for `open` is also required. If `autoClose` is `true`, an override for `close`is also required. + * + * Like `fs.ReadStream`, if `fd` is specified, `fs.WriteStream` will ignore the`path` argument and will use the specified file descriptor. This means that no`'open'` event will be + * emitted. `fd` should be blocking; non-blocking `fd`s + * should be passed to `net.Socket`. + * + * If `options` is a string, then it specifies the encoding. + * @since v0.1.31 + */ +export function createWriteStream( + path: PathLike, + options?: BufferEncoding | StreamOptions, +): WriteStream; diff --git a/ext/node/polyfills/internal/fs/streams.mjs b/ext/node/polyfills/internal/fs/streams.mjs new file mode 100644 index 0000000000..4d751df761 --- /dev/null +++ b/ext/node/polyfills/internal/fs/streams.mjs @@ -0,0 +1,494 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { kEmptyObject } from "internal:deno_node/polyfills/internal/util.mjs"; +import { deprecate } from "internal:deno_node/polyfills/util.ts"; +import { validateFunction, validateInteger } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { errorOrDestroy } from "internal:deno_node/polyfills/internal/streams/destroy.mjs"; +import { open as fsOpen } from "internal:deno_node/polyfills/_fs/_fs_open.ts"; +import { read as fsRead } from "internal:deno_node/polyfills/_fs/_fs_read.ts"; +import { write as fsWrite } from "internal:deno_node/polyfills/_fs/_fs_write.mjs"; +import { writev as fsWritev } from "internal:deno_node/polyfills/_fs/_fs_writev.mjs"; +import { close as fsClose } from "internal:deno_node/polyfills/_fs/_fs_close.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + copyObject, + getOptions, + getValidatedFd, + validatePath, +} from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import { finished, Readable, Writable } from "internal:deno_node/polyfills/stream.ts"; +import { toPathIfFileURL } from "internal:deno_node/polyfills/internal/url.ts"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +const kIoDone = Symbol("kIoDone"); +const kIsPerformingIO = Symbol("kIsPerformingIO"); + +const kFs = Symbol("kFs"); + +function _construct(callback) { + // deno-lint-ignore no-this-alias + const stream = this; + if (typeof stream.fd === "number") { + callback(); + return; + } + + if (stream.open !== openWriteFs && stream.open !== openReadFs) { + // Backwards compat for monkey patching open(). + const orgEmit = stream.emit; + stream.emit = function (...args) { + if (args[0] === "open") { + this.emit = orgEmit; + callback(); + Reflect.apply(orgEmit, this, args); + } else if (args[0] === "error") { + this.emit = orgEmit; + callback(args[1]); + } else { + Reflect.apply(orgEmit, this, args); + } + }; + stream.open(); + } else { + stream[kFs].open( + stream.path.toString(), + stream.flags, + stream.mode, + (er, fd) => { + if (er) { + callback(er); + } else { + stream.fd = fd; + callback(); + stream.emit("open", stream.fd); + stream.emit("ready"); + } + }, + ); + } +} + +function close(stream, err, cb) { + if (!stream.fd) { + cb(err); + } else { + stream[kFs].close(stream.fd, (er) => { + cb(er || err); + }); + stream.fd = null; + } +} + +function importFd(stream, options) { + if (typeof options.fd === "number") { + // When fd is a raw descriptor, we must keep our fingers crossed + // that the descriptor won't get closed, or worse, replaced with + // another one + // https://github.com/nodejs/node/issues/35862 + if (stream instanceof ReadStream) { + stream[kFs] = options.fs || { read: fsRead, close: fsClose }; + } + if (stream instanceof WriteStream) { + stream[kFs] = options.fs || + { write: fsWrite, writev: fsWritev, close: fsClose }; + } + return options.fd; + } + + throw new ERR_INVALID_ARG_TYPE("options.fd", ["number"], options.fd); +} + +export function ReadStream(path, options) { + if (!(this instanceof ReadStream)) { + return new ReadStream(path, options); + } + + // A little bit bigger buffer and water marks by default + options = copyObject(getOptions(options, kEmptyObject)); + if (options.highWaterMark === undefined) { + options.highWaterMark = 64 * 1024; + } + + if (options.autoDestroy === undefined) { + options.autoDestroy = false; + } + + if (options.fd == null) { + this.fd = null; + this[kFs] = options.fs || { open: fsOpen, read: fsRead, close: fsClose }; + validateFunction(this[kFs].open, "options.fs.open"); + + // Path will be ignored when fd is specified, so it can be falsy + this.path = toPathIfFileURL(path); + this.flags = options.flags === undefined ? "r" : options.flags; + this.mode = options.mode === undefined ? 0o666 : options.mode; + + validatePath(this.path); + } else { + this.fd = getValidatedFd(importFd(this, options)); + } + + options.autoDestroy = options.autoClose === undefined + ? true + : options.autoClose; + + validateFunction(this[kFs].read, "options.fs.read"); + + if (options.autoDestroy) { + validateFunction(this[kFs].close, "options.fs.close"); + } + + this.start = options.start; + this.end = options.end ?? Infinity; + this.pos = undefined; + this.bytesRead = 0; + this[kIsPerformingIO] = false; + + if (this.start !== undefined) { + validateInteger(this.start, "start", 0); + + this.pos = this.start; + } + + if (this.end !== Infinity) { + validateInteger(this.end, "end", 0); + + if (this.start !== undefined && this.start > this.end) { + throw new ERR_OUT_OF_RANGE( + "start", + `<= "end" (here: ${this.end})`, + this.start, + ); + } + } + + Reflect.apply(Readable, this, [options]); +} + +Object.setPrototypeOf(ReadStream.prototype, Readable.prototype); +Object.setPrototypeOf(ReadStream, Readable); + +Object.defineProperty(ReadStream.prototype, "autoClose", { + get() { + return this._readableState.autoDestroy; + }, + set(val) { + this._readableState.autoDestroy = val; + }, +}); + +const openReadFs = deprecate( + function () { + // Noop. + }, + "ReadStream.prototype.open() is deprecated", + "DEP0135", +); +ReadStream.prototype.open = openReadFs; + +ReadStream.prototype._construct = _construct; + +ReadStream.prototype._read = async function (n) { + n = this.pos !== undefined + ? Math.min(this.end - this.pos + 1, n) + : Math.min(this.end - this.bytesRead + 1, n); + + if (n <= 0) { + this.push(null); + return; + } + + const buf = Buffer.allocUnsafeSlow(n); + + let error = null; + let bytesRead = null; + let buffer = undefined; + + this[kIsPerformingIO] = true; + + await new Promise((resolve) => { + this[kFs] + .read( + this.fd, + buf, + 0, + n, + this.pos ?? null, + (_er, _bytesRead, _buf) => { + error = _er; + bytesRead = _bytesRead; + buffer = _buf; + return resolve(true); + }, + ); + }); + + this[kIsPerformingIO] = false; + + // Tell ._destroy() that it's safe to close the fd now. + if (this.destroyed) { + this.emit(kIoDone, error); + return; + } + + if (error) { + errorOrDestroy(this, error); + } else if ( + typeof bytesRead === "number" && + bytesRead > 0 + ) { + if (this.pos !== undefined) { + this.pos += bytesRead; + } + + this.bytesRead += bytesRead; + + if (bytesRead !== buffer.length) { + // Slow path. Shrink to fit. + // Copy instead of slice so that we don't retain + // large backing buffer for small reads. + const dst = Buffer.allocUnsafeSlow(bytesRead); + buffer.copy(dst, 0, 0, bytesRead); + buffer = dst; + } + + this.push(buffer); + } else { + this.push(null); + } +}; + +ReadStream.prototype._destroy = function (err, cb) { + // Usually for async IO it is safe to close a file descriptor + // even when there are pending operations. However, due to platform + // differences file IO is implemented using synchronous operations + // running in a thread pool. Therefore, file descriptors are not safe + // to close while used in a pending read or write operation. Wait for + // any pending IO (kIsPerformingIO) to complete (kIoDone). + if (this[kIsPerformingIO]) { + this.once(kIoDone, (er) => close(this, err || er, cb)); + } else { + close(this, err, cb); + } +}; + +ReadStream.prototype.close = function (cb) { + if (typeof cb === "function") finished(this, cb); + this.destroy(); +}; + +Object.defineProperty(ReadStream.prototype, "pending", { + get() { + return this.fd === null; + }, + configurable: true, +}); + +export function WriteStream(path, options) { + if (!(this instanceof WriteStream)) { + return new WriteStream(path, options); + } + + options = copyObject(getOptions(options, kEmptyObject)); + + // Only buffers are supported. + options.decodeStrings = true; + + if (options.fd == null) { + this.fd = null; + this[kFs] = options.fs || + { open: fsOpen, write: fsWrite, writev: fsWritev, close: fsClose }; + validateFunction(this[kFs].open, "options.fs.open"); + + // Path will be ignored when fd is specified, so it can be falsy + this.path = toPathIfFileURL(path); + this.flags = options.flags === undefined ? "w" : options.flags; + this.mode = options.mode === undefined ? 0o666 : options.mode; + + validatePath(this.path); + } else { + this.fd = getValidatedFd(importFd(this, options)); + } + + options.autoDestroy = options.autoClose === undefined + ? true + : options.autoClose; + + if (!this[kFs].write && !this[kFs].writev) { + throw new ERR_INVALID_ARG_TYPE( + "options.fs.write", + "function", + this[kFs].write, + ); + } + + if (this[kFs].write) { + validateFunction(this[kFs].write, "options.fs.write"); + } + + if (this[kFs].writev) { + validateFunction(this[kFs].writev, "options.fs.writev"); + } + + if (options.autoDestroy) { + validateFunction(this[kFs].close, "options.fs.close"); + } + + // It's enough to override either, in which case only one will be used. + if (!this[kFs].write) { + this._write = null; + } + if (!this[kFs].writev) { + this._writev = null; + } + + this.start = options.start; + this.pos = undefined; + this.bytesWritten = 0; + this[kIsPerformingIO] = false; + + if (this.start !== undefined) { + validateInteger(this.start, "start", 0); + + this.pos = this.start; + } + + Reflect.apply(Writable, this, [options]); + + if (options.encoding) { + this.setDefaultEncoding(options.encoding); + } +} + +Object.setPrototypeOf(WriteStream.prototype, Writable.prototype); +Object.setPrototypeOf(WriteStream, Writable); + +Object.defineProperty(WriteStream.prototype, "autoClose", { + get() { + return this._writableState.autoDestroy; + }, + set(val) { + this._writableState.autoDestroy = val; + }, +}); + +const openWriteFs = deprecate( + function () { + // Noop. + }, + "WriteStream.prototype.open() is deprecated", + "DEP0135", +); +WriteStream.prototype.open = openWriteFs; + +WriteStream.prototype._construct = _construct; + +WriteStream.prototype._write = function (data, _encoding, cb) { + this[kIsPerformingIO] = true; + this[kFs].write(this.fd, data, 0, data.length, this.pos, (er, bytes) => { + this[kIsPerformingIO] = false; + if (this.destroyed) { + // Tell ._destroy() that it's safe to close the fd now. + cb(er); + return this.emit(kIoDone, er); + } + + if (er) { + return cb(er); + } + + this.bytesWritten += bytes; + cb(); + }); + + if (this.pos !== undefined) { + this.pos += data.length; + } +}; + +WriteStream.prototype._writev = function (data, cb) { + const len = data.length; + const chunks = new Array(len); + let size = 0; + + for (let i = 0; i < len; i++) { + const chunk = data[i].chunk; + + chunks[i] = chunk; + size += chunk.length; + } + + this[kIsPerformingIO] = true; + this[kFs].writev(this.fd, chunks, this.pos ?? null, (er, bytes) => { + this[kIsPerformingIO] = false; + if (this.destroyed) { + // Tell ._destroy() that it's safe to close the fd now. + cb(er); + return this.emit(kIoDone, er); + } + + if (er) { + return cb(er); + } + + this.bytesWritten += bytes; + cb(); + }); + + if (this.pos !== undefined) { + this.pos += size; + } +}; + +WriteStream.prototype._destroy = function (err, cb) { + // Usually for async IO it is safe to close a file descriptor + // even when there are pending operations. However, due to platform + // differences file IO is implemented using synchronous operations + // running in a thread pool. Therefore, file descriptors are not safe + // to close while used in a pending read or write operation. Wait for + // any pending IO (kIsPerformingIO) to complete (kIoDone). + if (this[kIsPerformingIO]) { + this.once(kIoDone, (er) => close(this, err || er, cb)); + } else { + close(this, err, cb); + } +}; + +WriteStream.prototype.close = function (cb) { + if (cb) { + if (this.closed) { + nextTick(cb); + return; + } + this.on("close", cb); + } + + // If we are not autoClosing, we should call + // destroy on 'finish'. + if (!this.autoClose) { + this.on("finish", this.destroy); + } + + // We use end() instead of destroy() because of + // https://github.com/nodejs/node/issues/2006 + this.end(); +}; + +// There is no shutdown() for files. +WriteStream.prototype.destroySoon = WriteStream.prototype.end; + +Object.defineProperty(WriteStream.prototype, "pending", { + get() { + return this.fd === null; + }, + configurable: true, +}); + +export function createReadStream(path, options) { + return new ReadStream(path, options); +} + +export function createWriteStream(path, options) { + return new WriteStream(path, options); +} diff --git a/ext/node/polyfills/internal/fs/utils.mjs b/ext/node/polyfills/internal/fs/utils.mjs new file mode 100644 index 0000000000..9d74c5eee3 --- /dev/null +++ b/ext/node/polyfills/internal/fs/utils.mjs @@ -0,0 +1,1045 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +"use strict"; + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + ERR_FS_EISDIR, + ERR_FS_INVALID_SYMLINK_TYPE, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_OUT_OF_RANGE, + hideStackFrames, + uvException, +} from "internal:deno_node/polyfills/internal/errors.ts"; + +import { + isArrayBufferView, + isBigUint64Array, + isDate, + isUint8Array, +} from "internal:deno_node/polyfills/internal/util/types.ts"; +import { once } from "internal:deno_node/polyfills/internal/util.mjs"; +import { deprecate } from "internal:deno_node/polyfills/util.ts"; +import { toPathIfFileURL } from "internal:deno_node/polyfills/internal/url.ts"; +import { + validateAbortSignal, + validateBoolean, + validateFunction, + validateInt32, + validateInteger, + validateObject, + validateUint32, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import pathModule from "internal:deno_node/polyfills/path.ts"; +const kType = Symbol("type"); +const kStats = Symbol("stats"); +import assert from "internal:deno_node/polyfills/internal/assert.mjs"; +import { lstat, lstatSync } from "internal:deno_node/polyfills/_fs/_fs_lstat.ts"; +import { stat, statSync } from "internal:deno_node/polyfills/_fs/_fs_stat.ts"; +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; +import process from "internal:deno_node/polyfills/process.ts"; + +import { + fs as fsConstants, + os as osConstants, +} from "internal:deno_node/polyfills/internal_binding/constants.ts"; +const { + F_OK = 0, + W_OK = 0, + R_OK = 0, + X_OK = 0, + COPYFILE_EXCL, + COPYFILE_FICLONE, + COPYFILE_FICLONE_FORCE, + O_APPEND, + O_CREAT, + O_EXCL, + O_RDONLY, + O_RDWR, + O_SYNC, + O_TRUNC, + O_WRONLY, + S_IFBLK, + S_IFCHR, + S_IFDIR, + S_IFIFO, + S_IFLNK, + S_IFMT, + S_IFREG, + S_IFSOCK, + UV_FS_SYMLINK_DIR, + UV_FS_SYMLINK_JUNCTION, + UV_DIRENT_UNKNOWN, + UV_DIRENT_FILE, + UV_DIRENT_DIR, + UV_DIRENT_LINK, + UV_DIRENT_FIFO, + UV_DIRENT_SOCKET, + UV_DIRENT_CHAR, + UV_DIRENT_BLOCK, +} = fsConstants; + +// The access modes can be any of F_OK, R_OK, W_OK or X_OK. Some might not be +// available on specific systems. They can be used in combination as well +// (F_OK | R_OK | W_OK | X_OK). +const kMinimumAccessMode = Math.min(F_OK, W_OK, R_OK, X_OK); +const kMaximumAccessMode = F_OK | W_OK | R_OK | X_OK; + +const kDefaultCopyMode = 0; +// The copy modes can be any of COPYFILE_EXCL, COPYFILE_FICLONE or +// COPYFILE_FICLONE_FORCE. They can be used in combination as well +// (COPYFILE_EXCL | COPYFILE_FICLONE | COPYFILE_FICLONE_FORCE). +const kMinimumCopyMode = Math.min( + kDefaultCopyMode, + COPYFILE_EXCL, + COPYFILE_FICLONE, + COPYFILE_FICLONE_FORCE, +); +const kMaximumCopyMode = COPYFILE_EXCL | + COPYFILE_FICLONE | + COPYFILE_FICLONE_FORCE; + +// Most platforms don't allow reads or writes >= 2 GB. +// See https://github.com/libuv/libuv/pull/1501. +const kIoMaxLength = 2 ** 31 - 1; + +// Use 64kb in case the file type is not a regular file and thus do not know the +// actual file size. Increasing the value further results in more frequent over +// allocation for small files and consumes CPU time and memory that should be +// used else wise. +// Use up to 512kb per read otherwise to partition reading big files to prevent +// blocking other threads in case the available threads are all in use. +const kReadFileUnknownBufferLength = 64 * 1024; +const kReadFileBufferLength = 512 * 1024; + +const kWriteFileMaxChunkSize = 512 * 1024; + +export const kMaxUserId = 2 ** 32 - 1; + +export function assertEncoding(encoding) { + if (encoding && !Buffer.isEncoding(encoding)) { + const reason = "is invalid encoding"; + throw new ERR_INVALID_ARG_VALUE(encoding, "encoding", reason); + } +} + +export class Dirent { + constructor(name, type) { + this.name = name; + this[kType] = type; + } + + isDirectory() { + return this[kType] === UV_DIRENT_DIR; + } + + isFile() { + return this[kType] === UV_DIRENT_FILE; + } + + isBlockDevice() { + return this[kType] === UV_DIRENT_BLOCK; + } + + isCharacterDevice() { + return this[kType] === UV_DIRENT_CHAR; + } + + isSymbolicLink() { + return this[kType] === UV_DIRENT_LINK; + } + + isFIFO() { + return this[kType] === UV_DIRENT_FIFO; + } + + isSocket() { + return this[kType] === UV_DIRENT_SOCKET; + } +} + +class DirentFromStats extends Dirent { + constructor(name, stats) { + super(name, null); + this[kStats] = stats; + } +} + +for (const name of Reflect.ownKeys(Dirent.prototype)) { + if (name === "constructor") { + continue; + } + DirentFromStats.prototype[name] = function () { + return this[kStats][name](); + }; +} + +export function copyObject(source) { + const target = {}; + for (const key in source) { + target[key] = source[key]; + } + return target; +} + +const bufferSep = Buffer.from(pathModule.sep); + +function join(path, name) { + if ( + (typeof path === "string" || isUint8Array(path)) && + name === undefined + ) { + return path; + } + + if (typeof path === "string" && isUint8Array(name)) { + const pathBuffer = Buffer.from(pathModule.join(path, pathModule.sep)); + return Buffer.concat([pathBuffer, name]); + } + + if (typeof path === "string" && typeof name === "string") { + return pathModule.join(path, name); + } + + if (isUint8Array(path) && isUint8Array(name)) { + return Buffer.concat([path, bufferSep, name]); + } + + throw new ERR_INVALID_ARG_TYPE( + "path", + ["string", "Buffer"], + path, + ); +} + +export function getDirents(path, { 0: names, 1: types }, callback) { + let i; + if (typeof callback === "function") { + const len = names.length; + let toFinish = 0; + callback = once(callback); + for (i = 0; i < len; i++) { + const type = types[i]; + if (type === UV_DIRENT_UNKNOWN) { + const name = names[i]; + const idx = i; + toFinish++; + let filepath; + try { + filepath = join(path, name); + } catch (err) { + callback(err); + return; + } + lstat(filepath, (err, stats) => { + if (err) { + callback(err); + return; + } + names[idx] = new DirentFromStats(name, stats); + if (--toFinish === 0) { + callback(null, names); + } + }); + } else { + names[i] = new Dirent(names[i], types[i]); + } + } + if (toFinish === 0) { + callback(null, names); + } + } else { + const len = names.length; + for (i = 0; i < len; i++) { + names[i] = getDirent(path, names[i], types[i]); + } + return names; + } +} + +export function getDirent(path, name, type, callback) { + if (typeof callback === "function") { + if (type === UV_DIRENT_UNKNOWN) { + let filepath; + try { + filepath = join(path, name); + } catch (err) { + callback(err); + return; + } + lstat(filepath, (err, stats) => { + if (err) { + callback(err); + return; + } + callback(null, new DirentFromStats(name, stats)); + }); + } else { + callback(null, new Dirent(name, type)); + } + } else if (type === UV_DIRENT_UNKNOWN) { + const stats = lstatSync(join(path, name)); + return new DirentFromStats(name, stats); + } else { + return new Dirent(name, type); + } +} + +export function getOptions(options, defaultOptions) { + if ( + options === null || options === undefined || + typeof options === "function" + ) { + return defaultOptions; + } + + if (typeof options === "string") { + defaultOptions = { ...defaultOptions }; + defaultOptions.encoding = options; + options = defaultOptions; + } else if (typeof options !== "object") { + throw new ERR_INVALID_ARG_TYPE("options", ["string", "Object"], options); + } + + if (options.encoding !== "buffer") { + assertEncoding(options.encoding); + } + + if (options.signal !== undefined) { + validateAbortSignal(options.signal, "options.signal"); + } + return options; +} + +/** + * @param {InternalFSBinding.FSSyncContext} ctx + */ +export function handleErrorFromBinding(ctx) { + if (ctx.errno !== undefined) { // libuv error numbers + const err = uvException(ctx); + Error.captureStackTrace(err, handleErrorFromBinding); + throw err; + } + if (ctx.error !== undefined) { // Errors created in C++ land. + // TODO(joyeecheung): currently, ctx.error are encoding errors + // usually caused by memory problems. We need to figure out proper error + // code(s) for this. + Error.captureStackTrace(ctx.error, handleErrorFromBinding); + throw ctx.error; + } +} + +// Check if the path contains null types if it is a string nor Uint8Array, +// otherwise return silently. +export const nullCheck = hideStackFrames( + (path, propName, throwError = true) => { + const pathIsString = typeof path === "string"; + const pathIsUint8Array = isUint8Array(path); + + // We can only perform meaningful checks on strings and Uint8Arrays. + if ( + (!pathIsString && !pathIsUint8Array) || + (pathIsString && !path.includes("\u0000")) || + (pathIsUint8Array && !path.includes(0)) + ) { + return; + } + + const err = new ERR_INVALID_ARG_VALUE( + propName, + path, + "must be a string or Uint8Array without null bytes", + ); + if (throwError) { + throw err; + } + return err; + }, +); + +export function preprocessSymlinkDestination(path, type, linkPath) { + if (!isWindows) { + // No preprocessing is needed on Unix. + return path; + } + path = "" + path; + if (type === "junction") { + // Junctions paths need to be absolute and \\?\-prefixed. + // A relative target is relative to the link's parent directory. + path = pathModule.resolve(linkPath, "..", path); + return pathModule.toNamespacedPath(path); + } + if (pathModule.isAbsolute(path)) { + // If the path is absolute, use the \\?\-prefix to enable long filenames + return pathModule.toNamespacedPath(path); + } + // Windows symlinks don't tolerate forward slashes. + return path.replace(/\//g, "\\"); +} + +// Constructor for file stats. +function StatsBase( + dev, + mode, + nlink, + uid, + gid, + rdev, + blksize, + ino, + size, + blocks, +) { + this.dev = dev; + this.mode = mode; + this.nlink = nlink; + this.uid = uid; + this.gid = gid; + this.rdev = rdev; + this.blksize = blksize; + this.ino = ino; + this.size = size; + this.blocks = blocks; +} + +StatsBase.prototype.isDirectory = function () { + return this._checkModeProperty(S_IFDIR); +}; + +StatsBase.prototype.isFile = function () { + return this._checkModeProperty(S_IFREG); +}; + +StatsBase.prototype.isBlockDevice = function () { + return this._checkModeProperty(S_IFBLK); +}; + +StatsBase.prototype.isCharacterDevice = function () { + return this._checkModeProperty(S_IFCHR); +}; + +StatsBase.prototype.isSymbolicLink = function () { + return this._checkModeProperty(S_IFLNK); +}; + +StatsBase.prototype.isFIFO = function () { + return this._checkModeProperty(S_IFIFO); +}; + +StatsBase.prototype.isSocket = function () { + return this._checkModeProperty(S_IFSOCK); +}; + +const kNsPerMsBigInt = 10n ** 6n; +const kNsPerSecBigInt = 10n ** 9n; +const kMsPerSec = 10 ** 3; +const kNsPerMs = 10 ** 6; +function msFromTimeSpec(sec, nsec) { + return sec * kMsPerSec + nsec / kNsPerMs; +} + +function nsFromTimeSpecBigInt(sec, nsec) { + return sec * kNsPerSecBigInt + nsec; +} + +// The Date constructor performs Math.floor() to the timestamp. +// https://www.ecma-international.org/ecma-262/#sec-timeclip +// Since there may be a precision loss when the timestamp is +// converted to a floating point number, we manually round +// the timestamp here before passing it to Date(). +// Refs: https://github.com/nodejs/node/pull/12607 +function dateFromMs(ms) { + return new Date(Number(ms) + 0.5); +} + +export function BigIntStats( + dev, + mode, + nlink, + uid, + gid, + rdev, + blksize, + ino, + size, + blocks, + atimeNs, + mtimeNs, + ctimeNs, + birthtimeNs, +) { + Reflect.apply(StatsBase, this, [ + dev, + mode, + nlink, + uid, + gid, + rdev, + blksize, + ino, + size, + blocks, + ]); + + this.atimeMs = atimeNs / kNsPerMsBigInt; + this.mtimeMs = mtimeNs / kNsPerMsBigInt; + this.ctimeMs = ctimeNs / kNsPerMsBigInt; + this.birthtimeMs = birthtimeNs / kNsPerMsBigInt; + this.atimeNs = atimeNs; + this.mtimeNs = mtimeNs; + this.ctimeNs = ctimeNs; + this.birthtimeNs = birthtimeNs; + this.atime = dateFromMs(this.atimeMs); + this.mtime = dateFromMs(this.mtimeMs); + this.ctime = dateFromMs(this.ctimeMs); + this.birthtime = dateFromMs(this.birthtimeMs); +} + +Object.setPrototypeOf(BigIntStats.prototype, StatsBase.prototype); +Object.setPrototypeOf(BigIntStats, StatsBase); + +BigIntStats.prototype._checkModeProperty = function (property) { + if ( + isWindows && (property === S_IFIFO || property === S_IFBLK || + property === S_IFSOCK) + ) { + return false; // Some types are not available on Windows + } + return (this.mode & BigInt(S_IFMT)) === BigInt(property); +}; + +export function Stats( + dev, + mode, + nlink, + uid, + gid, + rdev, + blksize, + ino, + size, + blocks, + atimeMs, + mtimeMs, + ctimeMs, + birthtimeMs, +) { + StatsBase.call( + this, + dev, + mode, + nlink, + uid, + gid, + rdev, + blksize, + ino, + size, + blocks, + ); + this.atimeMs = atimeMs; + this.mtimeMs = mtimeMs; + this.ctimeMs = ctimeMs; + this.birthtimeMs = birthtimeMs; + this.atime = dateFromMs(atimeMs); + this.mtime = dateFromMs(mtimeMs); + this.ctime = dateFromMs(ctimeMs); + this.birthtime = dateFromMs(birthtimeMs); +} + +Object.setPrototypeOf(Stats.prototype, StatsBase.prototype); +Object.setPrototypeOf(Stats, StatsBase); + +// HACK: Workaround for https://github.com/standard-things/esm/issues/821. +// TODO(ronag): Remove this as soon as `esm` publishes a fixed version. +Stats.prototype.isFile = StatsBase.prototype.isFile; + +Stats.prototype._checkModeProperty = function (property) { + if ( + isWindows && (property === S_IFIFO || property === S_IFBLK || + property === S_IFSOCK) + ) { + return false; // Some types are not available on Windows + } + return (this.mode & S_IFMT) === property; +}; + +/** + * @param {Float64Array | BigUint64Array} stats + * @param {number} offset + * @returns + */ +export function getStatsFromBinding(stats, offset = 0) { + if (isBigUint64Array(stats)) { + return new BigIntStats( + stats[0 + offset], + stats[1 + offset], + stats[2 + offset], + stats[3 + offset], + stats[4 + offset], + stats[5 + offset], + stats[6 + offset], + stats[7 + offset], + stats[8 + offset], + stats[9 + offset], + nsFromTimeSpecBigInt(stats[10 + offset], stats[11 + offset]), + nsFromTimeSpecBigInt(stats[12 + offset], stats[13 + offset]), + nsFromTimeSpecBigInt(stats[14 + offset], stats[15 + offset]), + nsFromTimeSpecBigInt(stats[16 + offset], stats[17 + offset]), + ); + } + return new Stats( + stats[0 + offset], + stats[1 + offset], + stats[2 + offset], + stats[3 + offset], + stats[4 + offset], + stats[5 + offset], + stats[6 + offset], + stats[7 + offset], + stats[8 + offset], + stats[9 + offset], + msFromTimeSpec(stats[10 + offset], stats[11 + offset]), + msFromTimeSpec(stats[12 + offset], stats[13 + offset]), + msFromTimeSpec(stats[14 + offset], stats[15 + offset]), + msFromTimeSpec(stats[16 + offset], stats[17 + offset]), + ); +} + +export function stringToFlags(flags, name = "flags") { + if (typeof flags === "number") { + validateInt32(flags, name); + return flags; + } + + if (flags == null) { + return O_RDONLY; + } + + switch (flags) { + case "r": + return O_RDONLY; + case "rs": // Fall through. + case "sr": + return O_RDONLY | O_SYNC; + case "r+": + return O_RDWR; + case "rs+": // Fall through. + case "sr+": + return O_RDWR | O_SYNC; + + case "w": + return O_TRUNC | O_CREAT | O_WRONLY; + case "wx": // Fall through. + case "xw": + return O_TRUNC | O_CREAT | O_WRONLY | O_EXCL; + + case "w+": + return O_TRUNC | O_CREAT | O_RDWR; + case "wx+": // Fall through. + case "xw+": + return O_TRUNC | O_CREAT | O_RDWR | O_EXCL; + + case "a": + return O_APPEND | O_CREAT | O_WRONLY; + case "ax": // Fall through. + case "xa": + return O_APPEND | O_CREAT | O_WRONLY | O_EXCL; + case "as": // Fall through. + case "sa": + return O_APPEND | O_CREAT | O_WRONLY | O_SYNC; + + case "a+": + return O_APPEND | O_CREAT | O_RDWR; + case "ax+": // Fall through. + case "xa+": + return O_APPEND | O_CREAT | O_RDWR | O_EXCL; + case "as+": // Fall through. + case "sa+": + return O_APPEND | O_CREAT | O_RDWR | O_SYNC; + } + + throw new ERR_INVALID_ARG_VALUE("flags", flags); +} + +export const stringToSymlinkType = hideStackFrames((type) => { + let flags = 0; + if (typeof type === "string") { + switch (type) { + case "dir": + flags |= UV_FS_SYMLINK_DIR; + break; + case "junction": + flags |= UV_FS_SYMLINK_JUNCTION; + break; + case "file": + break; + default: + throw new ERR_FS_INVALID_SYMLINK_TYPE(type); + } + } + return flags; +}); + +// converts Date or number to a fractional UNIX timestamp +export function toUnixTimestamp(time, name = "time") { + // eslint-disable-next-line eqeqeq + if (typeof time === "string" && +time == time) { + return +time; + } + if (Number.isFinite(time)) { + if (time < 0) { + return Date.now() / 1000; + } + return time; + } + if (isDate(time)) { + // Convert to 123.456 UNIX timestamp + return Date.getTime(time) / 1000; + } + throw new ERR_INVALID_ARG_TYPE(name, ["Date", "Time in seconds"], time); +} + +export const validateOffsetLengthRead = hideStackFrames( + (offset, length, bufferLength) => { + if (offset < 0) { + throw new ERR_OUT_OF_RANGE("offset", ">= 0", offset); + } + if (length < 0) { + throw new ERR_OUT_OF_RANGE("length", ">= 0", length); + } + if (offset + length > bufferLength) { + throw new ERR_OUT_OF_RANGE( + "length", + `<= ${bufferLength - offset}`, + length, + ); + } + }, +); + +export const validateOffsetLengthWrite = hideStackFrames( + (offset, length, byteLength) => { + if (offset > byteLength) { + throw new ERR_OUT_OF_RANGE("offset", `<= ${byteLength}`, offset); + } + + if (length > byteLength - offset) { + throw new ERR_OUT_OF_RANGE("length", `<= ${byteLength - offset}`, length); + } + + if (length < 0) { + throw new ERR_OUT_OF_RANGE("length", ">= 0", length); + } + + validateInt32(length, "length", 0); + }, +); + +export const validatePath = hideStackFrames((path, propName = "path") => { + if (typeof path !== "string" && !isUint8Array(path)) { + throw new ERR_INVALID_ARG_TYPE(propName, ["string", "Buffer", "URL"], path); + } + + const err = nullCheck(path, propName, false); + + if (err !== undefined) { + throw err; + } +}); + +export const getValidatedPath = hideStackFrames( + (fileURLOrPath, propName = "path") => { + const path = toPathIfFileURL(fileURLOrPath); + validatePath(path, propName); + return path; + }, +); + +export const getValidatedFd = hideStackFrames((fd, propName = "fd") => { + if (Object.is(fd, -0)) { + return 0; + } + + validateInt32(fd, propName, 0); + + return fd; +}); + +export const validateBufferArray = hideStackFrames( + (buffers, propName = "buffers") => { + if (!Array.isArray(buffers)) { + throw new ERR_INVALID_ARG_TYPE(propName, "ArrayBufferView[]", buffers); + } + + for (let i = 0; i < buffers.length; i++) { + if (!isArrayBufferView(buffers[i])) { + throw new ERR_INVALID_ARG_TYPE(propName, "ArrayBufferView[]", buffers); + } + } + + return buffers; + }, +); + +let nonPortableTemplateWarn = true; + +export function warnOnNonPortableTemplate(template) { + // Template strings passed to the mkdtemp() family of functions should not + // end with 'X' because they are handled inconsistently across platforms. + if (nonPortableTemplateWarn && template.endsWith("X")) { + process.emitWarning( + "mkdtemp() templates ending with X are not portable. " + + "For details see: https://nodejs.org/api/fs.html", + ); + nonPortableTemplateWarn = false; + } +} + +const defaultCpOptions = { + dereference: false, + errorOnExist: false, + filter: undefined, + force: true, + preserveTimestamps: false, + recursive: false, +}; + +const defaultRmOptions = { + recursive: false, + force: false, + retryDelay: 100, + maxRetries: 0, +}; + +const defaultRmdirOptions = { + retryDelay: 100, + maxRetries: 0, + recursive: false, +}; + +export const validateCpOptions = hideStackFrames((options) => { + if (options === undefined) { + return { ...defaultCpOptions }; + } + validateObject(options, "options"); + options = { ...defaultCpOptions, ...options }; + validateBoolean(options.dereference, "options.dereference"); + validateBoolean(options.errorOnExist, "options.errorOnExist"); + validateBoolean(options.force, "options.force"); + validateBoolean(options.preserveTimestamps, "options.preserveTimestamps"); + validateBoolean(options.recursive, "options.recursive"); + if (options.filter !== undefined) { + validateFunction(options.filter, "options.filter"); + } + return options; +}); + +export const validateRmOptions = hideStackFrames( + (path, options, expectDir, cb) => { + options = validateRmdirOptions(options, defaultRmOptions); + validateBoolean(options.force, "options.force"); + + stat(path, (err, stats) => { + if (err) { + if (options.force && err.code === "ENOENT") { + return cb(null, options); + } + return cb(err, options); + } + + if (expectDir && !stats.isDirectory()) { + return cb(false); + } + + if (stats.isDirectory() && !options.recursive) { + return cb( + new ERR_FS_EISDIR({ + code: "EISDIR", + message: "is a directory", + path, + syscall: "rm", + errno: osConstants.errno.EISDIR, + }), + ); + } + return cb(null, options); + }); + }, +); + +export const validateRmOptionsSync = hideStackFrames( + (path, options, expectDir) => { + options = validateRmdirOptions(options, defaultRmOptions); + validateBoolean(options.force, "options.force"); + + if (!options.force || expectDir || !options.recursive) { + const isDirectory = statSync(path, { throwIfNoEntry: !options.force }) + ?.isDirectory(); + + if (expectDir && !isDirectory) { + return false; + } + + if (isDirectory && !options.recursive) { + throw new ERR_FS_EISDIR({ + code: "EISDIR", + message: "is a directory", + path, + syscall: "rm", + errno: EISDIR, + }); + } + } + + return options; + }, +); + +let recursiveRmdirWarned = process.noDeprecation; +export function emitRecursiveRmdirWarning() { + if (!recursiveRmdirWarned) { + process.emitWarning( + "In future versions of Node.js, fs.rmdir(path, { recursive: true }) " + + "will be removed. Use fs.rm(path, { recursive: true }) instead", + "DeprecationWarning", + "DEP0147", + ); + recursiveRmdirWarned = true; + } +} + +export const validateRmdirOptions = hideStackFrames( + (options, defaults = defaultRmdirOptions) => { + if (options === undefined) { + return defaults; + } + validateObject(options, "options"); + + options = { ...defaults, ...options }; + + validateBoolean(options.recursive, "options.recursive"); + validateInt32(options.retryDelay, "options.retryDelay", 0); + validateUint32(options.maxRetries, "options.maxRetries"); + + return options; + }, +); + +export const getValidMode = hideStackFrames((mode, type) => { + let min = kMinimumAccessMode; + let max = kMaximumAccessMode; + let def = F_OK; + if (type === "copyFile") { + min = kMinimumCopyMode; + max = kMaximumCopyMode; + def = mode || kDefaultCopyMode; + } else { + assert(type === "access"); + } + if (mode == null) { + return def; + } + if (Number.isInteger(mode) && mode >= min && mode <= max) { + return mode; + } + if (typeof mode !== "number") { + throw new ERR_INVALID_ARG_TYPE("mode", "integer", mode); + } + throw new ERR_OUT_OF_RANGE( + "mode", + `an integer >= ${min} && <= ${max}`, + mode, + ); +}); + +export const validateStringAfterArrayBufferView = hideStackFrames( + (buffer, name) => { + if (typeof buffer === "string") { + return; + } + + if ( + typeof buffer === "object" && + buffer !== null && + typeof buffer.toString === "function" && + Object.prototype.hasOwnProperty.call(buffer, "toString") + ) { + return; + } + + throw new ERR_INVALID_ARG_TYPE( + name, + ["string", "Buffer", "TypedArray", "DataView"], + buffer, + ); + }, +); + +export const validatePosition = hideStackFrames((position) => { + if (typeof position === "number") { + validateInteger(position, "position"); + } else if (typeof position === "bigint") { + if (!(position >= -(2n ** 63n) && position <= 2n ** 63n - 1n)) { + throw new ERR_OUT_OF_RANGE( + "position", + `>= ${-(2n ** 63n)} && <= ${2n ** 63n - 1n}`, + position, + ); + } + } else { + throw new ERR_INVALID_ARG_TYPE("position", ["integer", "bigint"], position); + } +}); + +export const realpathCacheKey = Symbol("realpathCacheKey"); +export const constants = { + kIoMaxLength, + kMaxUserId, + kReadFileBufferLength, + kReadFileUnknownBufferLength, + kWriteFileMaxChunkSize, +}; + +export const showStringCoercionDeprecation = deprecate( + () => {}, + "Implicit coercion of objects with own toString property is deprecated.", + "DEP0162", +); + +export default { + constants, + assertEncoding, + BigIntStats, // for testing + copyObject, + Dirent, + emitRecursiveRmdirWarning, + getDirent, + getDirents, + getOptions, + getValidatedFd, + getValidatedPath, + getValidMode, + handleErrorFromBinding, + kMaxUserId, + nullCheck, + preprocessSymlinkDestination, + realpathCacheKey, + getStatsFromBinding, + showStringCoercionDeprecation, + stringToFlags, + stringToSymlinkType, + Stats, + toUnixTimestamp, + validateBufferArray, + validateCpOptions, + validateOffsetLengthRead, + validateOffsetLengthWrite, + validatePath, + validatePosition, + validateRmOptions, + validateRmOptionsSync, + validateRmdirOptions, + validateStringAfterArrayBufferView, + warnOnNonPortableTemplate, +}; diff --git a/ext/node/polyfills/internal/hide_stack_frames.ts b/ext/node/polyfills/internal/hide_stack_frames.ts new file mode 100644 index 0000000000..e1a6e4c27e --- /dev/null +++ b/ext/node/polyfills/internal/hide_stack_frames.ts @@ -0,0 +1,16 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore no-explicit-any +type GenericFunction = (...args: any[]) => any; + +/** This function removes unnecessary frames from Node.js core errors. */ +export function hideStackFrames( + fn: T, +): T { + // We rename the functions that will be hidden to cut off the stacktrace + // at the outermost one. + const hidden = "__node_internal_" + fn.name; + Object.defineProperty(fn, "name", { value: hidden }); + + return fn; +} diff --git a/ext/node/polyfills/internal/http.ts b/ext/node/polyfills/internal/http.ts new file mode 100644 index 0000000000..f541039dc7 --- /dev/null +++ b/ext/node/polyfills/internal/http.ts @@ -0,0 +1,38 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { setUnrefTimeout } from "internal:deno_node/polyfills/timers.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +let utcCache: string | undefined; + +export function utcDate() { + if (!utcCache) cache(); + return utcCache; +} + +function cache() { + const d = new Date(); + utcCache = d.toUTCString(); + setUnrefTimeout(resetCache, 1000 - d.getMilliseconds()); +} + +function resetCache() { + utcCache = undefined; +} + +export function emitStatistics( + _statistics: { startTime: [number, number] } | null, +) { + notImplemented("internal/http.emitStatistics"); +} + +export const kOutHeaders = Symbol("kOutHeaders"); +export const kNeedDrain = Symbol("kNeedDrain"); + +export default { + utcDate, + emitStatistics, + kOutHeaders, + kNeedDrain, +}; diff --git a/ext/node/polyfills/internal/idna.ts b/ext/node/polyfills/internal/idna.ts new file mode 100644 index 0000000000..f71c06eba6 --- /dev/null +++ b/ext/node/polyfills/internal/idna.ts @@ -0,0 +1,461 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// Copyright Mathias Bynens + +// 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. + +// Adapted from https://github.com/mathiasbynens/punycode.js + +// TODO(cmorten): migrate punycode logic to "icu" internal binding and/or "url" +// internal module so there can be re-use within the "url" module etc. + +"use strict"; + +/** Highest positive signed 32-bit float value */ +const maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1 + +/** Bootstring parameters */ +const base = 36; +const tMin = 1; +const tMax = 26; +const skew = 38; +const damp = 700; +const initialBias = 72; +const initialN = 128; // 0x80 +const delimiter = "-"; // '\x2D' + +/** Regular expressions */ +export const regexPunycode = /^xn--/; +export const regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars +const regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators + +/** Error messages */ +const errors: Record = { + "overflow": "Overflow: input needs wider integers to process", + "not-basic": "Illegal input >= 0x80 (not a basic code point)", + "invalid-input": "Invalid input", +}; + +/** Convenience shortcuts */ +const baseMinusTMin = base - tMin; +const floor = Math.floor; + +/** + * A generic error utility function. + * + * @param type The error type. + * @return Throws a `RangeError` with the applicable error message. + */ +function error(type: string) { + throw new RangeError(errors[type]); +} + +/** + * A simple `Array#map`-like wrapper to work with domain name strings or email + * addresses. + * + * @param domain The domain name or email address. + * @param callback The function that gets called for every + * character. + * @return A new string of characters returned by the callback + * function. + */ +function mapDomain(str: string, fn: (label: string) => string) { + const parts = str.split("@"); + let result = ""; + + if (parts.length > 1) { + // In email addresses, only the domain name should be punycoded. Leave + // the local part (i.e. everything up to `@`) intact. + result = parts[0] + "@"; + str = parts[1]; + } + + // Avoid `split(regex)` for IE8 compatibility. See #17. + str = str.replace(regexSeparators, "\x2E"); + const labels = str.split("."); + const encoded = labels.map(fn).join("."); + + return result + encoded; +} + +/** + * Creates an array containing the numeric code points of each Unicode + * character in the string. While JavaScript uses UCS-2 internally, + * this function will convert a pair of surrogate halves (each of which + * UCS-2 exposes as separate characters) into a single code point, + * matching UTF-16. + * + * @param str The Unicode input string (UCS-2). + * @return The new array of code points. + */ +function ucs2decode(str: string) { + const output = []; + let counter = 0; + const length = str.length; + + while (counter < length) { + const value = str.charCodeAt(counter++); + + if (value >= 0xD800 && value <= 0xDBFF && counter < length) { + // It's a high surrogate, and there is a next character. + const extra = str.charCodeAt(counter++); + + if ((extra & 0xFC00) == 0xDC00) { // Low surrogate. + output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); + } else { + // It's an unmatched surrogate; only append this code unit, in case the + // next code unit is the high surrogate of a surrogate pair. + output.push(value); + counter--; + } + } else { + output.push(value); + } + } + + return output; +} + +/** + * Creates a string based on an array of numeric code points. + * @see `punycode.ucs2.decode` + * @memberOf punycode.ucs2 + * @name encode + * @param codePoints The array of numeric code points. + * @returns The new Unicode string (UCS-2). + */ +function ucs2encode(array: number[]) { + return String.fromCodePoint(...array); +} + +export const ucs2 = { + decode: ucs2decode, + encode: ucs2encode, +}; + +/** + * Converts a basic code point into a digit/integer. + * @see `digitToBasic()` + * @private + * @param codePoint The basic numeric code point value. + * @returns The numeric value of a basic code point (for use in + * representing integers) in the range `0` to `base - 1`, or `base` if + * the code point does not represent a value. + */ +function basicToDigit(codePoint: number) { + if (codePoint - 0x30 < 0x0A) { + return codePoint - 0x16; + } + if (codePoint - 0x41 < 0x1A) { + return codePoint - 0x41; + } + if (codePoint - 0x61 < 0x1A) { + return codePoint - 0x61; + } + return base; +} + +/** + * Converts a digit/integer into a basic code point. + * + * @param digit The numeric value of a basic code point. + * @return The basic code point whose value (when used for + * representing integers) is `digit`, which needs to be in the range + * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is + * used; else, the lowercase form is used. The behavior is undefined + * if `flag` is non-zero and `digit` has no uppercase form. + */ +function digitToBasic(digit: number, flag: number) { + // 0..25 map to ASCII a..z or A..Z + // 26..35 map to ASCII 0..9 + return digit + 22 + 75 * Number(digit < 26) - (Number(flag != 0) << 5); +} + +/** + * Bias adaptation function as per section 3.4 of RFC 3492. + * https://tools.ietf.org/html/rfc3492#section-3.4 + */ +function adapt(delta: number, numPoints: number, firstTime: boolean) { + let k = 0; + delta = firstTime ? Math.floor(delta / damp) : delta >> 1; + delta += Math.floor(delta / numPoints); + + for (; /* no initialization */ delta > baseMinusTMin * tMax >> 1; k += base) { + delta = Math.floor(delta / baseMinusTMin); + } + + return Math.floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); +} + +/** + * Converts a Punycode string of ASCII-only symbols to a string of Unicode + * symbols. + * @memberOf punycode + * @param input The Punycode string of ASCII-only symbols. + * @returns The resulting string of Unicode symbols. + */ +export function decode(input: string): string { + // Don't use UCS-2. + const output = []; + const inputLength = input.length; + let i = 0; + let n = initialN; + let bias = initialBias; + + // Handle the basic code points: let `basic` be the number of input code + // points before the last delimiter, or `0` if there is none, then copy + // the first basic code points to the output. + + let basic = input.lastIndexOf(delimiter); + if (basic < 0) { + basic = 0; + } + + for (let j = 0; j < basic; ++j) { + // if it's not a basic code point + if (input.charCodeAt(j) >= 0x80) { + error("not-basic"); + } + output.push(input.charCodeAt(j)); + } + + // Main decoding loop: start just after the last delimiter if any basic code + // points were copied; start at the beginning otherwise. + + for ( + let index = basic > 0 ? basic + 1 : 0; + index < inputLength; + /* no final expression */ + ) { + // `index` is the index of the next character to be consumed. + // Decode a generalized variable-length integer into `delta`, + // which gets added to `i`. The overflow checking is easier + // if we increase `i` as we go, then subtract off its starting + // value at the end to obtain `delta`. + const oldi = i; + for (let w = 1, k = base;; /* no condition */ k += base) { + if (index >= inputLength) { + error("invalid-input"); + } + + const digit = basicToDigit(input.charCodeAt(index++)); + + if (digit >= base || digit > floor((maxInt - i) / w)) { + error("overflow"); + } + + i += digit * w; + const t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (digit < t) { + break; + } + + const baseMinusT = base - t; + if (w > floor(maxInt / baseMinusT)) { + error("overflow"); + } + + w *= baseMinusT; + } + + const out = output.length + 1; + bias = adapt(i - oldi, out, oldi == 0); + + // `i` was supposed to wrap around from `out` to `0`, + // incrementing `n` each time, so we'll fix that now: + if (floor(i / out) > maxInt - n) { + error("overflow"); + } + + n += floor(i / out); + i %= out; + + // Insert `n` at position `i` of the output. + output.splice(i++, 0, n); + } + + return String.fromCodePoint(...output); +} + +/** + * Converts a string of Unicode symbols (e.g. a domain name label) to a + * Punycode string of ASCII-only symbols. + * + * @param str The string of Unicode symbols. + * @return The resulting Punycode string of ASCII-only symbols. + */ +export function encode(str: string) { + const output = []; + + // Convert the input in UCS-2 to an array of Unicode code points. + const input = ucs2decode(str); + + // Cache the length. + const inputLength = input.length; + + // Initialize the state. + let n = initialN; + let delta = 0; + let bias = initialBias; + + // Handle the basic code points. + for (const currentValue of input) { + if (currentValue < 0x80) { + output.push(String.fromCharCode(currentValue)); + } + } + + const basicLength = output.length; + let handledCPCount = basicLength; + + // `handledCPCount` is the number of code points that have been handled; + // `basicLength` is the number of basic code points. + + // Finish the basic string with a delimiter unless it's empty. + if (basicLength) { + output.push(delimiter); + } + + // Main encoding loop: + while (handledCPCount < inputLength) { + // All non-basic code points < n have been handled already. Find the next + // larger one: + let m = maxInt; + + for (const currentValue of input) { + if (currentValue >= n && currentValue < m) { + m = currentValue; + } + } + + // Increase `delta` enough to advance the decoder's state to , + // but guard against overflow. + const handledCPCountPlusOne = handledCPCount + 1; + + if (m - n > Math.floor((maxInt - delta) / handledCPCountPlusOne)) { + error("overflow"); + } + + delta += (m - n) * handledCPCountPlusOne; + n = m; + + for (const currentValue of input) { + if (currentValue < n && ++delta > maxInt) { + error("overflow"); + } + + if (currentValue == n) { + // Represent delta as a generalized variable-length integer. + let q = delta; + + for (let k = base;; /* no condition */ k += base) { + const t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); + + if (q < t) { + break; + } + + const qMinusT = q - t; + const baseMinusT = base - t; + + output.push( + String.fromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)), + ); + + q = Math.floor(qMinusT / baseMinusT); + } + + output.push(String.fromCharCode(digitToBasic(q, 0))); + + bias = adapt( + delta, + handledCPCountPlusOne, + handledCPCount == basicLength, + ); + + delta = 0; + ++handledCPCount; + } + } + + ++delta; + ++n; + } + + return output.join(""); +} + +/** + * Converts a Punycode string representing a domain name or an email address + * to Unicode. Only the Punycoded parts of the input will be converted, i.e. + * it doesn't matter if you call it on a string that has already been + * converted to Unicode. + * @memberOf punycode + * @param input The Punycoded domain name or email address to + * convert to Unicode. + * @returns The Unicode representation of the given Punycode + * string. + */ +export function toUnicode(input: string) { + return mapDomain(input, function (string) { + return regexPunycode.test(string) + ? decode(string.slice(4).toLowerCase()) + : string; + }); +} + +/** + * Converts a Unicode string representing a domain name or an email address to + * Punycode. Only the non-ASCII parts of the domain name will be converted, + * i.e. it doesn't matter if you call it with a domain that's already in + * ASCII. + * + * @param input The domain name or email address to convert, as a + * Unicode string. + * @return The Punycode representation of the given domain name or + * email address. + */ +export function toASCII(input: string): string { + return mapDomain(input, function (str: string) { + return regexNonASCII.test(str) ? "xn--" + encode(str) : str; + }); +} diff --git a/ext/node/polyfills/internal/net.ts b/ext/node/polyfills/internal/net.ts new file mode 100644 index 0000000000..2e3a92dfd4 --- /dev/null +++ b/ext/node/polyfills/internal/net.ts @@ -0,0 +1,95 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { uvException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { writeBuffer } from "internal:deno_node/polyfills/internal_binding/node_file.ts"; + +// IPv4 Segment +const v4Seg = "(?:[0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])"; +const v4Str = `(${v4Seg}[.]){3}${v4Seg}`; +const IPv4Reg = new RegExp(`^${v4Str}$`); + +// IPv6 Segment +const v6Seg = "(?:[0-9a-fA-F]{1,4})"; +const IPv6Reg = new RegExp( + "^(" + + `(?:${v6Seg}:){7}(?:${v6Seg}|:)|` + + `(?:${v6Seg}:){6}(?:${v4Str}|:${v6Seg}|:)|` + + `(?:${v6Seg}:){5}(?::${v4Str}|(:${v6Seg}){1,2}|:)|` + + `(?:${v6Seg}:){4}(?:(:${v6Seg}){0,1}:${v4Str}|(:${v6Seg}){1,3}|:)|` + + `(?:${v6Seg}:){3}(?:(:${v6Seg}){0,2}:${v4Str}|(:${v6Seg}){1,4}|:)|` + + `(?:${v6Seg}:){2}(?:(:${v6Seg}){0,3}:${v4Str}|(:${v6Seg}){1,5}|:)|` + + `(?:${v6Seg}:){1}(?:(:${v6Seg}){0,4}:${v4Str}|(:${v6Seg}){1,6}|:)|` + + `(?::((?::${v6Seg}){0,5}:${v4Str}|(?::${v6Seg}){1,7}|:))` + + ")(%[0-9a-zA-Z-.:]{1,})?$", +); + +export function isIPv4(ip: string) { + return RegExp.prototype.test.call(IPv4Reg, ip); +} + +export function isIPv6(ip: string) { + return RegExp.prototype.test.call(IPv6Reg, ip); +} + +export function isIP(ip: string) { + if (isIPv4(ip)) { + return 4; + } + if (isIPv6(ip)) { + return 6; + } + + return 0; +} + +export function makeSyncWrite(fd: number) { + return function ( + // deno-lint-ignore no-explicit-any + this: any, + // deno-lint-ignore no-explicit-any + chunk: any, + enc: string, + cb: (err?: Error) => void, + ) { + if (enc !== "buffer") { + chunk = Buffer.from(chunk, enc); + } + + this._handle.bytesWritten += chunk.length; + + const ctx: { errno?: number } = {}; + writeBuffer(fd, chunk, 0, chunk.length, null, ctx); + + if (ctx.errno !== undefined) { + const ex = uvException(ctx); + ex.errno = ctx.errno; + + return cb(ex); + } + + cb(); + }; +} + +export const normalizedArgsSymbol = Symbol("normalizedArgs"); diff --git a/ext/node/polyfills/internal/normalize_encoding.mjs b/ext/node/polyfills/internal/normalize_encoding.mjs new file mode 100644 index 0000000000..788c86c56d --- /dev/null +++ b/ext/node/polyfills/internal/normalize_encoding.mjs @@ -0,0 +1,72 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export function normalizeEncoding(enc) { + if (enc == null || enc === "utf8" || enc === "utf-8") return "utf8"; + return slowCases(enc); +} + +export function slowCases(enc) { + switch (enc.length) { + case 4: + if (enc === "UTF8") return "utf8"; + if (enc === "ucs2" || enc === "UCS2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf8") return "utf8"; + if (enc === "ucs2") return "utf16le"; + break; + case 3: + if ( + enc === "hex" || enc === "HEX" || + `${enc}`.toLowerCase() === "hex" + ) { + return "hex"; + } + break; + case 5: + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + if (enc === "UTF-8") return "utf8"; + if (enc === "ASCII") return "ascii"; + if (enc === "UCS-2") return "utf16le"; + enc = `${enc}`.toLowerCase(); + if (enc === "utf-8") return "utf8"; + if (enc === "ascii") return "ascii"; + if (enc === "ucs-2") return "utf16le"; + break; + case 6: + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + if (enc === "BASE64") return "base64"; + if (enc === "LATIN1" || enc === "BINARY") return "latin1"; + enc = `${enc}`.toLowerCase(); + if (enc === "base64") return "base64"; + if (enc === "latin1" || enc === "binary") return "latin1"; + break; + case 7: + if ( + enc === "utf16le" || enc === "UTF16LE" || + `${enc}`.toLowerCase() === "utf16le" + ) { + return "utf16le"; + } + break; + case 8: + if ( + enc === "utf-16le" || enc === "UTF-16LE" || + `${enc}`.toLowerCase() === "utf-16le" + ) { + return "utf16le"; + } + break; + case 9: + if ( + enc === "base64url" || enc === "BASE64URL" || + `${enc}`.toLowerCase() === "base64url" + ) { + return "base64url"; + } + break; + default: + if (enc === "") return "utf8"; + } +} diff --git a/ext/node/polyfills/internal/options.ts b/ext/node/polyfills/internal/options.ts new file mode 100644 index 0000000000..68ccf0528c --- /dev/null +++ b/ext/node/polyfills/internal/options.ts @@ -0,0 +1,45 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { getOptions } from "internal:deno_node/polyfills/internal_binding/node_options.ts"; + +let optionsMap: Map; + +function getOptionsFromBinding() { + if (!optionsMap) { + ({ options: optionsMap } = getOptions()); + } + + return optionsMap; +} + +export function getOptionValue(optionName: string) { + const options = getOptionsFromBinding(); + + if (optionName.startsWith("--no-")) { + const option = options.get("--" + optionName.slice(5)); + + return option && !option.value; + } + + return options.get(optionName)?.value; +} diff --git a/ext/node/polyfills/internal/primordials.mjs b/ext/node/polyfills/internal/primordials.mjs new file mode 100644 index 0000000000..1639efdb50 --- /dev/null +++ b/ext/node/polyfills/internal/primordials.mjs @@ -0,0 +1,30 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export const ArrayIsArray = Array.isArray; +export const ArrayPrototypeFilter = (that, ...args) => that.filter(...args); +export const ArrayPrototypeForEach = (that, ...args) => that.forEach(...args); +export const ArrayPrototypeIncludes = (that, ...args) => that.includes(...args); +export const ArrayPrototypeJoin = (that, ...args) => that.join(...args); +export const ArrayPrototypePush = (that, ...args) => that.push(...args); +export const ArrayPrototypeSlice = (that, ...args) => that.slice(...args); +export const ArrayPrototypeSome = (that, ...args) => that.some(...args); +export const ArrayPrototypeSort = (that, ...args) => that.sort(...args); +export const ArrayPrototypeUnshift = (that, ...args) => that.unshift(...args); +export const ObjectAssign = Object.assign; +export const ObjectCreate = Object.create; +export const ObjectPrototypeHasOwnProperty = Object.hasOwn; +export const RegExpPrototypeTest = (that, ...args) => that.test(...args); +export const RegExpPrototypeExec = RegExp.prototype.exec; +export const StringFromCharCode = String.fromCharCode; +export const StringPrototypeCharCodeAt = (that, ...args) => + that.charCodeAt(...args); +export const StringPrototypeEndsWith = (that, ...args) => + that.endsWith(...args); +export const StringPrototypeIncludes = (that, ...args) => + that.includes(...args); +export const StringPrototypeReplace = (that, ...args) => that.replace(...args); +export const StringPrototypeSlice = (that, ...args) => that.slice(...args); +export const StringPrototypeSplit = (that, ...args) => that.split(...args); +export const StringPrototypeStartsWith = (that, ...args) => + that.startsWith(...args); +export const StringPrototypeToUpperCase = (that) => that.toUpperCase(); diff --git a/ext/node/polyfills/internal/process/per_thread.mjs b/ext/node/polyfills/internal/process/per_thread.mjs new file mode 100644 index 0000000000..3146083d1a --- /dev/null +++ b/ext/node/polyfills/internal/process/per_thread.mjs @@ -0,0 +1,272 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +const kInternal = Symbol("internal properties"); + +const replaceUnderscoresRegex = /_/g; +const leadingDashesRegex = /^--?/; +const trailingValuesRegex = /=.*$/; + +// This builds the initial process.allowedNodeEnvironmentFlags +// from data in the config binding. +export function buildAllowedFlags() { + const allowedNodeEnvironmentFlags = [ + "--track-heap-objects", + "--no-track-heap-objects", + "--node-snapshot", + "--no-node-snapshot", + "--require", + "--max-old-space-size", + "--trace-exit", + "--no-trace-exit", + "--disallow-code-generation-from-strings", + "--experimental-json-modules", + "--no-experimental-json-modules", + "--interpreted-frames-native-stack", + "--inspect-brk", + "--no-inspect-brk", + "--trace-tls", + "--no-trace-tls", + "--stack-trace-limit", + "--experimental-repl-await", + "--no-experimental-repl-await", + "--preserve-symlinks", + "--no-preserve-symlinks", + "--report-uncaught-exception", + "--no-report-uncaught-exception", + "--experimental-modules", + "--no-experimental-modules", + "--report-signal", + "--jitless", + "--inspect-port", + "--heapsnapshot-near-heap-limit", + "--tls-keylog", + "--force-context-aware", + "--no-force-context-aware", + "--napi-modules", + "--abort-on-uncaught-exception", + "--diagnostic-dir", + "--verify-base-objects", + "--no-verify-base-objects", + "--unhandled-rejections", + "--perf-basic-prof", + "--trace-atomics-wait", + "--no-trace-atomics-wait", + "--deprecation", + "--no-deprecation", + "--perf-basic-prof-only-functions", + "--perf-prof", + "--max-http-header-size", + "--report-on-signal", + "--no-report-on-signal", + "--throw-deprecation", + "--no-throw-deprecation", + "--warnings", + "--no-warnings", + "--force-fips", + "--no-force-fips", + "--pending-deprecation", + "--no-pending-deprecation", + "--input-type", + "--tls-max-v1.3", + "--no-tls-max-v1.3", + "--tls-min-v1.2", + "--no-tls-min-v1.2", + "--inspect", + "--no-inspect", + "--heapsnapshot-signal", + "--trace-warnings", + "--no-trace-warnings", + "--trace-event-categories", + "--experimental-worker", + "--tls-max-v1.2", + "--no-tls-max-v1.2", + "--perf-prof-unwinding-info", + "--preserve-symlinks-main", + "--no-preserve-symlinks-main", + "--policy-integrity", + "--experimental-wasm-modules", + "--no-experimental-wasm-modules", + "--node-memory-debug", + "--inspect-publish-uid", + "--tls-min-v1.3", + "--no-tls-min-v1.3", + "--experimental-specifier-resolution", + "--secure-heap", + "--tls-min-v1.0", + "--no-tls-min-v1.0", + "--redirect-warnings", + "--experimental-report", + "--trace-event-file-pattern", + "--trace-uncaught", + "--no-trace-uncaught", + "--experimental-loader", + "--http-parser", + "--dns-result-order", + "--trace-sigint", + "--no-trace-sigint", + "--secure-heap-min", + "--enable-fips", + "--no-enable-fips", + "--enable-source-maps", + "--no-enable-source-maps", + "--insecure-http-parser", + "--no-insecure-http-parser", + "--use-openssl-ca", + "--no-use-openssl-ca", + "--tls-cipher-list", + "--experimental-top-level-await", + "--no-experimental-top-level-await", + "--openssl-config", + "--icu-data-dir", + "--v8-pool-size", + "--report-on-fatalerror", + "--no-report-on-fatalerror", + "--title", + "--tls-min-v1.1", + "--no-tls-min-v1.1", + "--report-filename", + "--trace-deprecation", + "--no-trace-deprecation", + "--report-compact", + "--no-report-compact", + "--experimental-policy", + "--experimental-import-meta-resolve", + "--no-experimental-import-meta-resolve", + "--zero-fill-buffers", + "--no-zero-fill-buffers", + "--report-dir", + "--use-bundled-ca", + "--no-use-bundled-ca", + "--experimental-vm-modules", + "--no-experimental-vm-modules", + "--force-async-hooks-checks", + "--no-force-async-hooks-checks", + "--frozen-intrinsics", + "--no-frozen-intrinsics", + "--huge-max-old-generation-size", + "--disable-proto", + "--debug-arraybuffer-allocations", + "--no-debug-arraybuffer-allocations", + "--conditions", + "--experimental-wasi-unstable-preview1", + "--no-experimental-wasi-unstable-preview1", + "--trace-sync-io", + "--no-trace-sync-io", + "--use-largepages", + "--experimental-abortcontroller", + "--debug-port", + "--es-module-specifier-resolution", + "--prof-process", + "-C", + "--loader", + "--report-directory", + "-r", + "--trace-events-enabled", + ]; + + /* + function isAccepted(to) { + if (!to.startsWith("-") || to === "--") return true; + const recursiveExpansion = aliases.get(to); + if (recursiveExpansion) { + if (recursiveExpansion[0] === to) { + recursiveExpansion.splice(0, 1); + } + return recursiveExpansion.every(isAccepted); + } + return options.get(to).envVarSettings === kAllowedInEnvironment; + } + for (const { 0: from, 1: expansion } of aliases) { + if (expansion.every(isAccepted)) { + let canonical = from; + if (canonical.endsWith("=")) { + canonical = canonical.slice(0, canonical.length - 1); + } + if (canonical.endsWith(" ")) { + canonical = canonical.slice(0, canonical.length - 4); + } + allowedNodeEnvironmentFlags.push(canonical); + } + } + */ + + const trimLeadingDashes = (flag) => flag.replace(leadingDashesRegex, ""); + + // Save these for comparison against flags provided to + // process.allowedNodeEnvironmentFlags.has() which lack leading dashes. + const nodeFlags = allowedNodeEnvironmentFlags.map(trimLeadingDashes); + + class NodeEnvironmentFlagsSet extends Set { + constructor(array) { + super(); + this[kInternal] = { array }; + } + + add() { + // No-op, `Set` API compatible + return this; + } + + delete() { + // No-op, `Set` API compatible + return false; + } + + clear() { + // No-op, `Set` API compatible + } + + has(key) { + // This will return `true` based on various possible + // permutations of a flag, including present/missing leading + // dash(es) and/or underscores-for-dashes. + // Strips any values after `=`, inclusive. + // TODO(addaleax): It might be more flexible to run the option parser + // on a dummy option set and see whether it rejects the argument or + // not. + if (typeof key === "string") { + key = key.replace(replaceUnderscoresRegex, "-"); + if (leadingDashesRegex.test(key)) { + key = key.replace(trailingValuesRegex, ""); + return this[kInternal].array.includes(key); + } + return nodeFlags.includes(key); + } + return false; + } + + entries() { + this[kInternal].set ??= new Set(this[kInternal].array); + return this[kInternal].set.entries(); + } + + forEach(callback, thisArg = undefined) { + this[kInternal].array.forEach((v) => + Reflect.apply(callback, thisArg, [v, v, this]) + ); + } + + get size() { + return this[kInternal].array.length; + } + + values() { + this[kInternal].set ??= new Set(this[kInternal].array); + return this[kInternal].set.values(); + } + } + NodeEnvironmentFlagsSet.prototype.keys = + NodeEnvironmentFlagsSet + .prototype[Symbol.iterator] = + NodeEnvironmentFlagsSet.prototype.values; + + Object.freeze(NodeEnvironmentFlagsSet.prototype.constructor); + Object.freeze(NodeEnvironmentFlagsSet.prototype); + + return Object.freeze( + new NodeEnvironmentFlagsSet( + allowedNodeEnvironmentFlags, + ), + ); +} diff --git a/ext/node/polyfills/internal/querystring.ts b/ext/node/polyfills/internal/querystring.ts new file mode 100644 index 0000000000..c00803afe8 --- /dev/null +++ b/ext/node/polyfills/internal/querystring.ts @@ -0,0 +1,92 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { ERR_INVALID_URI } from "internal:deno_node/polyfills/internal/errors.ts"; + +export const hexTable = new Array(256); +for (let i = 0; i < 256; ++i) { + hexTable[i] = "%" + ((i < 16 ? "0" : "") + i.toString(16)).toUpperCase(); +} + +// deno-fmt-ignore +export const isHexTable = new Int8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64 - 79 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 - 95 + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96 - 111 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112 - 127 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 ... + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ... 256 +]); + +export function encodeStr( + str: string, + noEscapeTable: Int8Array, + hexTable: string[], +): string { + const len = str.length; + if (len === 0) return ""; + + let out = ""; + let lastPos = 0; + + for (let i = 0; i < len; i++) { + let c = str.charCodeAt(i); + // ASCII + if (c < 0x80) { + if (noEscapeTable[c] === 1) continue; + if (lastPos < i) out += str.slice(lastPos, i); + lastPos = i + 1; + out += hexTable[c]; + continue; + } + + if (lastPos < i) out += str.slice(lastPos, i); + + // Multi-byte characters ... + if (c < 0x800) { + lastPos = i + 1; + out += hexTable[0xc0 | (c >> 6)] + hexTable[0x80 | (c & 0x3f)]; + continue; + } + if (c < 0xd800 || c >= 0xe000) { + lastPos = i + 1; + out += hexTable[0xe0 | (c >> 12)] + + hexTable[0x80 | ((c >> 6) & 0x3f)] + + hexTable[0x80 | (c & 0x3f)]; + continue; + } + // Surrogate pair + ++i; + + // This branch should never happen because all URLSearchParams entries + // should already be converted to USVString. But, included for + // completion's sake anyway. + if (i >= len) throw new ERR_INVALID_URI(); + + const c2 = str.charCodeAt(i) & 0x3ff; + + lastPos = i + 1; + c = 0x10000 + (((c & 0x3ff) << 10) | c2); + out += hexTable[0xf0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3f)] + + hexTable[0x80 | ((c >> 6) & 0x3f)] + + hexTable[0x80 | (c & 0x3f)]; + } + if (lastPos === 0) return str; + if (lastPos < len) return out + str.slice(lastPos); + return out; +} + +export default { + hexTable, + encodeStr, + isHexTable, +}; diff --git a/ext/node/polyfills/internal/readline/callbacks.mjs b/ext/node/polyfills/internal/readline/callbacks.mjs new file mode 100644 index 0000000000..3be88c8998 --- /dev/null +++ b/ext/node/polyfills/internal/readline/callbacks.mjs @@ -0,0 +1,137 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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"; + +import { ERR_INVALID_ARG_VALUE, ERR_INVALID_CURSOR_POS } from "internal:deno_node/polyfills/internal/errors.ts"; + +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; + +import { CSI } from "internal:deno_node/polyfills/internal/readline/utils.mjs"; + +const { + kClearLine, + kClearScreenDown, + kClearToLineBeginning, + kClearToLineEnd, +} = CSI; + +/** + * moves the cursor to the x and y coordinate on the given stream + */ + +export function cursorTo(stream, x, y, callback) { + if (callback !== undefined) { + validateFunction(callback, "callback"); + } + + if (typeof y === "function") { + callback = y; + y = undefined; + } + + if (Number.isNaN(x)) throw new ERR_INVALID_ARG_VALUE("x", x); + if (Number.isNaN(y)) throw new ERR_INVALID_ARG_VALUE("y", y); + + if (stream == null || (typeof x !== "number" && typeof y !== "number")) { + if (typeof callback === "function") process.nextTick(callback, null); + return true; + } + + if (typeof x !== "number") throw new ERR_INVALID_CURSOR_POS(); + + const data = typeof y !== "number" ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; + return stream.write(data, callback); +} + +/** + * moves the cursor relative to its current location + */ + +export function moveCursor(stream, dx, dy, callback) { + if (callback !== undefined) { + validateFunction(callback, "callback"); + } + + if (stream == null || !(dx || dy)) { + if (typeof callback === "function") process.nextTick(callback, null); + return true; + } + + let data = ""; + + if (dx < 0) { + data += CSI`${-dx}D`; + } else if (dx > 0) { + data += CSI`${dx}C`; + } + + if (dy < 0) { + data += CSI`${-dy}A`; + } else if (dy > 0) { + data += CSI`${dy}B`; + } + + return stream.write(data, callback); +} + +/** + * clears the current line the cursor is on: + * -1 for left of the cursor + * +1 for right of the cursor + * 0 for the entire line + */ + +export function clearLine(stream, dir, callback) { + if (callback !== undefined) { + validateFunction(callback, "callback"); + } + + if (stream === null || stream === undefined) { + if (typeof callback === "function") process.nextTick(callback, null); + return true; + } + + const type = dir < 0 + ? kClearToLineBeginning + : dir > 0 + ? kClearToLineEnd + : kClearLine; + return stream.write(type, callback); +} + +/** + * clears the screen from the current position of the cursor down + */ + +export function clearScreenDown(stream, callback) { + if (callback !== undefined) { + validateFunction(callback, "callback"); + } + + if (stream === null || stream === undefined) { + if (typeof callback === "function") process.nextTick(callback, null); + return true; + } + + return stream.write(kClearScreenDown, callback); +} diff --git a/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs b/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs new file mode 100644 index 0000000000..7f68dac475 --- /dev/null +++ b/ext/node/polyfills/internal/readline/emitKeypressEvents.mjs @@ -0,0 +1,106 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { charLengthAt, CSI, emitKeys } from "internal:deno_node/polyfills/internal/readline/utils.mjs"; +import { kSawKeyPress } from "internal:deno_node/polyfills/internal/readline/symbols.mjs"; +import { clearTimeout, setTimeout } from "internal:deno_node/polyfills/timers.ts"; + +const { + kEscape, +} = CSI; + +import { StringDecoder } from "internal:deno_node/polyfills/string_decoder.ts"; + +const KEYPRESS_DECODER = Symbol("keypress-decoder"); +const ESCAPE_DECODER = Symbol("escape-decoder"); + +// GNU readline library - keyseq-timeout is 500ms (default) +const ESCAPE_CODE_TIMEOUT = 500; + +/** + * accepts a readable Stream instance and makes it emit "keypress" events + */ + +export function emitKeypressEvents(stream, iface = {}) { + if (stream[KEYPRESS_DECODER]) return; + + stream[KEYPRESS_DECODER] = new StringDecoder("utf8"); + + stream[ESCAPE_DECODER] = emitKeys(stream); + stream[ESCAPE_DECODER].next(); + + const triggerEscape = () => stream[ESCAPE_DECODER].next(""); + const { escapeCodeTimeout = ESCAPE_CODE_TIMEOUT } = iface; + let timeoutId; + + function onData(input) { + if (stream.listenerCount("keypress") > 0) { + const string = stream[KEYPRESS_DECODER].write(input); + if (string) { + clearTimeout(timeoutId); + + // This supports characters of length 2. + iface[kSawKeyPress] = charLengthAt(string, 0) === string.length; + iface.isCompletionEnabled = false; + + let length = 0; + for (const character of string[Symbol.iterator]()) { + length += character.length; + if (length === string.length) { + iface.isCompletionEnabled = true; + } + + try { + stream[ESCAPE_DECODER].next(character); + // Escape letter at the tail position + if (length === string.length && character === kEscape) { + timeoutId = setTimeout(triggerEscape, escapeCodeTimeout); + } + } catch (err) { + // If the generator throws (it could happen in the `keypress` + // event), we need to restart it. + stream[ESCAPE_DECODER] = emitKeys(stream); + stream[ESCAPE_DECODER].next(); + throw err; + } + } + } + } else { + // Nobody's watching anyway + stream.removeListener("data", onData); + stream.on("newListener", onNewListener); + } + } + + function onNewListener(event) { + if (event === "keypress") { + stream.on("data", onData); + stream.removeListener("newListener", onNewListener); + } + } + + if (stream.listenerCount("keypress") > 0) { + stream.on("data", onData); + } else { + stream.on("newListener", onNewListener); + } +} diff --git a/ext/node/polyfills/internal/readline/interface.mjs b/ext/node/polyfills/internal/readline/interface.mjs new file mode 100644 index 0000000000..41d05fbf26 --- /dev/null +++ b/ext/node/polyfills/internal/readline/interface.mjs @@ -0,0 +1,1223 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// deno-lint-ignore-file camelcase no-inner-declarations no-this-alias + +import { ERR_INVALID_ARG_VALUE, ERR_USE_AFTER_CLOSE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + validateAbortSignal, + validateArray, + validateString, + validateUint32, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + // inspect, + getStringWidth, + stripVTControlCharacters, +} from "internal:deno_node/polyfills/internal/util/inspect.mjs"; +import EventEmitter from "internal:deno_node/polyfills/events.ts"; +import { emitKeypressEvents } from "internal:deno_node/polyfills/internal/readline/emitKeypressEvents.mjs"; +import { + charLengthAt, + charLengthLeft, + commonPrefix, + kSubstringSearch, +} from "internal:deno_node/polyfills/internal/readline/utils.mjs"; +import { clearScreenDown, cursorTo, moveCursor } from "internal:deno_node/polyfills/internal/readline/callbacks.mjs"; +import { Readable } from "internal:deno_node/polyfills/_stream.mjs"; + +import { StringDecoder } from "internal:deno_node/polyfills/string_decoder.ts"; +import { + kAddHistory, + kDecoder, + kDeleteLeft, + kDeleteLineLeft, + kDeleteLineRight, + kDeleteRight, + kDeleteWordLeft, + kDeleteWordRight, + kGetDisplayPos, + kHistoryNext, + kHistoryPrev, + kInsertString, + kLine, + kLine_buffer, + kMoveCursor, + kNormalWrite, + kOldPrompt, + kOnLine, + kPreviousKey, + kPrompt, + kQuestionCallback, + kRefreshLine, + kSawKeyPress, + kSawReturnAt, + kSetRawMode, + kTabComplete, + kTabCompleter, + kTtyWrite, + kWordLeft, + kWordRight, + kWriteToOutput, +} from "internal:deno_node/polyfills/internal/readline/symbols.mjs"; + +const kHistorySize = 30; +const kMincrlfDelay = 100; +// \r\n, \n, or \r followed by something other than \n +const lineEnding = /\r?\n|\r(?!\n)/; + +const kLineObjectStream = Symbol("line object stream"); +export const kQuestionCancel = Symbol("kQuestionCancel"); +export const kQuestion = Symbol("kQuestion"); + +// GNU readline library - keyseq-timeout is 500ms (default) +const ESCAPE_CODE_TIMEOUT = 500; + +export { + kAddHistory, + kDecoder, + kDeleteLeft, + kDeleteLineLeft, + kDeleteLineRight, + kDeleteRight, + kDeleteWordLeft, + kDeleteWordRight, + kGetDisplayPos, + kHistoryNext, + kHistoryPrev, + kInsertString, + kLine, + kLine_buffer, + kMoveCursor, + kNormalWrite, + kOldPrompt, + kOnLine, + kPreviousKey, + kPrompt, + kQuestionCallback, + kRefreshLine, + kSawKeyPress, + kSawReturnAt, + kSetRawMode, + kTabComplete, + kTabCompleter, + kTtyWrite, + kWordLeft, + kWordRight, + kWriteToOutput, +}; + +export function InterfaceConstructor(input, output, completer, terminal) { + this[kSawReturnAt] = 0; + // TODO(BridgeAR): Document this property. The name is not ideal, so we + // might want to expose an alias and document that instead. + this.isCompletionEnabled = true; + this[kSawKeyPress] = false; + this[kPreviousKey] = null; + this.escapeCodeTimeout = ESCAPE_CODE_TIMEOUT; + this.tabSize = 8; + Function.prototype.call(EventEmitter, this); + + let history; + let historySize; + let removeHistoryDuplicates = false; + let crlfDelay; + let prompt = "> "; + let signal; + + if (input?.input) { + // An options object was given + output = input.output; + completer = input.completer; + terminal = input.terminal; + history = input.history; + historySize = input.historySize; + signal = input.signal; + if (input.tabSize !== undefined) { + validateUint32(input.tabSize, "tabSize", true); + this.tabSize = input.tabSize; + } + removeHistoryDuplicates = input.removeHistoryDuplicates; + if (input.prompt !== undefined) { + prompt = input.prompt; + } + if (input.escapeCodeTimeout !== undefined) { + if (Number.isFinite(input.escapeCodeTimeout)) { + this.escapeCodeTimeout = input.escapeCodeTimeout; + } else { + throw new ERR_INVALID_ARG_VALUE( + "input.escapeCodeTimeout", + this.escapeCodeTimeout, + ); + } + } + + if (signal) { + validateAbortSignal(signal, "options.signal"); + } + + crlfDelay = input.crlfDelay; + input = input.input; + } + + if (completer !== undefined && typeof completer !== "function") { + throw new ERR_INVALID_ARG_VALUE("completer", completer); + } + + if (history === undefined) { + history = []; + } else { + validateArray(history, "history"); + } + + if (historySize === undefined) { + historySize = kHistorySize; + } + + if ( + typeof historySize !== "number" || + Number.isNaN(historySize) || + historySize < 0 + ) { + throw new ERR_INVALID_ARG_VALUE.RangeError("historySize", historySize); + } + + // Backwards compat; check the isTTY prop of the output stream + // when `terminal` was not specified + if (terminal === undefined && !(output === null || output === undefined)) { + terminal = !!output.isTTY; + } + + const self = this; + + this.line = ""; + this[kSubstringSearch] = null; + this.output = output; + this.input = input; + this.history = history; + this.historySize = historySize; + this.removeHistoryDuplicates = !!removeHistoryDuplicates; + this.crlfDelay = crlfDelay + ? Math.max(kMincrlfDelay, crlfDelay) + : kMincrlfDelay; + this.completer = completer; + + this.setPrompt(prompt); + + this.terminal = !!terminal; + + function onerror(err) { + self.emit("error", err); + } + + function ondata(data) { + self[kNormalWrite](data); + } + + function onend() { + if ( + typeof self[kLine_buffer] === "string" && + self[kLine_buffer].length > 0 + ) { + self.emit("line", self[kLine_buffer]); + } + self.close(); + } + + function ontermend() { + if (typeof self.line === "string" && self.line.length > 0) { + self.emit("line", self.line); + } + self.close(); + } + + function onkeypress(s, key) { + self[kTtyWrite](s, key); + if (key && key.sequence) { + // If the key.sequence is half of a surrogate pair + // (>= 0xd800 and <= 0xdfff), refresh the line so + // the character is displayed appropriately. + const ch = key.sequence.codePointAt(0); + if (ch >= 0xd800 && ch <= 0xdfff) self[kRefreshLine](); + } + } + + function onresize() { + self[kRefreshLine](); + } + + this[kLineObjectStream] = undefined; + + input.on("error", onerror); + + if (!this.terminal) { + function onSelfCloseWithoutTerminal() { + input.removeListener("data", ondata); + input.removeListener("error", onerror); + input.removeListener("end", onend); + } + + input.on("data", ondata); + input.on("end", onend); + self.once("close", onSelfCloseWithoutTerminal); + this[kDecoder] = new StringDecoder("utf8"); + } else { + function onSelfCloseWithTerminal() { + input.removeListener("keypress", onkeypress); + input.removeListener("error", onerror); + input.removeListener("end", ontermend); + if (output !== null && output !== undefined) { + output.removeListener("resize", onresize); + } + } + + emitKeypressEvents(input, this); + + // `input` usually refers to stdin + input.on("keypress", onkeypress); + input.on("end", ontermend); + + this[kSetRawMode](true); + this.terminal = true; + + // Cursor position on the line. + this.cursor = 0; + + this.historyIndex = -1; + + if (output !== null && output !== undefined) { + output.on("resize", onresize); + } + + self.once("close", onSelfCloseWithTerminal); + } + + if (signal) { + const onAborted = () => self.close(); + if (signal.aborted) { + process.nextTick(onAborted); + } else { + signal.addEventListener("abort", onAborted, { once: true }); + self.once("close", () => signal.removeEventListener("abort", onAborted)); + } + } + + // Current line + this.line = ""; + + input.resume(); +} + +Object.setPrototypeOf(InterfaceConstructor.prototype, EventEmitter.prototype); +Object.setPrototypeOf(InterfaceConstructor, EventEmitter); + +export class Interface extends InterfaceConstructor { + // eslint-disable-next-line no-useless-constructor + constructor(input, output, completer, terminal) { + super(input, output, completer, terminal); + } + get columns() { + if (this.output && this.output.columns) return this.output.columns; + return Infinity; + } + + /** + * Sets the prompt written to the output. + * @param {string} prompt + * @returns {void} + */ + setPrompt(prompt) { + this[kPrompt] = prompt; + } + + /** + * Returns the current prompt used by `rl.prompt()`. + * @returns {string} + */ + getPrompt() { + return this[kPrompt]; + } + + [kSetRawMode](mode) { + const wasInRawMode = this.input.isRaw; + + if (typeof this.input.setRawMode === "function") { + this.input.setRawMode(mode); + } + + return wasInRawMode; + } + + /** + * Writes the configured `prompt` to a new line in `output`. + * @param {boolean} [preserveCursor] + * @returns {void} + */ + prompt(preserveCursor) { + if (this.paused) this.resume(); + if (this.terminal && process.env.TERM !== "dumb") { + if (!preserveCursor) this.cursor = 0; + this[kRefreshLine](); + } else { + this[kWriteToOutput](this[kPrompt]); + } + } + + [kQuestion](query, cb) { + if (this.closed) { + throw new ERR_USE_AFTER_CLOSE("readline"); + } + if (this[kQuestionCallback]) { + this.prompt(); + } else { + this[kOldPrompt] = this[kPrompt]; + this.setPrompt(query); + this[kQuestionCallback] = cb; + this.prompt(); + } + } + + [kOnLine](line) { + if (this[kQuestionCallback]) { + const cb = this[kQuestionCallback]; + this[kQuestionCallback] = null; + this.setPrompt(this[kOldPrompt]); + cb(line); + } else { + this.emit("line", line); + } + } + + [kQuestionCancel]() { + if (this[kQuestionCallback]) { + this[kQuestionCallback] = null; + this.setPrompt(this[kOldPrompt]); + this.clearLine(); + } + } + + [kWriteToOutput](stringToWrite) { + validateString(stringToWrite, "stringToWrite"); + + if (this.output !== null && this.output !== undefined) { + this.output.write(stringToWrite); + } + } + + [kAddHistory]() { + if (this.line.length === 0) return ""; + + // If the history is disabled then return the line + if (this.historySize === 0) return this.line; + + // If the trimmed line is empty then return the line + if (this.line.trim().length === 0) return this.line; + + if (this.history.length === 0 || this.history[0] !== this.line) { + if (this.removeHistoryDuplicates) { + // Remove older history line if identical to new one + const dupIndex = this.history.indexOf(this.line); + if (dupIndex !== -1) this.history.splice(dupIndex, 1); + } + + this.history.unshift(this.line); + + // Only store so many + if (this.history.length > this.historySize) { + this.history.pop(); + } + } + + this.historyIndex = -1; + + // The listener could change the history object, possibly + // to remove the last added entry if it is sensitive and should + // not be persisted in the history, like a password + const line = this.history[0]; + + // Emit history event to notify listeners of update + this.emit("history", this.history); + + return line; + } + + [kRefreshLine]() { + // line length + const line = this[kPrompt] + this.line; + const dispPos = this[kGetDisplayPos](line); + const lineCols = dispPos.cols; + const lineRows = dispPos.rows; + + // cursor position + const cursorPos = this.getCursorPos(); + + // First move to the bottom of the current line, based on cursor pos + const prevRows = this.prevRows || 0; + if (prevRows > 0) { + moveCursor(this.output, 0, -prevRows); + } + + // Cursor to left edge. + cursorTo(this.output, 0); + // erase data + clearScreenDown(this.output); + + // Write the prompt and the current buffer content. + this[kWriteToOutput](line); + + // Force terminal to allocate a new line + if (lineCols === 0) { + this[kWriteToOutput](" "); + } + + // Move cursor to original position. + cursorTo(this.output, cursorPos.cols); + + const diff = lineRows - cursorPos.rows; + if (diff > 0) { + moveCursor(this.output, 0, -diff); + } + + this.prevRows = cursorPos.rows; + } + + /** + * Closes the `readline.Interface` instance. + * @returns {void} + */ + close() { + if (this.closed) return; + this.pause(); + if (this.terminal) { + this[kSetRawMode](false); + } + this.closed = true; + this.emit("close"); + } + + /** + * Pauses the `input` stream. + * @returns {void | Interface} + */ + pause() { + if (this.paused) return; + this.input.pause(); + this.paused = true; + this.emit("pause"); + return this; + } + + /** + * Resumes the `input` stream if paused. + * @returns {void | Interface} + */ + resume() { + if (!this.paused) return; + this.input.resume(); + this.paused = false; + this.emit("resume"); + return this; + } + + /** + * Writes either `data` or a `key` sequence identified by + * `key` to the `output`. + * @param {string} d + * @param {{ + * ctrl?: boolean; + * meta?: boolean; + * shift?: boolean; + * name?: string; + * }} [key] + * @returns {void} + */ + write(d, key) { + if (this.paused) this.resume(); + if (this.terminal) { + this[kTtyWrite](d, key); + } else { + this[kNormalWrite](d); + } + } + + [kNormalWrite](b) { + if (b === undefined) { + return; + } + let string = this[kDecoder].write(b); + if ( + this[kSawReturnAt] && + Date.now() - this[kSawReturnAt] <= this.crlfDelay + ) { + string = string.replace(/^\n/, ""); + this[kSawReturnAt] = 0; + } + + // Run test() on the new string chunk, not on the entire line buffer. + const newPartContainsEnding = lineEnding.test(string); + + if (this[kLine_buffer]) { + string = this[kLine_buffer] + string; + this[kLine_buffer] = null; + } + if (newPartContainsEnding) { + this[kSawReturnAt] = string.endsWith("\r") ? Date.now() : 0; + + // Got one or more newlines; process into "line" events + const lines = string.split(lineEnding); + // Either '' or (conceivably) the unfinished portion of the next line + string = lines.pop(); + this[kLine_buffer] = string; + for (let n = 0; n < lines.length; n++) this[kOnLine](lines[n]); + } else if (string) { + // No newlines this time, save what we have for next time + this[kLine_buffer] = string; + } + } + + [kInsertString](c) { + if (this.cursor < this.line.length) { + const beg = this.line.slice(0, this.cursor); + const end = this.line.slice( + this.cursor, + this.line.length, + ); + this.line = beg + c + end; + this.cursor += c.length; + this[kRefreshLine](); + } else { + this.line += c; + this.cursor += c.length; + + if (this.getCursorPos().cols === 0) { + this[kRefreshLine](); + } else { + this[kWriteToOutput](c); + } + } + } + + async [kTabComplete](lastKeypressWasTab) { + this.pause(); + const string = this.line.slice(0, this.cursor); + let value; + try { + value = await this.completer(string); + } catch (err) { + // TODO(bartlomieju): inspect is not ported yet + // this[kWriteToOutput](`Tab completion error: ${inspect(err)}`); + this[kWriteToOutput](`Tab completion error: ${err}`); + return; + } finally { + this.resume(); + } + this[kTabCompleter](lastKeypressWasTab, value); + } + + [kTabCompleter](lastKeypressWasTab, { 0: completions, 1: completeOn }) { + // Result and the text that was completed. + + if (!completions || completions.length === 0) { + return; + } + + // If there is a common prefix to all matches, then apply that portion. + const prefix = commonPrefix( + completions.filter((e) => e !== ""), + ); + if ( + prefix.startsWith(completeOn) && + prefix.length > completeOn.length + ) { + this[kInsertString](prefix.slice(completeOn.length)); + return; + } else if (!completeOn.startsWith(prefix)) { + this.line = this.line.slice(0, this.cursor - completeOn.length) + + prefix + + this.line.slice(this.cursor, this.line.length); + this.cursor = this.cursor - completeOn.length + prefix.length; + this._refreshLine(); + return; + } + + if (!lastKeypressWasTab) { + return; + } + + // Apply/show completions. + const completionsWidth = completions.map( + (e) => getStringWidth(e), + ); + const width = Math.max.apply(completionsWidth) + 2; // 2 space padding + let maxColumns = Math.floor(this.columns / width) || 1; + if (maxColumns === Infinity) { + maxColumns = 1; + } + let output = "\r\n"; + let lineIndex = 0; + let whitespace = 0; + for (let i = 0; i < completions.length; i++) { + const completion = completions[i]; + if (completion === "" || lineIndex === maxColumns) { + output += "\r\n"; + lineIndex = 0; + whitespace = 0; + } else { + output += " ".repeat(whitespace); + } + if (completion !== "") { + output += completion; + whitespace = width - completionsWidth[i]; + lineIndex++; + } else { + output += "\r\n"; + } + } + if (lineIndex !== 0) { + output += "\r\n\r\n"; + } + this[kWriteToOutput](output); + this[kRefreshLine](); + } + + [kWordLeft]() { + if (this.cursor > 0) { + // Reverse the string and match a word near beginning + // to avoid quadratic time complexity + const leading = this.line.slice(0, this.cursor); + const reversed = Array.from(leading).reverse().join(""); + const match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/); + this[kMoveCursor](-match[0].length); + } + } + + [kWordRight]() { + if (this.cursor < this.line.length) { + const trailing = this.line.slice(this.cursor); + const match = trailing.match(/^(?:\s+|[^\w\s]+|\w+)\s*/); + this[kMoveCursor](match[0].length); + } + } + + [kDeleteLeft]() { + if (this.cursor > 0 && this.line.length > 0) { + // The number of UTF-16 units comprising the character to the left + const charSize = charLengthLeft(this.line, this.cursor); + this.line = this.line.slice(0, this.cursor - charSize) + + this.line.slice(this.cursor, this.line.length); + + this.cursor -= charSize; + this[kRefreshLine](); + } + } + + [kDeleteRight]() { + if (this.cursor < this.line.length) { + // The number of UTF-16 units comprising the character to the left + const charSize = charLengthAt(this.line, this.cursor); + this.line = this.line.slice(0, this.cursor) + + this.line.slice( + this.cursor + charSize, + this.line.length, + ); + this[kRefreshLine](); + } + } + + [kDeleteWordLeft]() { + if (this.cursor > 0) { + // Reverse the string and match a word near beginning + // to avoid quadratic time complexity + let leading = this.line.slice(0, this.cursor); + const reversed = Array.from(leading).reverse().join(""); + const match = reversed.match(/^\s*(?:[^\w\s]+|\w+)?/); + leading = leading.slice( + 0, + leading.length - match[0].length, + ); + this.line = leading + + this.line.slice(this.cursor, this.line.length); + this.cursor = leading.length; + this[kRefreshLine](); + } + } + + [kDeleteWordRight]() { + if (this.cursor < this.line.length) { + const trailing = this.line.slice(this.cursor); + const match = trailing.match(/^(?:\s+|\W+|\w+)\s*/); + this.line = this.line.slice(0, this.cursor) + + trailing.slice(match[0].length); + this[kRefreshLine](); + } + } + + [kDeleteLineLeft]() { + this.line = this.line.slice(this.cursor); + this.cursor = 0; + this[kRefreshLine](); + } + + [kDeleteLineRight]() { + this.line = this.line.slice(0, this.cursor); + this[kRefreshLine](); + } + + clearLine() { + this[kMoveCursor](+Infinity); + this[kWriteToOutput]("\r\n"); + this.line = ""; + this.cursor = 0; + this.prevRows = 0; + } + + [kLine]() { + const line = this[kAddHistory](); + this.clearLine(); + this[kOnLine](line); + } + + // TODO(BridgeAR): Add underscores to the search part and a red background in + // case no match is found. This should only be the visual part and not the + // actual line content! + // TODO(BridgeAR): In case the substring based search is active and the end is + // reached, show a comment how to search the history as before. E.g., using + // + N. Only show this after two/three UPs or DOWNs, not on the first + // one. + [kHistoryNext]() { + if (this.historyIndex >= 0) { + const search = this[kSubstringSearch] || ""; + let index = this.historyIndex - 1; + while ( + index >= 0 && + (!this.history[index].startsWith(search) || + this.line === this.history[index]) + ) { + index--; + } + if (index === -1) { + this.line = search; + } else { + this.line = this.history[index]; + } + this.historyIndex = index; + this.cursor = this.line.length; // Set cursor to end of line. + this[kRefreshLine](); + } + } + + [kHistoryPrev]() { + if (this.historyIndex < this.history.length && this.history.length) { + const search = this[kSubstringSearch] || ""; + let index = this.historyIndex + 1; + while ( + index < this.history.length && + (!this.history[index].startsWith(search) || + this.line === this.history[index]) + ) { + index++; + } + if (index === this.history.length) { + this.line = search; + } else { + this.line = this.history[index]; + } + this.historyIndex = index; + this.cursor = this.line.length; // Set cursor to end of line. + this[kRefreshLine](); + } + } + + // Returns the last character's display position of the given string + [kGetDisplayPos](str) { + let offset = 0; + const col = this.columns; + let rows = 0; + str = stripVTControlCharacters(str); + for (const char of str[Symbol.iterator]()) { + if (char === "\n") { + // Rows must be incremented by 1 even if offset = 0 or col = +Infinity. + rows += Math.ceil(offset / col) || 1; + offset = 0; + continue; + } + // Tabs must be aligned by an offset of the tab size. + if (char === "\t") { + offset += this.tabSize - (offset % this.tabSize); + continue; + } + const width = getStringWidth(char); + if (width === 0 || width === 1) { + offset += width; + } else { + // width === 2 + if ((offset + 1) % col === 0) { + offset++; + } + offset += 2; + } + } + const cols = offset % col; + rows += (offset - cols) / col; + return { cols, rows }; + } + + /** + * Returns the real position of the cursor in relation + * to the input prompt + string. + * @returns {{ + * rows: number; + * cols: number; + * }} + */ + getCursorPos() { + const strBeforeCursor = this[kPrompt] + + this.line.slice(0, this.cursor); + return this[kGetDisplayPos](strBeforeCursor); + } + + // This function moves cursor dx places to the right + // (-dx for left) and refreshes the line if it is needed. + [kMoveCursor](dx) { + if (dx === 0) { + return; + } + const oldPos = this.getCursorPos(); + this.cursor += dx; + + // Bounds check + if (this.cursor < 0) { + this.cursor = 0; + } else if (this.cursor > this.line.length) { + this.cursor = this.line.length; + } + + const newPos = this.getCursorPos(); + + // Check if cursor stayed on the line. + if (oldPos.rows === newPos.rows) { + const diffWidth = newPos.cols - oldPos.cols; + moveCursor(this.output, diffWidth, 0); + } else { + this[kRefreshLine](); + } + } + + // Handle a write from the tty + [kTtyWrite](s, key) { + const previousKey = this[kPreviousKey]; + key = key || {}; + this[kPreviousKey] = key; + + // Activate or deactivate substring search. + if ( + (key.name === "up" || key.name === "down") && + !key.ctrl && + !key.meta && + !key.shift + ) { + if (this[kSubstringSearch] === null) { + this[kSubstringSearch] = this.line.slice( + 0, + this.cursor, + ); + } + } else if (this[kSubstringSearch] !== null) { + this[kSubstringSearch] = null; + // Reset the index in case there's no match. + if (this.history.length === this.historyIndex) { + this.historyIndex = -1; + } + } + + // Ignore escape key, fixes + // https://github.com/nodejs/node-v0.x-archive/issues/2876. + if (key.name === "escape") return; + + if (key.ctrl && key.shift) { + /* Control and shift pressed */ + switch (key.name) { + // TODO(BridgeAR): The transmitted escape sequence is `\b` and that is + // identical to -h. It should have a unique escape sequence. + case "backspace": + this[kDeleteLineLeft](); + break; + + case "delete": + this[kDeleteLineRight](); + break; + } + } else if (key.ctrl) { + /* Control key pressed */ + + switch (key.name) { + case "c": + if (this.listenerCount("SIGINT") > 0) { + this.emit("SIGINT"); + } else { + // This readline instance is finished + this.close(); + } + break; + + case "h": // delete left + this[kDeleteLeft](); + break; + + case "d": // delete right or EOF + if (this.cursor === 0 && this.line.length === 0) { + // This readline instance is finished + this.close(); + } else if (this.cursor < this.line.length) { + this[kDeleteRight](); + } + break; + + case "u": // Delete from current to start of line + this[kDeleteLineLeft](); + break; + + case "k": // Delete from current to end of line + this[kDeleteLineRight](); + break; + + case "a": // Go to the start of the line + this[kMoveCursor](-Infinity); + break; + + case "e": // Go to the end of the line + this[kMoveCursor](+Infinity); + break; + + case "b": // back one character + this[kMoveCursor](-charLengthLeft(this.line, this.cursor)); + break; + + case "f": // Forward one character + this[kMoveCursor](+charLengthAt(this.line, this.cursor)); + break; + + case "l": // Clear the whole screen + cursorTo(this.output, 0, 0); + clearScreenDown(this.output); + this[kRefreshLine](); + break; + + case "n": // next history item + this[kHistoryNext](); + break; + + case "p": // Previous history item + this[kHistoryPrev](); + break; + + case "z": + if (process.platform === "win32") break; + if (this.listenerCount("SIGTSTP") > 0) { + this.emit("SIGTSTP"); + } else { + process.once("SIGCONT", () => { + // Don't raise events if stream has already been abandoned. + if (!this.paused) { + // Stream must be paused and resumed after SIGCONT to catch + // SIGINT, SIGTSTP, and EOF. + this.pause(); + this.emit("SIGCONT"); + } + // Explicitly re-enable "raw mode" and move the cursor to + // the correct position. + // See https://github.com/joyent/node/issues/3295. + this[kSetRawMode](true); + this[kRefreshLine](); + }); + this[kSetRawMode](false); + process.kill(process.pid, "SIGTSTP"); + } + break; + + case "w": // Delete backwards to a word boundary + // TODO(BridgeAR): The transmitted escape sequence is `\b` and that is + // identical to -h. It should have a unique escape sequence. + // Falls through + case "backspace": + this[kDeleteWordLeft](); + break; + + case "delete": // Delete forward to a word boundary + this[kDeleteWordRight](); + break; + + case "left": + this[kWordLeft](); + break; + + case "right": + this[kWordRight](); + break; + } + } else if (key.meta) { + /* Meta key pressed */ + + switch (key.name) { + case "b": // backward word + this[kWordLeft](); + break; + + case "f": // forward word + this[kWordRight](); + break; + + case "d": // delete forward word + case "delete": + this[kDeleteWordRight](); + break; + + case "backspace": // Delete backwards to a word boundary + this[kDeleteWordLeft](); + break; + } + } else { + /* No modifier keys used */ + + // \r bookkeeping is only relevant if a \n comes right after. + if (this[kSawReturnAt] && key.name !== "enter") this[kSawReturnAt] = 0; + + switch (key.name) { + case "return": // Carriage return, i.e. \r + this[kSawReturnAt] = Date.now(); + this[kLine](); + break; + + case "enter": + // When key interval > crlfDelay + if ( + this[kSawReturnAt] === 0 || + Date.now() - this[kSawReturnAt] > this.crlfDelay + ) { + this[kLine](); + } + this[kSawReturnAt] = 0; + break; + + case "backspace": + this[kDeleteLeft](); + break; + + case "delete": + this[kDeleteRight](); + break; + + case "left": + // Obtain the code point to the left + this[kMoveCursor](-charLengthLeft(this.line, this.cursor)); + break; + + case "right": + this[kMoveCursor](+charLengthAt(this.line, this.cursor)); + break; + + case "home": + this[kMoveCursor](-Infinity); + break; + + case "end": + this[kMoveCursor](+Infinity); + break; + + case "up": + this[kHistoryPrev](); + break; + + case "down": + this[kHistoryNext](); + break; + + case "tab": + // If tab completion enabled, do that... + if ( + typeof this.completer === "function" && + this.isCompletionEnabled + ) { + const lastKeypressWasTab = previousKey && + previousKey.name === "tab"; + this[kTabComplete](lastKeypressWasTab); + break; + } + // falls through + default: + if (typeof s === "string" && s) { + const lines = s.split(/\r\n|\n|\r/); + for (let i = 0, len = lines.length; i < len; i++) { + if (i > 0) { + this[kLine](); + } + this[kInsertString](lines[i]); + } + } + } + } + } + + /** + * Creates an `AsyncIterator` object that iterates through + * each line in the input stream as a string. + * @typedef {{ + * [Symbol.asyncIterator]: () => InterfaceAsyncIterator, + * next: () => Promise + * }} InterfaceAsyncIterator + * @returns {InterfaceAsyncIterator} + */ + [Symbol.asyncIterator]() { + if (this[kLineObjectStream] === undefined) { + const readable = new Readable({ + objectMode: true, + read: () => { + this.resume(); + }, + destroy: (err, cb) => { + this.off("line", lineListener); + this.off("close", closeListener); + this.close(); + cb(err); + }, + }); + const lineListener = (input) => { + if (!readable.push(input)) { + // TODO(rexagod): drain to resume flow + this.pause(); + } + }; + const closeListener = () => { + readable.push(null); + }; + const errorListener = (err) => { + readable.destroy(err); + }; + this.on("error", errorListener); + this.on("line", lineListener); + this.on("close", closeListener); + this[kLineObjectStream] = readable; + } + + return this[kLineObjectStream][Symbol.asyncIterator](); + } +} diff --git a/ext/node/polyfills/internal/readline/promises.mjs b/ext/node/polyfills/internal/readline/promises.mjs new file mode 100644 index 0000000000..36aa3de120 --- /dev/null +++ b/ext/node/polyfills/internal/readline/promises.mjs @@ -0,0 +1,139 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. + +import { ArrayPrototypeJoin, ArrayPrototypePush } from "internal:deno_node/polyfills/internal/primordials.mjs"; + +import { CSI } from "internal:deno_node/polyfills/internal/readline/utils.mjs"; +import { validateBoolean, validateInteger } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { isWritable } from "internal:deno_node/polyfills/internal/streams/utils.mjs"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; + +const { + kClearToLineBeginning, + kClearToLineEnd, + kClearLine, + kClearScreenDown, +} = CSI; + +export class Readline { + #autoCommit = false; + #stream; + #todo = []; + + constructor(stream, options = undefined) { + if (!isWritable(stream)) { + throw new ERR_INVALID_ARG_TYPE("stream", "Writable", stream); + } + this.#stream = stream; + if (options?.autoCommit != null) { + validateBoolean(options.autoCommit, "options.autoCommit"); + this.#autoCommit = options.autoCommit; + } + } + + /** + * Moves the cursor to the x and y coordinate on the given stream. + * @param {integer} x + * @param {integer} [y] + * @returns {Readline} this + */ + cursorTo(x, y = undefined) { + validateInteger(x, "x"); + if (y != null) validateInteger(y, "y"); + + const data = y == null ? CSI`${x + 1}G` : CSI`${y + 1};${x + 1}H`; + if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); + else ArrayPrototypePush(this.#todo, data); + + return this; + } + + /** + * Moves the cursor relative to its current location. + * @param {integer} dx + * @param {integer} dy + * @returns {Readline} this + */ + moveCursor(dx, dy) { + if (dx || dy) { + validateInteger(dx, "dx"); + validateInteger(dy, "dy"); + + let data = ""; + + if (dx < 0) { + data += CSI`${-dx}D`; + } else if (dx > 0) { + data += CSI`${dx}C`; + } + + if (dy < 0) { + data += CSI`${-dy}A`; + } else if (dy > 0) { + data += CSI`${dy}B`; + } + if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); + else ArrayPrototypePush(this.#todo, data); + } + return this; + } + + /** + * Clears the current line the cursor is on. + * @param {-1|0|1} dir Direction to clear: + * -1 for left of the cursor + * +1 for right of the cursor + * 0 for the entire line + * @returns {Readline} this + */ + clearLine(dir) { + validateInteger(dir, "dir", -1, 1); + + const data = dir < 0 + ? kClearToLineBeginning + : dir > 0 + ? kClearToLineEnd + : kClearLine; + if (this.#autoCommit) process.nextTick(() => this.#stream.write(data)); + else ArrayPrototypePush(this.#todo, data); + return this; + } + + /** + * Clears the screen from the current position of the cursor down. + * @returns {Readline} this + */ + clearScreenDown() { + if (this.#autoCommit) { + process.nextTick(() => this.#stream.write(kClearScreenDown)); + } else { + ArrayPrototypePush(this.#todo, kClearScreenDown); + } + return this; + } + + /** + * Sends all the pending actions to the associated `stream` and clears the + * internal list of pending actions. + * @returns {Promise} Resolves when all pending actions have been + * flushed to the associated `stream`. + */ + commit() { + return new Promise((resolve) => { + this.#stream.write(ArrayPrototypeJoin(this.#todo, ""), resolve); + this.#todo = []; + }); + } + + /** + * Clears the internal list of pending actions without sending it to the + * associated `stream`. + * @returns {Readline} this + */ + rollback() { + this.#todo = []; + return this; + } +} + +export default Readline; diff --git a/ext/node/polyfills/internal/readline/symbols.mjs b/ext/node/polyfills/internal/readline/symbols.mjs new file mode 100644 index 0000000000..3a05c64d2d --- /dev/null +++ b/ext/node/polyfills/internal/readline/symbols.mjs @@ -0,0 +1,34 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +export const kAddHistory = Symbol("_addHistory"); +export const kDecoder = Symbol("_decoder"); +export const kDeleteLeft = Symbol("_deleteLeft"); +export const kDeleteLineLeft = Symbol("_deleteLineLeft"); +export const kDeleteLineRight = Symbol("_deleteLineRight"); +export const kDeleteRight = Symbol("_deleteRight"); +export const kDeleteWordLeft = Symbol("_deleteWordLeft"); +export const kDeleteWordRight = Symbol("_deleteWordRight"); +export const kGetDisplayPos = Symbol("_getDisplayPos"); +export const kHistoryNext = Symbol("_historyNext"); +export const kHistoryPrev = Symbol("_historyPrev"); +export const kInsertString = Symbol("_insertString"); +export const kLine = Symbol("_line"); +export const kLine_buffer = Symbol("_line_buffer"); +export const kMoveCursor = Symbol("_moveCursor"); +export const kNormalWrite = Symbol("_normalWrite"); +export const kOldPrompt = Symbol("_oldPrompt"); +export const kOnLine = Symbol("_onLine"); +export const kPreviousKey = Symbol("_previousKey"); +export const kPrompt = Symbol("_prompt"); +export const kQuestionCallback = Symbol("_questionCallback"); +export const kRefreshLine = Symbol("_refreshLine"); +export const kSawKeyPress = Symbol("_sawKeyPress"); +export const kSawReturnAt = Symbol("_sawReturnAt"); +export const kSetRawMode = Symbol("_setRawMode"); +export const kTabComplete = Symbol("_tabComplete"); +export const kTabCompleter = Symbol("_tabCompleter"); +export const kTtyWrite = Symbol("_ttyWrite"); +export const kWordLeft = Symbol("_wordLeft"); +export const kWordRight = Symbol("_wordRight"); +export const kWriteToOutput = Symbol("_writeToOutput"); diff --git a/ext/node/polyfills/internal/readline/utils.mjs b/ext/node/polyfills/internal/readline/utils.mjs new file mode 100644 index 0000000000..6224f112b3 --- /dev/null +++ b/ext/node/polyfills/internal/readline/utils.mjs @@ -0,0 +1,580 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +"use strict"; + +const kUTF16SurrogateThreshold = 0x10000; // 2 ** 16 +const kEscape = "\x1b"; +export const kSubstringSearch = Symbol("kSubstringSearch"); + +export function CSI(strings, ...args) { + let ret = `${kEscape}[`; + for (let n = 0; n < strings.length; n++) { + ret += strings[n]; + if (n < args.length) { + ret += args[n]; + } + } + return ret; +} + +CSI.kEscape = kEscape; +CSI.kClearToLineBeginning = CSI`1K`; +CSI.kClearToLineEnd = CSI`0K`; +CSI.kClearLine = CSI`2K`; +CSI.kClearScreenDown = CSI`0J`; + +// TODO(BridgeAR): Treat combined characters as single character, i.e, +// 'a\u0301' and '\u0301a' (both have the same visual output). +// Check Canonical_Combining_Class in +// http://userguide.icu-project.org/strings/properties +export function charLengthLeft(str, i) { + if (i <= 0) { + return 0; + } + if ( + (i > 1 && + str.codePointAt(i - 2) >= kUTF16SurrogateThreshold) || + str.codePointAt(i - 1) >= kUTF16SurrogateThreshold + ) { + return 2; + } + return 1; +} + +export function charLengthAt(str, i) { + if (str.length <= i) { + // Pretend to move to the right. This is necessary to autocomplete while + // moving to the right. + return 1; + } + return str.codePointAt(i) >= kUTF16SurrogateThreshold ? 2 : 1; +} + +/* + Some patterns seen in terminal key escape codes, derived from combos seen + at http://www.midnight-commander.org/browser/lib/tty/key.c + + ESC letter + ESC [ letter + ESC [ modifier letter + ESC [ 1 ; modifier letter + ESC [ num char + ESC [ num ; modifier char + ESC O letter + ESC O modifier letter + ESC O 1 ; modifier letter + ESC N letter + ESC [ [ num ; modifier char + ESC [ [ 1 ; modifier letter + ESC ESC [ num char + ESC ESC O letter + + - char is usually ~ but $ and ^ also happen with rxvt + - modifier is 1 + + (shift * 1) + + (left_alt * 2) + + (ctrl * 4) + + (right_alt * 8) + - two leading ESCs apparently mean the same as one leading ESC +*/ +export function* emitKeys(stream) { + while (true) { + let ch = yield; + let s = ch; + let escaped = false; + const key = { + sequence: null, + name: undefined, + ctrl: false, + meta: false, + shift: false, + }; + + if (ch === kEscape) { + escaped = true; + s += ch = yield; + + if (ch === kEscape) { + s += ch = yield; + } + } + + if (escaped && (ch === "O" || ch === "[")) { + // ANSI escape sequence + let code = ch; + let modifier = 0; + + if (ch === "O") { + // ESC O letter + // ESC O modifier letter + s += ch = yield; + + if (ch >= "0" && ch <= "9") { + modifier = (ch >> 0) - 1; + s += ch = yield; + } + + code += ch; + } else if (ch === "[") { + // ESC [ letter + // ESC [ modifier letter + // ESC [ [ modifier letter + // ESC [ [ num char + s += ch = yield; + + if (ch === "[") { + // \x1b[[A + // ^--- escape codes might have a second bracket + code += ch; + s += ch = yield; + } + + /* + * Here and later we try to buffer just enough data to get + * a complete ascii sequence. + * + * We have basically two classes of ascii characters to process: + * + * 1. `\x1b[24;5~` should be parsed as { code: '[24~', modifier: 5 } + * + * This particular example is featuring Ctrl+F12 in xterm. + * + * - `;5` part is optional, e.g. it could be `\x1b[24~` + * - first part can contain one or two digits + * + * So the generic regexp is like /^\d\d?(;\d)?[~^$]$/ + * + * 2. `\x1b[1;5H` should be parsed as { code: '[H', modifier: 5 } + * + * This particular example is featuring Ctrl+Home in xterm. + * + * - `1;5` part is optional, e.g. it could be `\x1b[H` + * - `1;` part is optional, e.g. it could be `\x1b[5H` + * + * So the generic regexp is like /^((\d;)?\d)?[A-Za-z]$/ + */ + const cmdStart = s.length - 1; + + // Skip one or two leading digits + if (ch >= "0" && ch <= "9") { + s += ch = yield; + + if (ch >= "0" && ch <= "9") { + s += ch = yield; + } + } + + // skip modifier + if (ch === ";") { + s += ch = yield; + + if (ch >= "0" && ch <= "9") { + s += yield; + } + } + + /* + * We buffered enough data, now trying to extract code + * and modifier from it + */ + const cmd = s.slice(cmdStart); + let match; + + if ((match = cmd.match(/^(\d\d?)(;(\d))?([~^$])$/))) { + code += match[1] + match[4]; + modifier = (match[3] || 1) - 1; + } else if ( + (match = cmd.match(/^((\d;)?(\d))?([A-Za-z])$/)) + ) { + code += match[4]; + modifier = (match[3] || 1) - 1; + } else { + code += cmd; + } + } + + // Parse the key modifier + key.ctrl = !!(modifier & 4); + key.meta = !!(modifier & 10); + key.shift = !!(modifier & 1); + key.code = code; + + // Parse the key itself + switch (code) { + /* xterm/gnome ESC [ letter (with modifier) */ + case "[P": + key.name = "f1"; + break; + case "[Q": + key.name = "f2"; + break; + case "[R": + key.name = "f3"; + break; + case "[S": + key.name = "f4"; + break; + + /* xterm/gnome ESC O letter (without modifier) */ + + case "OP": + key.name = "f1"; + break; + case "OQ": + key.name = "f2"; + break; + case "OR": + key.name = "f3"; + break; + case "OS": + key.name = "f4"; + break; + + /* xterm/rxvt ESC [ number ~ */ + + case "[11~": + key.name = "f1"; + break; + case "[12~": + key.name = "f2"; + break; + case "[13~": + key.name = "f3"; + break; + case "[14~": + key.name = "f4"; + break; + + /* from Cygwin and used in libuv */ + + case "[[A": + key.name = "f1"; + break; + case "[[B": + key.name = "f2"; + break; + case "[[C": + key.name = "f3"; + break; + case "[[D": + key.name = "f4"; + break; + case "[[E": + key.name = "f5"; + break; + + /* common */ + + case "[15~": + key.name = "f5"; + break; + case "[17~": + key.name = "f6"; + break; + case "[18~": + key.name = "f7"; + break; + case "[19~": + key.name = "f8"; + break; + case "[20~": + key.name = "f9"; + break; + case "[21~": + key.name = "f10"; + break; + case "[23~": + key.name = "f11"; + break; + case "[24~": + key.name = "f12"; + break; + + /* xterm ESC [ letter */ + + case "[A": + key.name = "up"; + break; + case "[B": + key.name = "down"; + break; + case "[C": + key.name = "right"; + break; + case "[D": + key.name = "left"; + break; + case "[E": + key.name = "clear"; + break; + case "[F": + key.name = "end"; + break; + case "[H": + key.name = "home"; + break; + + /* xterm/gnome ESC O letter */ + + case "OA": + key.name = "up"; + break; + case "OB": + key.name = "down"; + break; + case "OC": + key.name = "right"; + break; + case "OD": + key.name = "left"; + break; + case "OE": + key.name = "clear"; + break; + case "OF": + key.name = "end"; + break; + case "OH": + key.name = "home"; + break; + + /* xterm/rxvt ESC [ number ~ */ + + case "[1~": + key.name = "home"; + break; + case "[2~": + key.name = "insert"; + break; + case "[3~": + key.name = "delete"; + break; + case "[4~": + key.name = "end"; + break; + case "[5~": + key.name = "pageup"; + break; + case "[6~": + key.name = "pagedown"; + break; + + /* putty */ + + case "[[5~": + key.name = "pageup"; + break; + case "[[6~": + key.name = "pagedown"; + break; + + /* rxvt */ + + case "[7~": + key.name = "home"; + break; + case "[8~": + key.name = "end"; + break; + + /* rxvt keys with modifiers */ + + case "[a": + key.name = "up"; + key.shift = true; + break; + case "[b": + key.name = "down"; + key.shift = true; + break; + case "[c": + key.name = "right"; + key.shift = true; + break; + case "[d": + key.name = "left"; + key.shift = true; + break; + case "[e": + key.name = "clear"; + key.shift = true; + break; + + case "[2$": + key.name = "insert"; + key.shift = true; + break; + case "[3$": + key.name = "delete"; + key.shift = true; + break; + case "[5$": + key.name = "pageup"; + key.shift = true; + break; + case "[6$": + key.name = "pagedown"; + key.shift = true; + break; + case "[7$": + key.name = "home"; + key.shift = true; + break; + case "[8$": + key.name = "end"; + key.shift = true; + break; + + case "Oa": + key.name = "up"; + key.ctrl = true; + break; + case "Ob": + key.name = "down"; + key.ctrl = true; + break; + case "Oc": + key.name = "right"; + key.ctrl = true; + break; + case "Od": + key.name = "left"; + key.ctrl = true; + break; + case "Oe": + key.name = "clear"; + key.ctrl = true; + break; + + case "[2^": + key.name = "insert"; + key.ctrl = true; + break; + case "[3^": + key.name = "delete"; + key.ctrl = true; + break; + case "[5^": + key.name = "pageup"; + key.ctrl = true; + break; + case "[6^": + key.name = "pagedown"; + key.ctrl = true; + break; + case "[7^": + key.name = "home"; + key.ctrl = true; + break; + case "[8^": + key.name = "end"; + key.ctrl = true; + break; + + /* misc. */ + + case "[Z": + key.name = "tab"; + key.shift = true; + break; + default: + key.name = "undefined"; + break; + } + } else if (ch === "\r") { + // carriage return + key.name = "return"; + key.meta = escaped; + } else if (ch === "\n") { + // Enter, should have been called linefeed + key.name = "enter"; + key.meta = escaped; + } else if (ch === "\t") { + // tab + key.name = "tab"; + key.meta = escaped; + } else if (ch === "\b" || ch === "\x7f") { + // backspace or ctrl+h + key.name = "backspace"; + key.meta = escaped; + } else if (ch === kEscape) { + // escape key + key.name = "escape"; + key.meta = escaped; + } else if (ch === " ") { + key.name = "space"; + key.meta = escaped; + } else if (!escaped && ch <= "\x1a") { + // ctrl+letter + key.name = String.fromCharCode( + ch.charCodeAt() + "a".charCodeAt() - 1, + ); + key.ctrl = true; + } else if (/^[0-9A-Za-z]$/.test(ch)) { + // Letter, number, shift+letter + key.name = ch.toLowerCase(); + key.shift = /^[A-Z]$/.test(ch); + key.meta = escaped; + } else if (escaped) { + // Escape sequence timeout + key.name = ch.length ? undefined : "escape"; + key.meta = true; + } + + key.sequence = s; + + if (s.length !== 0 && (key.name !== undefined || escaped)) { + /* Named character or sequence */ + stream.emit("keypress", escaped ? undefined : s, key); + } else if (charLengthAt(s, 0) === s.length) { + /* Single unnamed character, e.g. "." */ + stream.emit("keypress", s, key); + } + /* Unrecognized or broken escape sequence, don't emit anything */ + } +} + +// This runs in O(n log n). +export function commonPrefix(strings) { + if (strings.length === 1) { + return strings[0]; + } + const sorted = strings.slice().sort(); + const min = sorted[0]; + const max = sorted[sorted.length - 1]; + for (let i = 0; i < min.length; i++) { + if (min[i] !== max[i]) { + return min.slice(0, i); + } + } + return min; +} + +export default { + CSI, + charLengthAt, + charLengthLeft, + emitKeys, + commonPrefix, + kSubstringSearch, +}; diff --git a/ext/node/polyfills/internal/stream_base_commons.ts b/ext/node/polyfills/internal/stream_base_commons.ts new file mode 100644 index 0000000000..dd1c74d0f6 --- /dev/null +++ b/ext/node/polyfills/internal/stream_base_commons.ts @@ -0,0 +1,355 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { ownerSymbol } from "internal:deno_node/polyfills/internal/async_hooks.ts"; +import { + kArrayBufferOffset, + kBytesWritten, + kLastWriteWasAsync, + LibuvStreamWrap, + streamBaseState, + WriteWrap, +} from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { isUint8Array } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { errnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { + getTimerDuration, + kTimeout, +} from "internal:deno_node/polyfills/internal/timers.mjs"; +import { setUnrefTimeout } from "internal:deno_node/polyfills/timers.ts"; +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +export const kMaybeDestroy = Symbol("kMaybeDestroy"); +export const kUpdateTimer = Symbol("kUpdateTimer"); +export const kAfterAsyncWrite = Symbol("kAfterAsyncWrite"); +export const kHandle = Symbol("kHandle"); +export const kSession = Symbol("kSession"); +export const kBuffer = Symbol("kBuffer"); +export const kBufferGen = Symbol("kBufferGen"); +export const kBufferCb = Symbol("kBufferCb"); + +// deno-lint-ignore no-explicit-any +function handleWriteReq(req: any, data: any, encoding: string) { + const { handle } = req; + + switch (encoding) { + case "buffer": { + const ret = handle.writeBuffer(req, data); + + if (streamBaseState[kLastWriteWasAsync]) { + req.buffer = data; + } + + return ret; + } + case "latin1": + case "binary": + return handle.writeLatin1String(req, data); + case "utf8": + case "utf-8": + return handle.writeUtf8String(req, data); + case "ascii": + return handle.writeAsciiString(req, data); + case "ucs2": + case "ucs-2": + case "utf16le": + case "utf-16le": + return handle.writeUcs2String(req, data); + default: { + const buffer = Buffer.from(data, encoding); + const ret = handle.writeBuffer(req, buffer); + + if (streamBaseState[kLastWriteWasAsync]) { + req.buffer = buffer; + } + + return ret; + } + } +} + +// deno-lint-ignore no-explicit-any +function onWriteComplete(this: any, status: number) { + let stream = this.handle[ownerSymbol]; + + if (stream.constructor.name === "ReusedHandle") { + stream = stream.handle; + } + + if (stream.destroyed) { + if (typeof this.callback === "function") { + this.callback(null); + } + + return; + } + + if (status < 0) { + const ex = errnoException(status, "write", this.error); + + if (typeof this.callback === "function") { + this.callback(ex); + } else { + stream.destroy(ex); + } + + return; + } + + stream[kUpdateTimer](); + stream[kAfterAsyncWrite](this); + + if (typeof this.callback === "function") { + this.callback(null); + } +} + +function createWriteWrap( + handle: LibuvStreamWrap, + callback: (err?: Error | null) => void, +) { + const req = new WriteWrap(); + + req.handle = handle; + req.oncomplete = onWriteComplete; + req.async = false; + req.bytes = 0; + req.buffer = null; + req.callback = callback; + + return req; +} + +export function writevGeneric( + // deno-lint-ignore no-explicit-any + owner: any, + // deno-lint-ignore no-explicit-any + data: any, + cb: (err?: Error | null) => void, +) { + const req = createWriteWrap(owner[kHandle], cb); + const allBuffers = data.allBuffers; + let chunks; + + if (allBuffers) { + chunks = data; + + for (let i = 0; i < data.length; i++) { + data[i] = data[i].chunk; + } + } else { + chunks = new Array(data.length << 1); + + for (let i = 0; i < data.length; i++) { + const entry = data[i]; + chunks[i * 2] = entry.chunk; + chunks[i * 2 + 1] = entry.encoding; + } + } + + const err = req.handle.writev(req, chunks, allBuffers); + + // Retain chunks + if (err === 0) { + req._chunks = chunks; + } + + afterWriteDispatched(req, err, cb); + + return req; +} + +export function writeGeneric( + // deno-lint-ignore no-explicit-any + owner: any, + // deno-lint-ignore no-explicit-any + data: any, + encoding: string, + cb: (err?: Error | null) => void, +) { + const req = createWriteWrap(owner[kHandle], cb); + const err = handleWriteReq(req, data, encoding); + + afterWriteDispatched(req, err, cb); + + return req; +} + +function afterWriteDispatched( + // deno-lint-ignore no-explicit-any + req: any, + err: number, + cb: (err?: Error | null) => void, +) { + req.bytes = streamBaseState[kBytesWritten]; + req.async = !!streamBaseState[kLastWriteWasAsync]; + + if (err !== 0) { + return cb(errnoException(err, "write", req.error)); + } + + if (!req.async && typeof req.callback === "function") { + req.callback(); + } +} + +// Here we differ from Node slightly. Node makes use of the `kReadBytesOrError` +// entry of the `streamBaseState` array from the `stream_wrap` internal binding. +// Here we pass the `nread` value directly to this method as async Deno APIs +// don't grant us the ability to rely on some mutable array entry setting. +export function onStreamRead( + // deno-lint-ignore no-explicit-any + this: any, + arrayBuffer: Uint8Array, + nread: number, +) { + // deno-lint-ignore no-this-alias + const handle = this; + + let stream = this[ownerSymbol]; + + if (stream.constructor.name === "ReusedHandle") { + stream = stream.handle; + } + + stream[kUpdateTimer](); + + if (nread > 0 && !stream.destroyed) { + let ret; + let result; + const userBuf = stream[kBuffer]; + + if (userBuf) { + result = stream[kBufferCb](nread, userBuf) !== false; + const bufGen = stream[kBufferGen]; + + if (bufGen !== null) { + const nextBuf = bufGen(); + + if (isUint8Array(nextBuf)) { + stream[kBuffer] = ret = nextBuf; + } + } + } else { + const offset = streamBaseState[kArrayBufferOffset]; + const buf = Buffer.from(arrayBuffer, offset, nread); + result = stream.push(buf); + } + + if (!result) { + handle.reading = false; + + if (!stream.destroyed) { + const err = handle.readStop(); + + if (err) { + stream.destroy(errnoException(err, "read")); + } + } + } + + return ret; + } + + if (nread === 0) { + return; + } + + if (nread !== codeMap.get("EOF")) { + // CallJSOnreadMethod expects the return value to be a buffer. + // Ref: https://github.com/nodejs/node/pull/34375 + stream.destroy(errnoException(nread, "read")); + + return; + } + + // Defer this until we actually emit end + if (stream._readableState.endEmitted) { + if (stream[kMaybeDestroy]) { + stream[kMaybeDestroy](); + } + } else { + if (stream[kMaybeDestroy]) { + stream.on("end", stream[kMaybeDestroy]); + } + + if (handle.readStop) { + const err = handle.readStop(); + + if (err) { + // CallJSOnreadMethod expects the return value to be a buffer. + // Ref: https://github.com/nodejs/node/pull/34375 + stream.destroy(errnoException(err, "read")); + + return; + } + } + + // Push a null to signal the end of data. + // Do it before `maybeDestroy` for correct order of events: + // `end` -> `close` + stream.push(null); + stream.read(0); + } +} + +export function setStreamTimeout( + // deno-lint-ignore no-explicit-any + this: any, + msecs: number, + callback?: () => void, +) { + if (this.destroyed) { + return this; + } + + this.timeout = msecs; + + // Type checking identical to timers.enroll() + msecs = getTimerDuration(msecs, "msecs"); + + // Attempt to clear an existing timer in both cases - + // even if it will be rescheduled we don't want to leak an existing timer. + clearTimeout(this[kTimeout]); + + if (msecs === 0) { + if (callback !== undefined) { + validateFunction(callback, "callback"); + this.removeListener("timeout", callback); + } + } else { + this[kTimeout] = setUnrefTimeout(this._onTimeout.bind(this), msecs); + + if (this[kSession]) { + this[kSession][kUpdateTimer](); + } + + if (callback !== undefined) { + validateFunction(callback, "callback"); + this.once("timeout", callback); + } + } + + return this; +} diff --git a/ext/node/polyfills/internal/streams/add-abort-signal.mjs b/ext/node/polyfills/internal/streams/add-abort-signal.mjs new file mode 100644 index 0000000000..5d7512f1c5 --- /dev/null +++ b/ext/node/polyfills/internal/streams/add-abort-signal.mjs @@ -0,0 +1,48 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { AbortError, ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; +import eos from "internal:deno_node/polyfills/internal/streams/end-of-stream.mjs"; + +// This method is inlined here for readable-stream +// It also does not allow for signal to not exist on the stream +// https://github.com/nodejs/node/pull/36061#discussion_r533718029 +const validateAbortSignal = (signal, name) => { + if ( + typeof signal !== "object" || + !("aborted" in signal) + ) { + throw new ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); + } +}; + +function isStream(obj) { + return !!(obj && typeof obj.pipe === "function"); +} + +function addAbortSignal(signal, stream) { + validateAbortSignal(signal, "signal"); + if (!isStream(stream)) { + throw new ERR_INVALID_ARG_TYPE("stream", "stream.Stream", stream); + } + return addAbortSignalNoValidate(signal, stream); +} +function addAbortSignalNoValidate(signal, stream) { + if (typeof signal !== "object" || !("aborted" in signal)) { + return stream; + } + const onAbort = () => { + stream.destroy(new AbortError()); + }; + if (signal.aborted) { + onAbort(); + } else { + signal.addEventListener("abort", onAbort); + eos(stream, () => signal.removeEventListener("abort", onAbort)); + } + return stream; +} + +export default { addAbortSignal, addAbortSignalNoValidate }; +export { addAbortSignal, addAbortSignalNoValidate }; diff --git a/ext/node/polyfills/internal/streams/buffer_list.mjs b/ext/node/polyfills/internal/streams/buffer_list.mjs new file mode 100644 index 0000000000..3016ffba56 --- /dev/null +++ b/ext/node/polyfills/internal/streams/buffer_list.mjs @@ -0,0 +1,188 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { inspect } from "internal:deno_node/polyfills/internal/util/inspect.mjs"; + +class BufferList { + constructor() { + this.head = null; + this.tail = null; + this.length = 0; + } + + push(v) { + const entry = { data: v, next: null }; + if (this.length > 0) { + this.tail.next = entry; + } else { + this.head = entry; + } + this.tail = entry; + ++this.length; + } + + unshift(v) { + const entry = { data: v, next: this.head }; + if (this.length === 0) { + this.tail = entry; + } + this.head = entry; + ++this.length; + } + + shift() { + if (this.length === 0) { + return; + } + const ret = this.head.data; + if (this.length === 1) { + this.head = this.tail = null; + } else { + this.head = this.head.next; + } + --this.length; + return ret; + } + + clear() { + this.head = this.tail = null; + this.length = 0; + } + + join(s) { + if (this.length === 0) { + return ""; + } + let p = this.head; + let ret = "" + p.data; + while (p = p.next) { + ret += s + p.data; + } + return ret; + } + + concat(n) { + if (this.length === 0) { + return Buffer.alloc(0); + } + const ret = Buffer.allocUnsafe(n >>> 0); + let p = this.head; + let i = 0; + while (p) { + ret.set(p.data, i); + i += p.data.length; + p = p.next; + } + return ret; + } + + // Consumes a specified amount of bytes or characters from the buffered data. + consume(n, hasStrings) { + const data = this.head.data; + if (n < data.length) { + // `slice` is the same for buffers and strings. + const slice = data.slice(0, n); + this.head.data = data.slice(n); + return slice; + } + if (n === data.length) { + // First chunk is a perfect match. + return this.shift(); + } + // Result spans more than one buffer. + return hasStrings ? this._getString(n) : this._getBuffer(n); + } + + first() { + return this.head.data; + } + + *[Symbol.iterator]() { + for (let p = this.head; p; p = p.next) { + yield p.data; + } + } + + // Consumes a specified amount of characters from the buffered data. + _getString(n) { + let ret = ""; + let p = this.head; + let c = 0; + do { + const str = p.data; + if (n > str.length) { + ret += str; + n -= str.length; + } else { + if (n === str.length) { + ret += str; + ++c; + if (p.next) { + this.head = p.next; + } else { + this.head = this.tail = null; + } + } else { + ret += str.slice(0, n); + this.head = p; + p.data = str.slice(n); + } + break; + } + ++c; + } while (p = p.next); + this.length -= c; + return ret; + } + + // Consumes a specified amount of bytes from the buffered data. + _getBuffer(n) { + const ret = Buffer.allocUnsafe(n); + const retLen = n; + let p = this.head; + let c = 0; + do { + const buf = p.data; + if (n > buf.length) { + ret.set(buf, retLen - n); + n -= buf.length; + } else { + if (n === buf.length) { + ret.set(buf, retLen - n); + ++c; + if (p.next) { + this.head = p.next; + } else { + this.head = this.tail = null; + } + } else { + ret.set( + new Uint8Array(buf.buffer, buf.byteOffset, n), + retLen - n, + ); + this.head = p; + p.data = buf.slice(n); + } + break; + } + ++c; + } while (p = p.next); + this.length -= c; + return ret; + } + + // Make sure the linked list only shows the minimal necessary information. + [inspect.custom](_, options) { + return inspect(this, { + ...options, + // Only inspect one level. + depth: 0, + // It should not recurse. + customInspect: false, + }); + } +} + +export default BufferList; diff --git a/ext/node/polyfills/internal/streams/destroy.mjs b/ext/node/polyfills/internal/streams/destroy.mjs new file mode 100644 index 0000000000..b065f2119b --- /dev/null +++ b/ext/node/polyfills/internal/streams/destroy.mjs @@ -0,0 +1,320 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { aggregateTwoErrors, ERR_MULTIPLE_CALLBACK } from "internal:deno_node/polyfills/internal/errors.ts"; +import * as process from "internal:deno_node/polyfills/_process/process.ts"; + +const kDestroy = Symbol("kDestroy"); +const kConstruct = Symbol("kConstruct"); + +function checkError(err, w, r) { + if (err) { + // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 + err.stack; // eslint-disable-line no-unused-expressions + + if (w && !w.errored) { + w.errored = err; + } + if (r && !r.errored) { + r.errored = err; + } + } +} + +// Backwards compat. cb() is undocumented and unused in core but +// unfortunately might be used by modules. +function destroy(err, cb) { + const r = this._readableState; + const w = this._writableState; + // With duplex streams we use the writable side for state. + const s = w || r; + + if ((w && w.destroyed) || (r && r.destroyed)) { + if (typeof cb === "function") { + cb(); + } + + return this; + } + + // We set destroyed to true before firing error callbacks in order + // to make it re-entrance safe in case destroy() is called within callbacks + checkError(err, w, r); + + if (w) { + w.destroyed = true; + } + if (r) { + r.destroyed = true; + } + + // If still constructing then defer calling _destroy. + if (!s.constructed) { + this.once(kDestroy, function (er) { + _destroy(this, aggregateTwoErrors(er, err), cb); + }); + } else { + _destroy(this, err, cb); + } + + return this; +} + +function _destroy(self, err, cb) { + let called = false; + + function onDestroy(err) { + if (called) { + return; + } + called = true; + + const r = self._readableState; + const w = self._writableState; + + checkError(err, w, r); + + if (w) { + w.closed = true; + } + if (r) { + r.closed = true; + } + + if (typeof cb === "function") { + cb(err); + } + + if (err) { + process.nextTick(emitErrorCloseNT, self, err); + } else { + process.nextTick(emitCloseNT, self); + } + } + try { + const result = self._destroy(err || null, onDestroy); + if (result != null) { + const then = result.then; + if (typeof then === "function") { + then.call( + result, + function () { + process.nextTick(onDestroy, null); + }, + function (err) { + process.nextTick(onDestroy, err); + }, + ); + } + } + } catch (err) { + onDestroy(err); + } +} + +function emitErrorCloseNT(self, err) { + emitErrorNT(self, err); + emitCloseNT(self); +} + +function emitCloseNT(self) { + const r = self._readableState; + const w = self._writableState; + + if (w) { + w.closeEmitted = true; + } + if (r) { + r.closeEmitted = true; + } + + if ((w && w.emitClose) || (r && r.emitClose)) { + self.emit("close"); + } +} + +function emitErrorNT(self, err) { + const r = self._readableState; + const w = self._writableState; + + if ((w && w.errorEmitted) || (r && r.errorEmitted)) { + return; + } + + if (w) { + w.errorEmitted = true; + } + if (r) { + r.errorEmitted = true; + } + + self.emit("error", err); +} + +function undestroy() { + const r = this._readableState; + const w = this._writableState; + + if (r) { + r.constructed = true; + r.closed = false; + r.closeEmitted = false; + r.destroyed = false; + r.errored = null; + r.errorEmitted = false; + r.reading = false; + r.ended = false; + r.endEmitted = false; + } + + if (w) { + w.constructed = true; + w.destroyed = false; + w.closed = false; + w.closeEmitted = false; + w.errored = null; + w.errorEmitted = false; + w.ended = false; + w.ending = false; + w.finalCalled = false; + w.prefinished = false; + w.finished = false; + } +} + +function errorOrDestroy(stream, err, sync) { + // We have tests that rely on errors being emitted + // in the same tick, so changing this is semver major. + // For now when you opt-in to autoDestroy we allow + // the error to be emitted nextTick. In a future + // semver major update we should change the default to this. + + const r = stream._readableState; + const w = stream._writableState; + + if ((w && w.destroyed) || (r && r.destroyed)) { + return this; + } + + if ((r && r.autoDestroy) || (w && w.autoDestroy)) { + stream.destroy(err); + } else if (err) { + // Avoid V8 leak, https://github.com/nodejs/node/pull/34103#issuecomment-652002364 + err.stack; // eslint-disable-line no-unused-expressions + + if (w && !w.errored) { + w.errored = err; + } + if (r && !r.errored) { + r.errored = err; + } + if (sync) { + process.nextTick(emitErrorNT, stream, err); + } else { + emitErrorNT(stream, err); + } + } +} + +function construct(stream, cb) { + if (typeof stream._construct !== "function") { + return; + } + + const r = stream._readableState; + const w = stream._writableState; + + if (r) { + r.constructed = false; + } + if (w) { + w.constructed = false; + } + + stream.once(kConstruct, cb); + + if (stream.listenerCount(kConstruct) > 1) { + // Duplex + return; + } + + process.nextTick(constructNT, stream); +} + +function constructNT(stream) { + let called = false; + + function onConstruct(err) { + if (called) { + errorOrDestroy(stream, err ?? new ERR_MULTIPLE_CALLBACK()); + return; + } + called = true; + + const r = stream._readableState; + const w = stream._writableState; + const s = w || r; + + if (r) { + r.constructed = true; + } + if (w) { + w.constructed = true; + } + + if (s.destroyed) { + stream.emit(kDestroy, err); + } else if (err) { + errorOrDestroy(stream, err, true); + } else { + process.nextTick(emitConstructNT, stream); + } + } + + try { + const result = stream._construct(onConstruct); + if (result != null) { + const then = result.then; + if (typeof then === "function") { + then.call( + result, + function () { + process.nextTick(onConstruct, null); + }, + function (err) { + process.nextTick(onConstruct, err); + }, + ); + } + } + } catch (err) { + onConstruct(err); + } +} + +function emitConstructNT(stream) { + stream.emit(kConstruct); +} + +function isRequest(stream) { + return stream && stream.setHeader && typeof stream.abort === "function"; +} + +// Normalize destroy for legacy. +function destroyer(stream, err) { + if (!stream) return; + if (isRequest(stream)) return stream.abort(); + if (isRequest(stream.req)) return stream.req.abort(); + if (typeof stream.destroy === "function") return stream.destroy(err); + if (typeof stream.close === "function") return stream.close(); +} + +export default { + construct, + destroyer, + destroy, + undestroy, + errorOrDestroy, +}; +export { construct, destroy, destroyer, errorOrDestroy, undestroy }; diff --git a/ext/node/polyfills/internal/streams/duplex.mjs b/ext/node/polyfills/internal/streams/duplex.mjs new file mode 100644 index 0000000000..b2086d4675 --- /dev/null +++ b/ext/node/polyfills/internal/streams/duplex.mjs @@ -0,0 +1,9 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { Duplex } from "internal:deno_node/polyfills/_stream.mjs"; +const { from, fromWeb, toWeb } = Duplex; + +export default Duplex; +export { from, fromWeb, toWeb }; diff --git a/ext/node/polyfills/internal/streams/end-of-stream.mjs b/ext/node/polyfills/internal/streams/end-of-stream.mjs new file mode 100644 index 0000000000..b5c380d56d --- /dev/null +++ b/ext/node/polyfills/internal/streams/end-of-stream.mjs @@ -0,0 +1,229 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { AbortError, ERR_STREAM_PREMATURE_CLOSE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { once } from "internal:deno_node/polyfills/internal/util.mjs"; +import { + validateAbortSignal, + validateFunction, + validateObject, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import * as process from "internal:deno_node/polyfills/_process/process.ts"; + +function isRequest(stream) { + return stream.setHeader && typeof stream.abort === "function"; +} + +function isServerResponse(stream) { + return ( + typeof stream._sent100 === "boolean" && + typeof stream._removedConnection === "boolean" && + typeof stream._removedContLen === "boolean" && + typeof stream._removedTE === "boolean" && + typeof stream._closed === "boolean" + ); +} + +function isReadable(stream) { + return typeof stream.readable === "boolean" || + typeof stream.readableEnded === "boolean" || + !!stream._readableState; +} + +function isWritable(stream) { + return typeof stream.writable === "boolean" || + typeof stream.writableEnded === "boolean" || + !!stream._writableState; +} + +function isWritableFinished(stream) { + if (stream.writableFinished) return true; + const wState = stream._writableState; + if (!wState || wState.errored) return false; + return wState.finished || (wState.ended && wState.length === 0); +} + +const nop = () => {}; + +function isReadableEnded(stream) { + if (stream.readableEnded) return true; + const rState = stream._readableState; + if (!rState || rState.errored) return false; + return rState.endEmitted || (rState.ended && rState.length === 0); +} + +function eos(stream, options, callback) { + if (arguments.length === 2) { + callback = options; + options = {}; + } else if (options == null) { + options = {}; + } else { + validateObject(options, "options"); + } + validateFunction(callback, "callback"); + validateAbortSignal(options.signal, "options.signal"); + + callback = once(callback); + + const readable = options.readable || + (options.readable !== false && isReadable(stream)); + const writable = options.writable || + (options.writable !== false && isWritable(stream)); + + const wState = stream._writableState; + const rState = stream._readableState; + const state = wState || rState; + + const onlegacyfinish = () => { + if (!stream.writable) onfinish(); + }; + + // TODO (ronag): Improve soft detection to include core modules and + // common ecosystem modules that do properly emit 'close' but fail + // this generic check. + let willEmitClose = isServerResponse(stream) || ( + state && + state.autoDestroy && + state.emitClose && + state.closed === false && + isReadable(stream) === readable && + isWritable(stream) === writable + ); + + let writableFinished = stream.writableFinished || + (wState && wState.finished); + const onfinish = () => { + writableFinished = true; + // Stream should not be destroyed here. If it is that + // means that user space is doing something differently and + // we cannot trust willEmitClose. + if (stream.destroyed) willEmitClose = false; + + if (willEmitClose && (!stream.readable || readable)) return; + if (!readable || readableEnded) callback.call(stream); + }; + + let readableEnded = stream.readableEnded || + (rState && rState.endEmitted); + const onend = () => { + readableEnded = true; + // Stream should not be destroyed here. If it is that + // means that user space is doing something differently and + // we cannot trust willEmitClose. + if (stream.destroyed) willEmitClose = false; + + if (willEmitClose && (!stream.writable || writable)) return; + if (!writable || writableFinished) callback.call(stream); + }; + + const onerror = (err) => { + callback.call(stream, err); + }; + + const onclose = () => { + if (readable && !readableEnded) { + if (!isReadableEnded(stream)) { + return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); + } + } + if (writable && !writableFinished) { + if (!isWritableFinished(stream)) { + return callback.call(stream, new ERR_STREAM_PREMATURE_CLOSE()); + } + } + callback.call(stream); + }; + + const onrequest = () => { + stream.req.on("finish", onfinish); + }; + + if (isRequest(stream)) { + stream.on("complete", onfinish); + if (!willEmitClose) { + stream.on("abort", onclose); + } + if (stream.req) onrequest(); + else stream.on("request", onrequest); + } else if (writable && !wState) { // legacy streams + stream.on("end", onlegacyfinish); + stream.on("close", onlegacyfinish); + } + + // Not all streams will emit 'close' after 'aborted'. + if (!willEmitClose && typeof stream.aborted === "boolean") { + stream.on("aborted", onclose); + } + + stream.on("end", onend); + stream.on("finish", onfinish); + if (options.error !== false) stream.on("error", onerror); + stream.on("close", onclose); + + // _closed is for OutgoingMessage which is not a proper Writable. + const closed = (!wState && !rState && stream._closed === true) || ( + (wState && wState.closed) || + (rState && rState.closed) || + (wState && wState.errorEmitted) || + (rState && rState.errorEmitted) || + (rState && stream.req && stream.aborted) || + ( + (!wState || !willEmitClose || typeof wState.closed !== "boolean") && + (!rState || !willEmitClose || typeof rState.closed !== "boolean") && + (!writable || (wState && wState.finished)) && + (!readable || (rState && rState.endEmitted)) + ) + ); + + if (closed) { + // TODO(ronag): Re-throw error if errorEmitted? + // TODO(ronag): Throw premature close as if finished was called? + // before being closed? i.e. if closed but not errored, ended or finished. + // TODO(ronag): Throw some kind of error? Does it make sense + // to call finished() on a "finished" stream? + // TODO(ronag): willEmitClose? + process.nextTick(() => { + callback(); + }); + } + + const cleanup = () => { + callback = nop; + stream.removeListener("aborted", onclose); + stream.removeListener("complete", onfinish); + stream.removeListener("abort", onclose); + stream.removeListener("request", onrequest); + if (stream.req) stream.req.removeListener("finish", onfinish); + stream.removeListener("end", onlegacyfinish); + stream.removeListener("close", onlegacyfinish); + stream.removeListener("finish", onfinish); + stream.removeListener("end", onend); + stream.removeListener("error", onerror); + stream.removeListener("close", onclose); + }; + + if (options.signal && !closed) { + const abort = () => { + // Keep it because cleanup removes it. + const endCallback = callback; + cleanup(); + endCallback.call(stream, new AbortError()); + }; + if (options.signal.aborted) { + process.nextTick(abort); + } else { + const originalCallback = callback; + callback = once((...args) => { + options.signal.removeEventListener("abort", abort); + originalCallback.apply(stream, args); + }); + options.signal.addEventListener("abort", abort); + } + } + + return cleanup; +} + +export default eos; diff --git a/ext/node/polyfills/internal/streams/lazy_transform.mjs b/ext/node/polyfills/internal/streams/lazy_transform.mjs new file mode 100644 index 0000000000..2bb93bd919 --- /dev/null +++ b/ext/node/polyfills/internal/streams/lazy_transform.mjs @@ -0,0 +1,53 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { getDefaultEncoding } from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import stream from "internal:deno_node/polyfills/stream.ts"; + +function LazyTransform(options) { + this._options = options; +} +Object.setPrototypeOf(LazyTransform.prototype, stream.Transform.prototype); +Object.setPrototypeOf(LazyTransform, stream.Transform); + +function makeGetter(name) { + return function () { + stream.Transform.call(this, this._options); + this._writableState.decodeStrings = false; + + if (!this._options || !this._options.defaultEncoding) { + this._writableState.defaultEncoding = getDefaultEncoding(); + } + + return this[name]; + }; +} + +function makeSetter(name) { + return function (val) { + Object.defineProperty(this, name, { + value: val, + enumerable: true, + configurable: true, + writable: true, + }); + }; +} + +Object.defineProperties(LazyTransform.prototype, { + _readableState: { + get: makeGetter("_readableState"), + set: makeSetter("_readableState"), + configurable: true, + enumerable: true, + }, + _writableState: { + get: makeGetter("_writableState"), + set: makeSetter("_writableState"), + configurable: true, + enumerable: true, + }, +}); + +export default LazyTransform; diff --git a/ext/node/polyfills/internal/streams/legacy.mjs b/ext/node/polyfills/internal/streams/legacy.mjs new file mode 100644 index 0000000000..0de18956f1 --- /dev/null +++ b/ext/node/polyfills/internal/streams/legacy.mjs @@ -0,0 +1,113 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import EE from "internal:deno_node/polyfills/events.ts"; + +function Stream(opts) { + EE.call(this, opts); +} +Object.setPrototypeOf(Stream.prototype, EE.prototype); +Object.setPrototypeOf(Stream, EE); + +Stream.prototype.pipe = function (dest, options) { + // deno-lint-ignore no-this-alias + const source = this; + + function ondata(chunk) { + if (dest.writable && dest.write(chunk) === false && source.pause) { + source.pause(); + } + } + + source.on("data", ondata); + + function ondrain() { + if (source.readable && source.resume) { + source.resume(); + } + } + + dest.on("drain", ondrain); + + // If the 'end' option is not supplied, dest.end() will be called when + // source gets the 'end' or 'close' events. Only dest.end() once. + if (!dest._isStdio && (!options || options.end !== false)) { + source.on("end", onend); + source.on("close", onclose); + } + + let didOnEnd = false; + function onend() { + if (didOnEnd) return; + didOnEnd = true; + + dest.end(); + } + + function onclose() { + if (didOnEnd) return; + didOnEnd = true; + + if (typeof dest.destroy === "function") dest.destroy(); + } + + // Don't leave dangling pipes when there are errors. + function onerror(er) { + cleanup(); + if (EE.listenerCount(this, "error") === 0) { + this.emit("error", er); + } + } + + prependListener(source, "error", onerror); + prependListener(dest, "error", onerror); + + // Remove all the event listeners that were added. + function cleanup() { + source.removeListener("data", ondata); + dest.removeListener("drain", ondrain); + + source.removeListener("end", onend); + source.removeListener("close", onclose); + + source.removeListener("error", onerror); + dest.removeListener("error", onerror); + + source.removeListener("end", cleanup); + source.removeListener("close", cleanup); + + dest.removeListener("close", cleanup); + } + + source.on("end", cleanup); + source.on("close", cleanup); + + dest.on("close", cleanup); + dest.emit("pipe", source); + + // Allow for unix-like usage: A.pipe(B).pipe(C) + return dest; +}; + +function prependListener(emitter, event, fn) { + // Sadly this is not cacheable as some libraries bundle their own + // event emitter implementation with them. + if (typeof emitter.prependListener === "function") { + return emitter.prependListener(event, fn); + } + + // This is a hack to make sure that our error handler is attached before any + // userland ones. NEVER DO THIS. This is here only because this code needs + // to continue to work with older versions of Node.js that do not include + // the prependListener() method. The goal is to eventually remove this hack. + if (!emitter._events || !emitter._events[event]) { + emitter.on(event, fn); + } else if (Array.isArray(emitter._events[event])) { + emitter._events[event].unshift(fn); + } else { + emitter._events[event] = [fn, emitter._events[event]]; + } +} + +export { prependListener, Stream }; diff --git a/ext/node/polyfills/internal/streams/passthrough.mjs b/ext/node/polyfills/internal/streams/passthrough.mjs new file mode 100644 index 0000000000..136a0484a5 --- /dev/null +++ b/ext/node/polyfills/internal/streams/passthrough.mjs @@ -0,0 +1,7 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { PassThrough } from "internal:deno_node/polyfills/_stream.mjs"; + +export default PassThrough; diff --git a/ext/node/polyfills/internal/streams/readable.mjs b/ext/node/polyfills/internal/streams/readable.mjs new file mode 100644 index 0000000000..36133d2970 --- /dev/null +++ b/ext/node/polyfills/internal/streams/readable.mjs @@ -0,0 +1,9 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { Readable } from "internal:deno_node/polyfills/_stream.mjs"; +const { ReadableState, _fromList, from, fromWeb, toWeb, wrap } = Readable; + +export default Readable; +export { _fromList, from, fromWeb, ReadableState, toWeb, wrap }; diff --git a/ext/node/polyfills/internal/streams/state.mjs b/ext/node/polyfills/internal/streams/state.mjs new file mode 100644 index 0000000000..93708fe9d0 --- /dev/null +++ b/ext/node/polyfills/internal/streams/state.mjs @@ -0,0 +1,10 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +function getDefaultHighWaterMark(objectMode) { + return objectMode ? 16 : 16 * 1024; +} + +export default { getDefaultHighWaterMark }; +export { getDefaultHighWaterMark }; diff --git a/ext/node/polyfills/internal/streams/transform.mjs b/ext/node/polyfills/internal/streams/transform.mjs new file mode 100644 index 0000000000..3fc4fa5cdc --- /dev/null +++ b/ext/node/polyfills/internal/streams/transform.mjs @@ -0,0 +1,7 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { Transform } from "internal:deno_node/polyfills/_stream.mjs"; + +export default Transform; diff --git a/ext/node/polyfills/internal/streams/utils.mjs b/ext/node/polyfills/internal/streams/utils.mjs new file mode 100644 index 0000000000..a575f831d3 --- /dev/null +++ b/ext/node/polyfills/internal/streams/utils.mjs @@ -0,0 +1,242 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +const kIsDisturbed = Symbol("kIsDisturbed"); + +function isReadableNodeStream(obj) { + return !!( + obj && + typeof obj.pipe === "function" && + typeof obj.on === "function" && + (!obj._writableState || obj._readableState?.readable !== false) && // Duplex + (!obj._writableState || obj._readableState) // Writable has .pipe. + ); +} + +function isWritableNodeStream(obj) { + return !!( + obj && + typeof obj.write === "function" && + typeof obj.on === "function" && + (!obj._readableState || obj._writableState?.writable !== false) // Duplex + ); +} + +function isDuplexNodeStream(obj) { + return !!( + obj && + (typeof obj.pipe === "function" && obj._readableState) && + typeof obj.on === "function" && + typeof obj.write === "function" + ); +} + +function isNodeStream(obj) { + return ( + obj && + ( + obj._readableState || + obj._writableState || + (typeof obj.write === "function" && typeof obj.on === "function") || + (typeof obj.pipe === "function" && typeof obj.on === "function") + ) + ); +} + +function isDestroyed(stream) { + if (!isNodeStream(stream)) return null; + const wState = stream._writableState; + const rState = stream._readableState; + const state = wState || rState; + return !!(stream.destroyed || state?.destroyed); +} + +// Have been end():d. +function isWritableEnded(stream) { + if (!isWritableNodeStream(stream)) return null; + if (stream.writableEnded === true) return true; + const wState = stream._writableState; + if (wState?.errored) return false; + if (typeof wState?.ended !== "boolean") return null; + return wState.ended; +} + +// Have emitted 'finish'. +function isWritableFinished(stream, strict) { + if (!isWritableNodeStream(stream)) return null; + if (stream.writableFinished === true) return true; + const wState = stream._writableState; + if (wState?.errored) return false; + if (typeof wState?.finished !== "boolean") return null; + return !!( + wState.finished || + (strict === false && wState.ended === true && wState.length === 0) + ); +} + +// Have been push(null):d. +function isReadableEnded(stream) { + if (!isReadableNodeStream(stream)) return null; + if (stream.readableEnded === true) return true; + const rState = stream._readableState; + if (!rState || rState.errored) return false; + if (typeof rState?.ended !== "boolean") return null; + return rState.ended; +} + +// Have emitted 'end'. +function isReadableFinished(stream, strict) { + if (!isReadableNodeStream(stream)) return null; + const rState = stream._readableState; + if (rState?.errored) return false; + if (typeof rState?.endEmitted !== "boolean") return null; + return !!( + rState.endEmitted || + (strict === false && rState.ended === true && rState.length === 0) + ); +} + +function isDisturbed(stream) { + return !!(stream && ( + stream.readableDidRead || + stream.readableAborted || + stream[kIsDisturbed] + )); +} + +function isReadable(stream) { + const r = isReadableNodeStream(stream); + if (r === null || typeof stream?.readable !== "boolean") return null; + if (isDestroyed(stream)) return false; + return r && stream.readable && !isReadableFinished(stream); +} + +function isWritable(stream) { + const r = isWritableNodeStream(stream); + if (r === null || typeof stream?.writable !== "boolean") return null; + if (isDestroyed(stream)) return false; + return r && stream.writable && !isWritableEnded(stream); +} + +function isFinished(stream, opts) { + if (!isNodeStream(stream)) { + return null; + } + + if (isDestroyed(stream)) { + return true; + } + + if (opts?.readable !== false && isReadable(stream)) { + return false; + } + + if (opts?.writable !== false && isWritable(stream)) { + return false; + } + + return true; +} + +function isClosed(stream) { + if (!isNodeStream(stream)) { + return null; + } + + const wState = stream._writableState; + const rState = stream._readableState; + + if ( + typeof wState?.closed === "boolean" || + typeof rState?.closed === "boolean" + ) { + return wState?.closed || rState?.closed; + } + + if (typeof stream._closed === "boolean" && isOutgoingMessage(stream)) { + return stream._closed; + } + + return null; +} + +function isOutgoingMessage(stream) { + return ( + typeof stream._closed === "boolean" && + typeof stream._defaultKeepAlive === "boolean" && + typeof stream._removedConnection === "boolean" && + typeof stream._removedContLen === "boolean" + ); +} + +function isServerResponse(stream) { + return ( + typeof stream._sent100 === "boolean" && + isOutgoingMessage(stream) + ); +} + +function isServerRequest(stream) { + return ( + typeof stream._consuming === "boolean" && + typeof stream._dumped === "boolean" && + stream.req?.upgradeOrConnect === undefined + ); +} + +function willEmitClose(stream) { + if (!isNodeStream(stream)) return null; + + const wState = stream._writableState; + const rState = stream._readableState; + const state = wState || rState; + + return (!state && isServerResponse(stream)) || !!( + state && + state.autoDestroy && + state.emitClose && + state.closed === false + ); +} + +export default { + isDisturbed, + kIsDisturbed, + isClosed, + isDestroyed, + isDuplexNodeStream, + isFinished, + isReadable, + isReadableNodeStream, + isReadableEnded, + isReadableFinished, + isNodeStream, + isWritable, + isWritableNodeStream, + isWritableEnded, + isWritableFinished, + isServerRequest, + isServerResponse, + willEmitClose, +}; +export { + isClosed, + isDestroyed, + isDisturbed, + isDuplexNodeStream, + isFinished, + isNodeStream, + isReadable, + isReadableEnded, + isReadableFinished, + isReadableNodeStream, + isServerRequest, + isServerResponse, + isWritable, + isWritableEnded, + isWritableFinished, + isWritableNodeStream, + kIsDisturbed, + willEmitClose, +}; diff --git a/ext/node/polyfills/internal/streams/writable.mjs b/ext/node/polyfills/internal/streams/writable.mjs new file mode 100644 index 0000000000..6f4d77960f --- /dev/null +++ b/ext/node/polyfills/internal/streams/writable.mjs @@ -0,0 +1,9 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +// deno-lint-ignore-file + +import { Writable } from "internal:deno_node/polyfills/_stream.mjs"; +const { WritableState, fromWeb, toWeb } = Writable; + +export default Writable; +export { fromWeb, toWeb, WritableState }; diff --git a/ext/node/polyfills/internal/test/binding.ts b/ext/node/polyfills/internal/test/binding.ts new file mode 100644 index 0000000000..996cc57aaa --- /dev/null +++ b/ext/node/polyfills/internal/test/binding.ts @@ -0,0 +1,16 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. +import { getBinding } from "internal:deno_node/polyfills/internal_binding/mod.ts"; +import type { BindingName } from "internal:deno_node/polyfills/internal_binding/mod.ts"; + +export function internalBinding(name: BindingName) { + return getBinding(name); +} + +// TODO(kt3k): export actual primordials +export const primordials = {}; + +export default { + internalBinding, + primordials, +}; diff --git a/ext/node/polyfills/internal/timers.mjs b/ext/node/polyfills/internal/timers.mjs new file mode 100644 index 0000000000..648fb1bc14 --- /dev/null +++ b/ext/node/polyfills/internal/timers.mjs @@ -0,0 +1,125 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { inspect } from "internal:deno_node/polyfills/internal/util/inspect.mjs"; +import { validateFunction, validateNumber } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { ERR_OUT_OF_RANGE } from "internal:deno_node/polyfills/internal/errors.ts"; +import { emitWarning } from "internal:deno_node/polyfills/process.ts"; + +const setTimeout_ = globalThis.setTimeout; +const clearTimeout_ = globalThis.clearTimeout; +const setInterval_ = globalThis.setInterval; + +// Timeout values > TIMEOUT_MAX are set to 1. +export const TIMEOUT_MAX = 2 ** 31 - 1; + +export const kTimerId = Symbol("timerId"); +export const kTimeout = Symbol("timeout"); +const kRefed = Symbol("refed"); +const createTimer = Symbol("createTimer"); + +// Timer constructor function. +export function Timeout(callback, after, args, isRepeat, isRefed) { + if (typeof after === "number" && after > TIMEOUT_MAX) { + after = 1; + } + this._idleTimeout = after; + this._onTimeout = callback; + this._timerArgs = args; + this._isRepeat = isRepeat; + this[kRefed] = isRefed; + this[kTimerId] = this[createTimer](); +} + +Timeout.prototype[createTimer] = function () { + const callback = this._onTimeout; + const cb = (...args) => callback.bind(this)(...args); + const id = this._isRepeat + ? setInterval_(cb, this._idleTimeout, ...this._timerArgs) + : setTimeout_(cb, this._idleTimeout, ...this._timerArgs); + if (!this[kRefed]) { + Deno.unrefTimer(id); + } + return id; +}; + +// Make sure the linked list only shows the minimal necessary information. +Timeout.prototype[inspect.custom] = function (_, options) { + return inspect(this, { + ...options, + // Only inspect one level. + depth: 0, + // It should not recurse. + customInspect: false, + }); +}; + +Timeout.prototype.refresh = function () { + clearTimeout_(this[kTimerId]); + this[kTimerId] = this[createTimer](); + return this; +}; + +Timeout.prototype.unref = function () { + if (this[kRefed]) { + this[kRefed] = false; + Deno.unrefTimer(this[kTimerId]); + } + return this; +}; + +Timeout.prototype.ref = function () { + if (!this[kRefed]) { + this[kRefed] = true; + Deno.refTimer(this[kTimerId]); + } + return this; +}; + +Timeout.prototype.hasRef = function () { + return this[kRefed]; +}; + +Timeout.prototype[Symbol.toPrimitive] = function () { + return this[kTimerId]; +}; + +/** + * @param {number} msecs + * @param {string} name + * @returns + */ +export function getTimerDuration(msecs, name) { + validateNumber(msecs, name); + + if (msecs < 0 || !Number.isFinite(msecs)) { + throw new ERR_OUT_OF_RANGE(name, "a non-negative finite number", msecs); + } + + // Ensure that msecs fits into signed int32 + if (msecs > TIMEOUT_MAX) { + emitWarning( + `${msecs} does not fit into a 32-bit signed integer.` + + `\nTimer duration was truncated to ${TIMEOUT_MAX}.`, + "TimeoutOverflowWarning", + ); + + return TIMEOUT_MAX; + } + + return msecs; +} + +export function setUnrefTimeout(callback, timeout, ...args) { + validateFunction(callback, "callback"); + return new Timeout(callback, timeout, args, false, false); +} + +export default { + getTimerDuration, + kTimerId, + kTimeout, + setUnrefTimeout, + Timeout, + TIMEOUT_MAX, +}; diff --git a/ext/node/polyfills/internal/url.ts b/ext/node/polyfills/internal/url.ts new file mode 100644 index 0000000000..415ad9be6e --- /dev/null +++ b/ext/node/polyfills/internal/url.ts @@ -0,0 +1,49 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { fileURLToPath } from "internal:deno_node/polyfills/url.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +const searchParams = Symbol("query"); + +export function toPathIfFileURL( + fileURLOrPath: string | Buffer | URL, +): string | Buffer { + if (!(fileURLOrPath instanceof URL)) { + return fileURLOrPath; + } + return fileURLToPath(fileURLOrPath); +} + +// Utility function that converts a URL object into an ordinary +// options object as expected by the http.request and https.request +// APIs. +// deno-lint-ignore no-explicit-any +export function urlToHttpOptions(url: any): any { + // deno-lint-ignore no-explicit-any + const options: any = { + protocol: url.protocol, + hostname: typeof url.hostname === "string" && + url.hostname.startsWith("[") + ? url.hostname.slice(1, -1) + : url.hostname, + hash: url.hash, + search: url.search, + pathname: url.pathname, + path: `${url.pathname || ""}${url.search || ""}`, + href: url.href, + }; + if (url.port !== "") { + options.port = Number(url.port); + } + if (url.username || url.password) { + options.auth = `${decodeURIComponent(url.username)}:${ + decodeURIComponent(url.password) + }`; + } + return options; +} + +export { searchParams as searchParamsSymbol }; + +export default { + toPathIfFileURL, +}; diff --git a/ext/node/polyfills/internal/util.mjs b/ext/node/polyfills/internal/util.mjs new file mode 100644 index 0000000000..ba26c6a6a0 --- /dev/null +++ b/ext/node/polyfills/internal/util.mjs @@ -0,0 +1,141 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { normalizeEncoding, slowCases } from "internal:deno_node/polyfills/internal/normalize_encoding.mjs"; +export { normalizeEncoding, slowCases }; +import { ObjectCreate, StringPrototypeToUpperCase } from "internal:deno_node/polyfills/internal/primordials.mjs"; +import { ERR_UNKNOWN_SIGNAL } from "internal:deno_node/polyfills/internal/errors.ts"; +import { os } from "internal:deno_node/polyfills/internal_binding/constants.ts"; + +export const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom"); +export const kEnumerableProperty = Object.create(null); +kEnumerableProperty.enumerable = true; + +export const kEmptyObject = Object.freeze(Object.create(null)); + +export function once(callback) { + let called = false; + return function (...args) { + if (called) return; + called = true; + Reflect.apply(callback, this, args); + }; +} + +export function createDeferredPromise() { + let resolve; + let reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + + return { promise, resolve, reject }; +} + +// In addition to being accessible through util.promisify.custom, +// this symbol is registered globally and can be accessed in any environment as +// Symbol.for('nodejs.util.promisify.custom'). +const kCustomPromisifiedSymbol = Symbol.for("nodejs.util.promisify.custom"); +// This is an internal Node symbol used by functions returning multiple +// arguments, e.g. ['bytesRead', 'buffer'] for fs.read(). +const kCustomPromisifyArgsSymbol = Symbol.for( + "nodejs.util.promisify.customArgs", +); + +export const customPromisifyArgs = kCustomPromisifyArgsSymbol; + +export function promisify( + original, +) { + validateFunction(original, "original"); + if (original[kCustomPromisifiedSymbol]) { + const fn = original[kCustomPromisifiedSymbol]; + + validateFunction(fn, "util.promisify.custom"); + + return Object.defineProperty(fn, kCustomPromisifiedSymbol, { + value: fn, + enumerable: false, + writable: false, + configurable: true, + }); + } + + // Names to create an object from in case the callback receives multiple + // arguments, e.g. ['bytesRead', 'buffer'] for fs.read. + const argumentNames = original[kCustomPromisifyArgsSymbol]; + function fn(...args) { + return new Promise((resolve, reject) => { + args.push((err, ...values) => { + if (err) { + return reject(err); + } + if (argumentNames !== undefined && values.length > 1) { + const obj = {}; + for (let i = 0; i < argumentNames.length; i++) { + obj[argumentNames[i]] = values[i]; + } + resolve(obj); + } else { + resolve(values[0]); + } + }); + Reflect.apply(original, this, args); + }); + } + + Object.setPrototypeOf(fn, Object.getPrototypeOf(original)); + + Object.defineProperty(fn, kCustomPromisifiedSymbol, { + value: fn, + enumerable: false, + writable: false, + configurable: true, + }); + return Object.defineProperties( + fn, + Object.getOwnPropertyDescriptors(original), + ); +} + +let signalsToNamesMapping; +function getSignalsToNamesMapping() { + if (signalsToNamesMapping !== undefined) { + return signalsToNamesMapping; + } + + signalsToNamesMapping = ObjectCreate(null); + for (const key in os.signals) { + signalsToNamesMapping[os.signals[key]] = key; + } + + return signalsToNamesMapping; +} + +export function convertToValidSignal(signal) { + if (typeof signal === "number" && getSignalsToNamesMapping()[signal]) { + return signal; + } + + if (typeof signal === "string") { + const signalName = os.signals[StringPrototypeToUpperCase(signal)]; + if (signalName) return signalName; + } + + throw new ERR_UNKNOWN_SIGNAL(signal); +} + +promisify.custom = kCustomPromisifiedSymbol; + +export default { + convertToValidSignal, + createDeferredPromise, + customInspectSymbol, + customPromisifyArgs, + kEmptyObject, + kEnumerableProperty, + normalizeEncoding, + once, + promisify, + slowCases, +}; diff --git a/ext/node/polyfills/internal/util/comparisons.ts b/ext/node/polyfills/internal/util/comparisons.ts new file mode 100644 index 0000000000..1620e468b5 --- /dev/null +++ b/ext/node/polyfills/internal/util/comparisons.ts @@ -0,0 +1,681 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +// deno-lint-ignore-file +import { + isAnyArrayBuffer, + isArrayBufferView, + isBigIntObject, + isBooleanObject, + isBoxedPrimitive, + isDate, + isFloat32Array, + isFloat64Array, + isMap, + isNativeError, + isNumberObject, + isRegExp, + isSet, + isStringObject, + isSymbolObject, + isTypedArray, +} from "internal:deno_node/polyfills/internal/util/types.ts"; + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + getOwnNonIndexProperties, + ONLY_ENUMERABLE, + SKIP_SYMBOLS, +} from "internal:deno_node/polyfills/internal_binding/util.ts"; + +enum valueType { + noIterator, + isArray, + isSet, + isMap, +} + +interface Memo { + val1: Map; + val2: Map; + position: number; +} +let memo: Memo; + +export function isDeepStrictEqual(val1: unknown, val2: unknown): boolean { + return innerDeepEqual(val1, val2, true); +} +export function isDeepEqual(val1: unknown, val2: unknown): boolean { + return innerDeepEqual(val1, val2, false); +} + +function innerDeepEqual( + val1: unknown, + val2: unknown, + strict: boolean, + memos = memo, +): boolean { + // Basic case covered by Strict Equality Comparison + if (val1 === val2) { + if (val1 !== 0) return true; + return strict ? Object.is(val1, val2) : true; + } + if (strict) { + // Cases where the values are not objects + // If both values are Not a Number NaN + if (typeof val1 !== "object") { + return ( + typeof val1 === "number" && Number.isNaN(val1) && Number.isNaN(val2) + ); + } + // If either value is null + if (typeof val2 !== "object" || val1 === null || val2 === null) { + return false; + } + // If the prototype are not the same + if (Object.getPrototypeOf(val1) !== Object.getPrototypeOf(val2)) { + return false; + } + } else { + // Non strict case where values are either null or NaN + if (val1 === null || typeof val1 !== "object") { + if (val2 === null || typeof val2 !== "object") { + return val1 == val2 || (Number.isNaN(val1) && Number.isNaN(val2)); + } + return false; + } + if (val2 === null || typeof val2 !== "object") { + return false; + } + } + + const val1Tag = Object.prototype.toString.call(val1); + const val2Tag = Object.prototype.toString.call(val2); + + // prototype must be Strictly Equal + if ( + val1Tag !== val2Tag + ) { + return false; + } + + // handling when values are array + if (Array.isArray(val1)) { + // quick rejection cases + if (!Array.isArray(val2) || val1.length !== val2.length) { + return false; + } + const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS; + const keys1 = getOwnNonIndexProperties(val1, filter); + const keys2 = getOwnNonIndexProperties(val2, filter); + if (keys1.length !== keys2.length) { + return false; + } + return keyCheck(val1, val2, strict, memos, valueType.isArray, keys1); + } else if (val1Tag === "[object Object]") { + return keyCheck( + val1 as object, + val2 as object, + strict, + memos, + valueType.noIterator, + ); + } else if (val1 instanceof Date) { + if (!(val2 instanceof Date) || val1.getTime() !== val2.getTime()) { + return false; + } + } else if (val1 instanceof RegExp) { + if (!(val2 instanceof RegExp) || !areSimilarRegExps(val1, val2)) { + return false; + } + } else if (isNativeError(val1) || val1 instanceof Error) { + // stack may or may not be same, hence it shouldn't be compared + if ( + // How to handle the type errors here + (!isNativeError(val2) && !(val2 instanceof Error)) || + (val1 as Error).message !== (val2 as Error).message || + (val1 as Error).name !== (val2 as Error).name + ) { + return false; + } + } else if (isArrayBufferView(val1)) { + const TypedArrayPrototypeGetSymbolToStringTag = ( + val: + | BigInt64Array + | BigUint64Array + | Float32Array + | Float64Array + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array, + ) => + Object.getOwnPropertySymbols(val) + .map((item) => item.toString()) + .toString(); + if ( + isTypedArray(val1) && + isTypedArray(val2) && + (TypedArrayPrototypeGetSymbolToStringTag(val1) !== + TypedArrayPrototypeGetSymbolToStringTag(val2)) + ) { + return false; + } + + if (!strict && (isFloat32Array(val1) || isFloat64Array(val1))) { + if (!areSimilarFloatArrays(val1, val2)) { + return false; + } + } else if (!areSimilarTypedArrays(val1, val2)) { + return false; + } + const filter = strict ? ONLY_ENUMERABLE : ONLY_ENUMERABLE | SKIP_SYMBOLS; + const keysVal1 = getOwnNonIndexProperties(val1 as object, filter); + const keysVal2 = getOwnNonIndexProperties(val2 as object, filter); + if (keysVal1.length !== keysVal2.length) { + return false; + } + return keyCheck( + val1 as object, + val2 as object, + strict, + memos, + valueType.noIterator, + keysVal1, + ); + } else if (isSet(val1)) { + if ( + !isSet(val2) || + (val1 as Set).size !== (val2 as Set).size + ) { + return false; + } + return keyCheck( + val1 as object, + val2 as object, + strict, + memos, + valueType.isSet, + ); + } else if (isMap(val1)) { + if ( + !isMap(val2) || val1.size !== val2.size + ) { + return false; + } + return keyCheck( + val1 as object, + val2 as object, + strict, + memos, + valueType.isMap, + ); + } else if (isAnyArrayBuffer(val1)) { + if (!isAnyArrayBuffer(val2) || !areEqualArrayBuffers(val1, val2)) { + return false; + } + } else if (isBoxedPrimitive(val1)) { + if (!isEqualBoxedPrimitive(val1, val2)) { + return false; + } + } else if ( + Array.isArray(val2) || + isArrayBufferView(val2) || + isSet(val2) || + isMap(val2) || + isDate(val2) || + isRegExp(val2) || + isAnyArrayBuffer(val2) || + isBoxedPrimitive(val2) || + isNativeError(val2) || + val2 instanceof Error + ) { + return false; + } + return keyCheck( + val1 as object, + val2 as object, + strict, + memos, + valueType.noIterator, + ); +} + +function keyCheck( + val1: object, + val2: object, + strict: boolean, + memos: Memo, + iterationType: valueType, + aKeys: (string | symbol)[] = [], +) { + if (arguments.length === 5) { + aKeys = Object.keys(val1); + const bKeys = Object.keys(val2); + + // The pair must have the same number of owned properties. + if (aKeys.length !== bKeys.length) { + return false; + } + } + + // Cheap key test + let i = 0; + for (; i < aKeys.length; i++) { + if (!val2.propertyIsEnumerable(aKeys[i])) { + return false; + } + } + + if (strict && arguments.length === 5) { + const symbolKeysA = Object.getOwnPropertySymbols(val1); + if (symbolKeysA.length !== 0) { + let count = 0; + for (i = 0; i < symbolKeysA.length; i++) { + const key = symbolKeysA[i]; + if (val1.propertyIsEnumerable(key)) { + if (!val2.propertyIsEnumerable(key)) { + return false; + } + // added toString here + aKeys.push(key.toString()); + count++; + } else if (val2.propertyIsEnumerable(key)) { + return false; + } + } + const symbolKeysB = Object.getOwnPropertySymbols(val2); + if ( + symbolKeysA.length !== symbolKeysB.length && + getEnumerables(val2, symbolKeysB).length !== count + ) { + return false; + } + } else { + const symbolKeysB = Object.getOwnPropertySymbols(val2); + if ( + symbolKeysB.length !== 0 && + getEnumerables(val2, symbolKeysB).length !== 0 + ) { + return false; + } + } + } + if ( + aKeys.length === 0 && + (iterationType === valueType.noIterator || + (iterationType === valueType.isArray && (val1 as []).length === 0) || + (val1 as Set).size === 0) + ) { + return true; + } + + if (memos === undefined) { + memos = { + val1: new Map(), + val2: new Map(), + position: 0, + }; + } else { + const val2MemoA = memos.val1.get(val1); + if (val2MemoA !== undefined) { + const val2MemoB = memos.val2.get(val2); + if (val2MemoB !== undefined) { + return val2MemoA === val2MemoB; + } + } + memos.position++; + } + + memos.val1.set(val1, memos.position); + memos.val2.set(val2, memos.position); + + const areEq = objEquiv(val1, val2, strict, aKeys, memos, iterationType); + + memos.val1.delete(val1); + memos.val2.delete(val2); + + return areEq; +} + +function areSimilarRegExps(a: RegExp, b: RegExp) { + return a.source === b.source && a.flags === b.flags && + a.lastIndex === b.lastIndex; +} + +// TODO(standvpmnt): add type for arguments +function areSimilarFloatArrays(arr1: any, arr2: any): boolean { + if (arr1.byteLength !== arr2.byteLength) { + return false; + } + for (let i = 0; i < arr1.byteLength; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + return true; +} + +// TODO(standvpmnt): add type for arguments +function areSimilarTypedArrays(arr1: any, arr2: any): boolean { + if (arr1.byteLength !== arr2.byteLength) { + return false; + } + return ( + Buffer.compare( + new Uint8Array(arr1.buffer, arr1.byteOffset, arr1.byteLength), + new Uint8Array(arr2.buffer, arr2.byteOffset, arr2.byteLength), + ) === 0 + ); +} +// TODO(standvpmnt): add type for arguments +function areEqualArrayBuffers(buf1: any, buf2: any): boolean { + return ( + buf1.byteLength === buf2.byteLength && + Buffer.compare(new Uint8Array(buf1), new Uint8Array(buf2)) === 0 + ); +} + +// TODO(standvpmnt): this check of getOwnPropertySymbols and getOwnPropertyNames +// length is sufficient to handle the current test case, however this will fail +// to catch a scenario wherein the getOwnPropertySymbols and getOwnPropertyNames +// length is the same(will be very contrived but a possible shortcoming +function isEqualBoxedPrimitive(a: any, b: any): boolean { + if ( + Object.getOwnPropertyNames(a).length !== + Object.getOwnPropertyNames(b).length + ) { + return false; + } + if ( + Object.getOwnPropertySymbols(a).length !== + Object.getOwnPropertySymbols(b).length + ) { + return false; + } + if (isNumberObject(a)) { + return ( + isNumberObject(b) && + Object.is( + Number.prototype.valueOf.call(a), + Number.prototype.valueOf.call(b), + ) + ); + } + if (isStringObject(a)) { + return ( + isStringObject(b) && + (String.prototype.valueOf.call(a) === String.prototype.valueOf.call(b)) + ); + } + if (isBooleanObject(a)) { + return ( + isBooleanObject(b) && + (Boolean.prototype.valueOf.call(a) === Boolean.prototype.valueOf.call(b)) + ); + } + if (isBigIntObject(a)) { + return ( + isBigIntObject(b) && + (BigInt.prototype.valueOf.call(a) === BigInt.prototype.valueOf.call(b)) + ); + } + if (isSymbolObject(a)) { + return ( + isSymbolObject(b) && + (Symbol.prototype.valueOf.call(a) === + Symbol.prototype.valueOf.call(b)) + ); + } + // assert.fail(`Unknown boxed type ${val1}`); + // return false; + throw Error(`Unknown boxed type`); +} + +function getEnumerables(val: any, keys: any) { + return keys.filter((key: string) => val.propertyIsEnumerable(key)); +} + +function objEquiv( + obj1: any, + obj2: any, + strict: boolean, + keys: any, + memos: Memo, + iterationType: valueType, +): boolean { + let i = 0; + + if (iterationType === valueType.isSet) { + if (!setEquiv(obj1, obj2, strict, memos)) { + return false; + } + } else if (iterationType === valueType.isMap) { + if (!mapEquiv(obj1, obj2, strict, memos)) { + return false; + } + } else if (iterationType === valueType.isArray) { + for (; i < obj1.length; i++) { + if (obj1.hasOwnProperty(i)) { + if ( + !obj2.hasOwnProperty(i) || + !innerDeepEqual(obj1[i], obj2[i], strict, memos) + ) { + return false; + } + } else if (obj2.hasOwnProperty(i)) { + return false; + } else { + const keys1 = Object.keys(obj1); + for (; i < keys1.length; i++) { + const key = keys1[i]; + if ( + !obj2.hasOwnProperty(key) || + !innerDeepEqual(obj1[key], obj2[key], strict, memos) + ) { + return false; + } + } + if (keys1.length !== Object.keys(obj2).length) { + return false; + } + if (keys1.length !== Object.keys(obj2).length) { + return false; + } + return true; + } + } + } + + // Expensive test + for (i = 0; i < keys.length; i++) { + const key = keys[i]; + if (!innerDeepEqual(obj1[key], obj2[key], strict, memos)) { + return false; + } + } + return true; +} + +function findLooseMatchingPrimitives( + primitive: unknown, +): boolean | null | undefined { + switch (typeof primitive) { + case "undefined": + return null; + case "object": + return undefined; + case "symbol": + return false; + case "string": + primitive = +primitive; + case "number": + if (Number.isNaN(primitive)) { + return false; + } + } + return true; +} + +function setMightHaveLoosePrim( + set1: Set, + set2: Set, + primitive: any, +) { + const altValue = findLooseMatchingPrimitives(primitive); + if (altValue != null) return altValue; + + return set2.has(altValue) && !set1.has(altValue); +} + +function setHasEqualElement( + set: any, + val1: any, + strict: boolean, + memos: Memo, +): boolean { + for (const val2 of set) { + if (innerDeepEqual(val1, val2, strict, memos)) { + set.delete(val2); + return true; + } + } + + return false; +} + +function setEquiv(set1: any, set2: any, strict: boolean, memos: Memo): boolean { + let set = null; + for (const item of set1) { + if (typeof item === "object" && item !== null) { + if (set === null) { + // What is SafeSet from primordials? + // set = new SafeSet(); + set = new Set(); + } + set.add(item); + } else if (!set2.has(item)) { + if (strict) return false; + + if (!setMightHaveLoosePrim(set1, set2, item)) { + return false; + } + + if (set === null) { + set = new Set(); + } + set.add(item); + } + } + + if (set !== null) { + for (const item of set2) { + if (typeof item === "object" && item !== null) { + if (!setHasEqualElement(set, item, strict, memos)) return false; + } else if ( + !strict && + !set1.has(item) && + !setHasEqualElement(set, item, strict, memos) + ) { + return false; + } + } + return set.size === 0; + } + + return true; +} + +// TODO(standvpmnt): add types for argument +function mapMightHaveLoosePrimitive( + map1: Map, + map2: Map, + primitive: any, + item: any, + memos: Memo, +): boolean { + const altValue = findLooseMatchingPrimitives(primitive); + if (altValue != null) { + return altValue; + } + const curB = map2.get(altValue); + if ( + (curB === undefined && !map2.has(altValue)) || + !innerDeepEqual(item, curB, false, memo) + ) { + return false; + } + return !map1.has(altValue) && innerDeepEqual(item, curB, false, memos); +} + +function mapEquiv(map1: any, map2: any, strict: boolean, memos: Memo): boolean { + let set = null; + + for (const { 0: key, 1: item1 } of map1) { + if (typeof key === "object" && key !== null) { + if (set === null) { + set = new Set(); + } + set.add(key); + } else { + const item2 = map2.get(key); + if ( + ( + (item2 === undefined && !map2.has(key)) || + !innerDeepEqual(item1, item2, strict, memos) + ) + ) { + if (strict) return false; + if (!mapMightHaveLoosePrimitive(map1, map2, key, item1, memos)) { + return false; + } + if (set === null) { + set = new Set(); + } + set.add(key); + } + } + } + + if (set !== null) { + for (const { 0: key, 1: item } of map2) { + if (typeof key === "object" && key !== null) { + if (!mapHasEqualEntry(set, map1, key, item, strict, memos)) { + return false; + } + } else if ( + !strict && (!map1.has(key) || + !innerDeepEqual(map1.get(key), item, false, memos)) && + !mapHasEqualEntry(set, map1, key, item, false, memos) + ) { + return false; + } + } + return set.size === 0; + } + + return true; +} + +function mapHasEqualEntry( + set: any, + map: any, + key1: any, + item1: any, + strict: boolean, + memos: Memo, +): boolean { + for (const key2 of set) { + if ( + innerDeepEqual(key1, key2, strict, memos) && + innerDeepEqual(item1, map.get(key2), strict, memos) + ) { + set.delete(key2); + return true; + } + } + return false; +} diff --git a/ext/node/polyfills/internal/util/debuglog.ts b/ext/node/polyfills/internal/util/debuglog.ts new file mode 100644 index 0000000000..498facbd15 --- /dev/null +++ b/ext/node/polyfills/internal/util/debuglog.ts @@ -0,0 +1,121 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. +import { inspect } from "internal:deno_node/polyfills/internal/util/inspect.mjs"; + +// `debugImpls` and `testEnabled` are deliberately not initialized so any call +// to `debuglog()` before `initializeDebugEnv()` is called will throw. +let debugImpls: Record void>; +let testEnabled: (str: string) => boolean; + +// `debugEnv` is initial value of process.env.NODE_DEBUG +function initializeDebugEnv(debugEnv: string) { + debugImpls = Object.create(null); + if (debugEnv) { + // This is run before any user code, it's OK not to use primordials. + debugEnv = debugEnv.replace(/[|\\{}()[\]^$+?.]/g, "\\$&") + .replaceAll("*", ".*") + .replaceAll(",", "$|^"); + const debugEnvRegex = new RegExp(`^${debugEnv}$`, "i"); + testEnabled = (str) => debugEnvRegex.exec(str) !== null; + } else { + testEnabled = () => false; + } +} + +// Emits warning when user sets +// NODE_DEBUG=http or NODE_DEBUG=http2. +function emitWarningIfNeeded(set: string) { + if ("HTTP" === set || "HTTP2" === set) { + console.warn( + "Setting the NODE_DEBUG environment variable " + + "to '" + set.toLowerCase() + "' can expose sensitive " + + "data (such as passwords, tokens and authentication headers) " + + "in the resulting log.", + ); + } +} + +const noop = () => {}; + +function debuglogImpl( + enabled: boolean, + set: string, +): (...args: unknown[]) => void { + if (debugImpls[set] === undefined) { + if (enabled) { + emitWarningIfNeeded(set); + debugImpls[set] = function debug(...args: unknown[]) { + const msg = args.map((arg) => inspect(arg)).join(" "); + console.error("%s %s: %s", set, String(Deno.pid), msg); + }; + } else { + debugImpls[set] = noop; + } + } + + return debugImpls[set]; +} + +// debuglogImpl depends on process.pid and process.env.NODE_DEBUG, +// so it needs to be called lazily in top scopes of internal modules +// that may be loaded before these run time states are allowed to +// be accessed. +export function debuglog( + set: string, + cb?: (debug: (...args: unknown[]) => void) => void, +) { + function init() { + set = set.toUpperCase(); + enabled = testEnabled(set); + } + + let debug = (...args: unknown[]): void => { + init(); + // Only invokes debuglogImpl() when the debug function is + // called for the first time. + debug = debuglogImpl(enabled, set); + + if (typeof cb === "function") { + cb(debug); + } + + return debug(...args); + }; + + let enabled: boolean; + let test = () => { + init(); + test = () => enabled; + return enabled; + }; + + const logger = (...args: unknown[]) => debug(...args); + + Object.defineProperty(logger, "enabled", { + get() { + return test(); + }, + configurable: true, + enumerable: true, + }); + + return logger; +} + +let debugEnv; +/* TODO(kt3k): enable initializing debugEnv. +It's not possible to access env var when snapshotting. + +try { + debugEnv = Deno.env.get("NODE_DEBUG") ?? ""; +} catch (error) { + if (error instanceof Deno.errors.PermissionDenied) { + debugEnv = ""; + } else { + throw error; + } +} +*/ +initializeDebugEnv(debugEnv); + +export default { debuglog }; diff --git a/ext/node/polyfills/internal/util/inspect.mjs b/ext/node/polyfills/internal/util/inspect.mjs new file mode 100644 index 0000000000..53a878aa39 --- /dev/null +++ b/ext/node/polyfills/internal/util/inspect.mjs @@ -0,0 +1,2237 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import * as types from "internal:deno_node/polyfills/internal/util/types.ts"; +import { validateObject, validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; + +import { + ALL_PROPERTIES, + getOwnNonIndexProperties, + ONLY_ENUMERABLE, +} from "internal:deno_node/polyfills/internal_binding/util.ts"; + +const kObjectType = 0; +const kArrayType = 1; +const kArrayExtrasType = 2; + +const kMinLineLength = 16; + +// Constants to map the iterator state. +const kWeak = 0; +const kIterator = 1; +const kMapEntries = 2; + +const kPending = 0; +const kRejected = 2; + +// Escaped control characters (plus the single quote and the backslash). Use +// empty strings to fill up unused entries. +// deno-fmt-ignore +const meta = [ + '\\x00', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\x07', // x07 + '\\b', '\\t', '\\n', '\\x0B', '\\f', '\\r', '\\x0E', '\\x0F', // x0F + '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', // x17 + '\\x18', '\\x19', '\\x1A', '\\x1B', '\\x1C', '\\x1D', '\\x1E', '\\x1F', // x1F + '', '', '', '', '', '', '', "\\'", '', '', '', '', '', '', '', '', // x2F + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x3F + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x4F + '', '', '', '', '', '', '', '', '', '', '', '', '\\\\', '', '', '', // x5F + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x6F + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\x7F', // x7F + '\\x80', '\\x81', '\\x82', '\\x83', '\\x84', '\\x85', '\\x86', '\\x87', // x87 + '\\x88', '\\x89', '\\x8A', '\\x8B', '\\x8C', '\\x8D', '\\x8E', '\\x8F', // x8F + '\\x90', '\\x91', '\\x92', '\\x93', '\\x94', '\\x95', '\\x96', '\\x97', // x97 + '\\x98', '\\x99', '\\x9A', '\\x9B', '\\x9C', '\\x9D', '\\x9E', '\\x9F', // x9F +]; + +// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot +const isUndetectableObject = (v) => typeof v === "undefined" && v !== undefined; + +// deno-lint-ignore no-control-regex +const strEscapeSequencesRegExp = /[\x00-\x1f\x27\x5c\x7f-\x9f]/; +// deno-lint-ignore no-control-regex +const strEscapeSequencesReplacer = /[\x00-\x1f\x27\x5c\x7f-\x9f]/g; +// deno-lint-ignore no-control-regex +const strEscapeSequencesRegExpSingle = /[\x00-\x1f\x5c\x7f-\x9f]/; +// deno-lint-ignore no-control-regex +const strEscapeSequencesReplacerSingle = /[\x00-\x1f\x5c\x7f-\x9f]/g; + +const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/; +const numberRegExp = /^(0|[1-9][0-9]*)$/; +const nodeModulesRegExp = /[/\\]node_modules[/\\](.+?)(?=[/\\])/g; + +const classRegExp = /^(\s+[^(]*?)\s*{/; +// eslint-disable-next-line node-core/no-unescaped-regexp-dot +const stripCommentsRegExp = /(\/\/.*?\n)|(\/\*(.|\n)*?\*\/)/g; + +const inspectDefaultOptions = { + showHidden: false, + depth: 2, + colors: false, + customInspect: true, + showProxy: false, + maxArrayLength: 100, + maxStringLength: 10000, + breakLength: 80, + compact: 3, + sorted: false, + getters: false, +}; + +function getUserOptions(ctx, isCrossContext) { + const ret = { + stylize: ctx.stylize, + showHidden: ctx.showHidden, + depth: ctx.depth, + colors: ctx.colors, + customInspect: ctx.customInspect, + showProxy: ctx.showProxy, + maxArrayLength: ctx.maxArrayLength, + maxStringLength: ctx.maxStringLength, + breakLength: ctx.breakLength, + compact: ctx.compact, + sorted: ctx.sorted, + getters: ctx.getters, + ...ctx.userOptions, + }; + + // Typically, the target value will be an instance of `Object`. If that is + // *not* the case, the object may come from another vm.Context, and we want + // to avoid passing it objects from this Context in that case, so we remove + // the prototype from the returned object itself + the `stylize()` function, + // and remove all other non-primitives, including non-primitive user options. + if (isCrossContext) { + Object.setPrototypeOf(ret, null); + for (const key of Object.keys(ret)) { + if ( + (typeof ret[key] === "object" || typeof ret[key] === "function") && + ret[key] !== null + ) { + delete ret[key]; + } + } + ret.stylize = Object.setPrototypeOf((value, flavour) => { + let stylized; + try { + stylized = `${ctx.stylize(value, flavour)}`; + } catch { + // noop + } + + if (typeof stylized !== "string") return value; + // `stylized` is a string as it should be, which is safe to pass along. + return stylized; + }, null); + } + + return ret; +} + +/** + * Echos the value of any input. Tries to print the value out + * in the best way possible given the different types. + */ +/* Legacy: value, showHidden, depth, colors */ +export function inspect(value, opts) { + // Default options + const ctx = { + budget: {}, + indentationLvl: 0, + seen: [], + currentDepth: 0, + stylize: stylizeNoColor, + showHidden: inspectDefaultOptions.showHidden, + depth: inspectDefaultOptions.depth, + colors: inspectDefaultOptions.colors, + customInspect: inspectDefaultOptions.customInspect, + showProxy: inspectDefaultOptions.showProxy, + maxArrayLength: inspectDefaultOptions.maxArrayLength, + maxStringLength: inspectDefaultOptions.maxStringLength, + breakLength: inspectDefaultOptions.breakLength, + compact: inspectDefaultOptions.compact, + sorted: inspectDefaultOptions.sorted, + getters: inspectDefaultOptions.getters, + }; + if (arguments.length > 1) { + // Legacy... + if (arguments.length > 2) { + if (arguments[2] !== undefined) { + ctx.depth = arguments[2]; + } + if (arguments.length > 3 && arguments[3] !== undefined) { + ctx.colors = arguments[3]; + } + } + // Set user-specified options + if (typeof opts === "boolean") { + ctx.showHidden = opts; + } else if (opts) { + const optKeys = Object.keys(opts); + for (let i = 0; i < optKeys.length; ++i) { + const key = optKeys[i]; + // TODO(BridgeAR): Find a solution what to do about stylize. Either make + // this function public or add a new API with a similar or better + // functionality. + if ( + // deno-lint-ignore no-prototype-builtins + inspectDefaultOptions.hasOwnProperty(key) || + key === "stylize" + ) { + ctx[key] = opts[key]; + } else if (ctx.userOptions === undefined) { + // This is required to pass through the actual user input. + ctx.userOptions = opts; + } + } + } + } + if (ctx.colors) ctx.stylize = stylizeWithColor; + if (ctx.maxArrayLength === null) ctx.maxArrayLength = Infinity; + if (ctx.maxStringLength === null) ctx.maxStringLength = Infinity; + return formatValue(ctx, value, 0); +} +const customInspectSymbol = Symbol.for("nodejs.util.inspect.custom"); +inspect.custom = customInspectSymbol; + +Object.defineProperty(inspect, "defaultOptions", { + get() { + return inspectDefaultOptions; + }, + set(options) { + validateObject(options, "options"); + return Object.assign(inspectDefaultOptions, options); + }, +}); + +// Set Graphics Rendition https://en.wikipedia.org/wiki/ANSI_escape_code#graphics +// Each color consists of an array with the color code as first entry and the +// reset code as second entry. +const defaultFG = 39; +const defaultBG = 49; +inspect.colors = Object.assign(Object.create(null), { + reset: [0, 0], + bold: [1, 22], + dim: [2, 22], // Alias: faint + italic: [3, 23], + underline: [4, 24], + blink: [5, 25], + // Swap foreground and background colors + inverse: [7, 27], // Alias: swapcolors, swapColors + hidden: [8, 28], // Alias: conceal + strikethrough: [9, 29], // Alias: strikeThrough, crossedout, crossedOut + doubleunderline: [21, 24], // Alias: doubleUnderline + black: [30, defaultFG], + red: [31, defaultFG], + green: [32, defaultFG], + yellow: [33, defaultFG], + blue: [34, defaultFG], + magenta: [35, defaultFG], + cyan: [36, defaultFG], + white: [37, defaultFG], + bgBlack: [40, defaultBG], + bgRed: [41, defaultBG], + bgGreen: [42, defaultBG], + bgYellow: [43, defaultBG], + bgBlue: [44, defaultBG], + bgMagenta: [45, defaultBG], + bgCyan: [46, defaultBG], + bgWhite: [47, defaultBG], + framed: [51, 54], + overlined: [53, 55], + gray: [90, defaultFG], // Alias: grey, blackBright + redBright: [91, defaultFG], + greenBright: [92, defaultFG], + yellowBright: [93, defaultFG], + blueBright: [94, defaultFG], + magentaBright: [95, defaultFG], + cyanBright: [96, defaultFG], + whiteBright: [97, defaultFG], + bgGray: [100, defaultBG], // Alias: bgGrey, bgBlackBright + bgRedBright: [101, defaultBG], + bgGreenBright: [102, defaultBG], + bgYellowBright: [103, defaultBG], + bgBlueBright: [104, defaultBG], + bgMagentaBright: [105, defaultBG], + bgCyanBright: [106, defaultBG], + bgWhiteBright: [107, defaultBG], +}); + +function defineColorAlias(target, alias) { + Object.defineProperty(inspect.colors, alias, { + get() { + return this[target]; + }, + set(value) { + this[target] = value; + }, + configurable: true, + enumerable: false, + }); +} + +defineColorAlias("gray", "grey"); +defineColorAlias("gray", "blackBright"); +defineColorAlias("bgGray", "bgGrey"); +defineColorAlias("bgGray", "bgBlackBright"); +defineColorAlias("dim", "faint"); +defineColorAlias("strikethrough", "crossedout"); +defineColorAlias("strikethrough", "strikeThrough"); +defineColorAlias("strikethrough", "crossedOut"); +defineColorAlias("hidden", "conceal"); +defineColorAlias("inverse", "swapColors"); +defineColorAlias("inverse", "swapcolors"); +defineColorAlias("doubleunderline", "doubleUnderline"); + +// TODO(BridgeAR): Add function style support for more complex styles. +// Don't use 'blue' not visible on cmd.exe +inspect.styles = Object.assign(Object.create(null), { + special: "cyan", + number: "yellow", + bigint: "yellow", + boolean: "yellow", + undefined: "grey", + null: "bold", + string: "green", + symbol: "green", + date: "magenta", + // "name": intentionally not styling + // TODO(BridgeAR): Highlight regular expressions properly. + regexp: "red", + module: "underline", +}); + +function addQuotes(str, quotes) { + if (quotes === -1) { + return `"${str}"`; + } + if (quotes === -2) { + return `\`${str}\``; + } + return `'${str}'`; +} + +// TODO(wafuwafu13): Figure out +const escapeFn = (str) => meta[str.charCodeAt(0)]; + +// Escape control characters, single quotes and the backslash. +// This is similar to JSON stringify escaping. +function strEscape(str) { + let escapeTest = strEscapeSequencesRegExp; + let escapeReplace = strEscapeSequencesReplacer; + let singleQuote = 39; + + // Check for double quotes. If not present, do not escape single quotes and + // instead wrap the text in double quotes. If double quotes exist, check for + // backticks. If they do not exist, use those as fallback instead of the + // double quotes. + if (str.includes("'")) { + // This invalidates the charCode and therefore can not be matched for + // anymore. + if (!str.includes('"')) { + singleQuote = -1; + } else if ( + !str.includes("`") && + !str.includes("${") + ) { + singleQuote = -2; + } + if (singleQuote !== 39) { + escapeTest = strEscapeSequencesRegExpSingle; + escapeReplace = strEscapeSequencesReplacerSingle; + } + } + + // Some magic numbers that worked out fine while benchmarking with v8 6.0 + if (str.length < 5000 && !escapeTest.test(str)) { + return addQuotes(str, singleQuote); + } + if (str.length > 100) { + str = str.replace(escapeReplace, escapeFn); + return addQuotes(str, singleQuote); + } + + let result = ""; + let last = 0; + const lastIndex = str.length; + for (let i = 0; i < lastIndex; i++) { + const point = str.charCodeAt(i); + if ( + point === singleQuote || + point === 92 || + point < 32 || + (point > 126 && point < 160) + ) { + if (last === i) { + result += meta[point]; + } else { + result += `${str.slice(last, i)}${meta[point]}`; + } + last = i + 1; + } + } + + if (last !== lastIndex) { + result += str.slice(last); + } + return addQuotes(result, singleQuote); +} + +function stylizeWithColor(str, styleType) { + const style = inspect.styles[styleType]; + if (style !== undefined) { + const color = inspect.colors[style]; + if (color !== undefined) { + return `\u001b[${color[0]}m${str}\u001b[${color[1]}m`; + } + } + return str; +} + +function stylizeNoColor(str) { + return str; +} + +// Note: using `formatValue` directly requires the indentation level to be +// corrected by setting `ctx.indentationLvL += diff` and then to decrease the +// value afterwards again. +function formatValue( + ctx, + value, + recurseTimes, + typedArray, +) { + // Primitive types cannot have properties. + if ( + typeof value !== "object" && + typeof value !== "function" && + !isUndetectableObject(value) + ) { + return formatPrimitive(ctx.stylize, value, ctx); + } + if (value === null) { + return ctx.stylize("null", "null"); + } + + // Memorize the context for custom inspection on proxies. + const context = value; + // Always check for proxies to prevent side effects and to prevent triggering + // any proxy handlers. + // TODO(wafuwafu13): Set Proxy + const proxy = undefined; + // const proxy = getProxyDetails(value, !!ctx.showProxy); + // if (proxy !== undefined) { + // if (ctx.showProxy) { + // return formatProxy(ctx, proxy, recurseTimes); + // } + // value = proxy; + // } + + // Provide a hook for user-specified inspect functions. + // Check that value is an object with an inspect function on it. + if (ctx.customInspect) { + const maybeCustom = value[customInspectSymbol]; + if ( + typeof maybeCustom === "function" && + // Filter out the util module, its inspect function is special. + maybeCustom !== inspect && + // Also filter out any prototype objects using the circular check. + !(value.constructor && value.constructor.prototype === value) + ) { + // This makes sure the recurseTimes are reported as before while using + // a counter internally. + const depth = ctx.depth === null ? null : ctx.depth - recurseTimes; + const isCrossContext = proxy !== undefined || + !(context instanceof Object); + const ret = maybeCustom.call( + context, + depth, + getUserOptions(ctx, isCrossContext), + ); + // If the custom inspection method returned `this`, don't go into + // infinite recursion. + if (ret !== context) { + if (typeof ret !== "string") { + return formatValue(ctx, ret, recurseTimes); + } + return ret.replace(/\n/g, `\n${" ".repeat(ctx.indentationLvl)}`); + } + } + } + + // Using an array here is actually better for the average case than using + // a Set. `seen` will only check for the depth and will never grow too large. + if (ctx.seen.includes(value)) { + let index = 1; + if (ctx.circular === undefined) { + ctx.circular = new Map(); + ctx.circular.set(value, index); + } else { + index = ctx.circular.get(value); + if (index === undefined) { + index = ctx.circular.size + 1; + ctx.circular.set(value, index); + } + } + return ctx.stylize(`[Circular *${index}]`, "special"); + } + + return formatRaw(ctx, value, recurseTimes, typedArray); +} + +function formatRaw(ctx, value, recurseTimes, typedArray) { + let keys; + let protoProps; + if (ctx.showHidden && (recurseTimes <= ctx.depth || ctx.depth === null)) { + protoProps = []; + } + + const constructor = getConstructorName(value, ctx, recurseTimes, protoProps); + // Reset the variable to check for this later on. + if (protoProps !== undefined && protoProps.length === 0) { + protoProps = undefined; + } + + let tag = value[Symbol.toStringTag]; + // Only list the tag in case it's non-enumerable / not an own property. + // Otherwise we'd print this twice. + if ( + typeof tag !== "string" + // TODO(wafuwafu13): Implement + // (tag !== "" && + // (ctx.showHidden + // ? Object.prototype.hasOwnProperty + // : Object.prototype.propertyIsEnumerable)( + // value, + // Symbol.toStringTag, + // )) + ) { + tag = ""; + } + let base = ""; + let formatter = getEmptyFormatArray; + let braces; + let noIterator = true; + let i = 0; + const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE; + + let extrasType = kObjectType; + + // Iterators and the rest are split to reduce checks. + // We have to check all values in case the constructor is set to null. + // Otherwise it would not possible to identify all types properly. + if (value[Symbol.iterator] || constructor === null) { + noIterator = false; + if (Array.isArray(value)) { + // Only set the constructor for non ordinary ("Array [...]") arrays. + const prefix = (constructor !== "Array" || tag !== "") + ? getPrefix(constructor, tag, "Array", `(${value.length})`) + : ""; + keys = getOwnNonIndexProperties(value, filter); + braces = [`${prefix}[`, "]"]; + if (value.length === 0 && keys.length === 0 && protoProps === undefined) { + return `${braces[0]}]`; + } + extrasType = kArrayExtrasType; + formatter = formatArray; + } else if (types.isSet(value)) { + const size = value.size; + const prefix = getPrefix(constructor, tag, "Set", `(${size})`); + keys = getKeys(value, ctx.showHidden); + formatter = constructor !== null + ? formatSet.bind(null, value) + : formatSet.bind(null, value.values()); + if (size === 0 && keys.length === 0 && protoProps === undefined) { + return `${prefix}{}`; + } + braces = [`${prefix}{`, "}"]; + } else if (types.isMap(value)) { + const size = value.size; + const prefix = getPrefix(constructor, tag, "Map", `(${size})`); + keys = getKeys(value, ctx.showHidden); + formatter = constructor !== null + ? formatMap.bind(null, value) + : formatMap.bind(null, value.entries()); + if (size === 0 && keys.length === 0 && protoProps === undefined) { + return `${prefix}{}`; + } + braces = [`${prefix}{`, "}"]; + } else if (types.isTypedArray(value)) { + keys = getOwnNonIndexProperties(value, filter); + const bound = value; + const fallback = ""; + if (constructor === null) { + // TODO(wafuwafu13): Implement + // fallback = TypedArrayPrototypeGetSymbolToStringTag(value); + // // Reconstruct the array information. + // bound = new primordials[fallback](value); + } + const size = value.length; + const prefix = getPrefix(constructor, tag, fallback, `(${size})`); + braces = [`${prefix}[`, "]"]; + if (value.length === 0 && keys.length === 0 && !ctx.showHidden) { + return `${braces[0]}]`; + } + // Special handle the value. The original value is required below. The + // bound function is required to reconstruct missing information. + (formatter) = formatTypedArray.bind(null, bound, size); + extrasType = kArrayExtrasType; + } else if (types.isMapIterator(value)) { + keys = getKeys(value, ctx.showHidden); + braces = getIteratorBraces("Map", tag); + // Add braces to the formatter parameters. + (formatter) = formatIterator.bind(null, braces); + } else if (types.isSetIterator(value)) { + keys = getKeys(value, ctx.showHidden); + braces = getIteratorBraces("Set", tag); + // Add braces to the formatter parameters. + (formatter) = formatIterator.bind(null, braces); + } else { + noIterator = true; + } + } + if (noIterator) { + keys = getKeys(value, ctx.showHidden); + braces = ["{", "}"]; + if (constructor === "Object") { + if (types.isArgumentsObject(value)) { + braces[0] = "[Arguments] {"; + } else if (tag !== "") { + braces[0] = `${getPrefix(constructor, tag, "Object")}{`; + } + if (keys.length === 0 && protoProps === undefined) { + return `${braces[0]}}`; + } + } else if (typeof value === "function") { + base = getFunctionBase(value, constructor, tag); + if (keys.length === 0 && protoProps === undefined) { + return ctx.stylize(base, "special"); + } + } else if (types.isRegExp(value)) { + // Make RegExps say that they are RegExps + base = RegExp(constructor !== null ? value : new RegExp(value)) + .toString(); + const prefix = getPrefix(constructor, tag, "RegExp"); + if (prefix !== "RegExp ") { + base = `${prefix}${base}`; + } + if ( + (keys.length === 0 && protoProps === undefined) || + (recurseTimes > ctx.depth && ctx.depth !== null) + ) { + return ctx.stylize(base, "regexp"); + } + } else if (types.isDate(value)) { + // Make dates with properties first say the date + base = Number.isNaN(value.getTime()) + ? value.toString() + : value.toISOString(); + const prefix = getPrefix(constructor, tag, "Date"); + if (prefix !== "Date ") { + base = `${prefix}${base}`; + } + if (keys.length === 0 && protoProps === undefined) { + return ctx.stylize(base, "date"); + } + } else if (value instanceof Error) { + base = formatError(value, constructor, tag, ctx, keys); + if (keys.length === 0 && protoProps === undefined) { + return base; + } + } else if (types.isAnyArrayBuffer(value)) { + // Fast path for ArrayBuffer and SharedArrayBuffer. + // Can't do the same for DataView because it has a non-primitive + // .buffer property that we need to recurse for. + const arrayType = types.isArrayBuffer(value) + ? "ArrayBuffer" + : "SharedArrayBuffer"; + const prefix = getPrefix(constructor, tag, arrayType); + if (typedArray === undefined) { + (formatter) = formatArrayBuffer; + } else if (keys.length === 0 && protoProps === undefined) { + return prefix + + `{ byteLength: ${formatNumber(ctx.stylize, value.byteLength)} }`; + } + braces[0] = `${prefix}{`; + Array.prototype.unshift.call(keys, "byteLength"); + } else if (types.isDataView(value)) { + braces[0] = `${getPrefix(constructor, tag, "DataView")}{`; + // .buffer goes last, it's not a primitive like the others. + Array.prototype.unshift.call(keys, "byteLength", "byteOffset", "buffer"); + } else if (types.isPromise(value)) { + braces[0] = `${getPrefix(constructor, tag, "Promise")}{`; + (formatter) = formatPromise; + } else if (types.isWeakSet(value)) { + braces[0] = `${getPrefix(constructor, tag, "WeakSet")}{`; + (formatter) = ctx.showHidden ? formatWeakSet : formatWeakCollection; + } else if (types.isWeakMap(value)) { + braces[0] = `${getPrefix(constructor, tag, "WeakMap")}{`; + (formatter) = ctx.showHidden ? formatWeakMap : formatWeakCollection; + } else if (types.isModuleNamespaceObject(value)) { + braces[0] = `${getPrefix(constructor, tag, "Module")}{`; + // Special handle keys for namespace objects. + (formatter) = formatNamespaceObject.bind(null, keys); + } else if (types.isBoxedPrimitive(value)) { + base = getBoxedBase(value, ctx, keys, constructor, tag); + if (keys.length === 0 && protoProps === undefined) { + return base; + } + } else { + if (keys.length === 0 && protoProps === undefined) { + // TODO(wafuwafu13): Implement + // if (types.isExternal(value)) { + // const address = getExternalValue(value).toString(16); + // return ctx.stylize(`[External: ${address}]`, 'special'); + // } + return `${getCtxStyle(value, constructor, tag)}{}`; + } + braces[0] = `${getCtxStyle(value, constructor, tag)}{`; + } + } + + if (recurseTimes > ctx.depth && ctx.depth !== null) { + let constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); + if (constructor !== null) { + constructorName = `[${constructorName}]`; + } + return ctx.stylize(constructorName, "special"); + } + recurseTimes += 1; + + ctx.seen.push(value); + ctx.currentDepth = recurseTimes; + let output; + const indentationLvl = ctx.indentationLvl; + try { + output = formatter(ctx, value, recurseTimes); + for (i = 0; i < keys.length; i++) { + output.push( + formatProperty(ctx, value, recurseTimes, keys[i], extrasType), + ); + } + if (protoProps !== undefined) { + output.push(...protoProps); + } + } catch (err) { + const constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); + return handleMaxCallStackSize(ctx, err, constructorName, indentationLvl); + } + if (ctx.circular !== undefined) { + const index = ctx.circular.get(value); + if (index !== undefined) { + const reference = ctx.stylize(``, "special"); + // Add reference always to the very beginning of the output. + if (ctx.compact !== true) { + base = base === "" ? reference : `${reference} ${base}`; + } else { + braces[0] = `${reference} ${braces[0]}`; + } + } + } + ctx.seen.pop(); + + if (ctx.sorted) { + const comparator = ctx.sorted === true ? undefined : ctx.sorted; + if (extrasType === kObjectType) { + output = output.sort(comparator); + } else if (keys.length > 1) { + const sorted = output.slice(output.length - keys.length).sort(comparator); + output.splice(output.length - keys.length, keys.length, ...sorted); + } + } + + const res = reduceToSingleString( + ctx, + output, + base, + braces, + extrasType, + recurseTimes, + value, + ); + const budget = ctx.budget[ctx.indentationLvl] || 0; + const newLength = budget + res.length; + ctx.budget[ctx.indentationLvl] = newLength; + // If any indentationLvl exceeds this limit, limit further inspecting to the + // minimum. Otherwise the recursive algorithm might continue inspecting the + // object even though the maximum string size (~2 ** 28 on 32 bit systems and + // ~2 ** 30 on 64 bit systems) exceeded. The actual output is not limited at + // exactly 2 ** 27 but a bit higher. This depends on the object shape. + // This limit also makes sure that huge objects don't block the event loop + // significantly. + if (newLength > 2 ** 27) { + ctx.depth = -1; + } + return res; +} + +const builtInObjects = new Set( + Object.getOwnPropertyNames(globalThis).filter((e) => + /^[A-Z][a-zA-Z0-9]+$/.test(e) + ), +); + +function addPrototypeProperties( + ctx, + main, + obj, + recurseTimes, + output, +) { + let depth = 0; + let keys; + let keySet; + do { + if (depth !== 0 || main === obj) { + obj = Object.getPrototypeOf(obj); + // Stop as soon as a null prototype is encountered. + if (obj === null) { + return; + } + // Stop as soon as a built-in object type is detected. + const descriptor = Object.getOwnPropertyDescriptor(obj, "constructor"); + if ( + descriptor !== undefined && + typeof descriptor.value === "function" && + builtInObjects.has(descriptor.value.name) + ) { + return; + } + } + + if (depth === 0) { + keySet = new Set(); + } else { + Array.prototype.forEach.call(keys, (key) => keySet.add(key)); + } + // Get all own property names and symbols. + keys = Reflect.ownKeys(obj); + Array.prototype.push.call(ctx.seen, main); + for (const key of keys) { + // Ignore the `constructor` property and keys that exist on layers above. + if ( + key === "constructor" || + // deno-lint-ignore no-prototype-builtins + main.hasOwnProperty(key) || + (depth !== 0 && keySet.has(key)) + ) { + continue; + } + const desc = Object.getOwnPropertyDescriptor(obj, key); + if (typeof desc.value === "function") { + continue; + } + const value = formatProperty( + ctx, + obj, + recurseTimes, + key, + kObjectType, + desc, + main, + ); + if (ctx.colors) { + // Faint! + Array.prototype.push.call(output, `\u001b[2m${value}\u001b[22m`); + } else { + Array.prototype.push.call(output, value); + } + } + Array.prototype.pop.call(ctx.seen); + // Limit the inspection to up to three prototype layers. Using `recurseTimes` + // is not a good choice here, because it's as if the properties are declared + // on the current object from the users perspective. + } while (++depth !== 3); +} + +function getConstructorName( + obj, + ctx, + recurseTimes, + protoProps, +) { + let firstProto; + const tmp = obj; + while (obj || isUndetectableObject(obj)) { + const descriptor = Object.getOwnPropertyDescriptor(obj, "constructor"); + if ( + descriptor !== undefined && + typeof descriptor.value === "function" && + descriptor.value.name !== "" && + isInstanceof(tmp, descriptor.value) + ) { + if ( + protoProps !== undefined && + (firstProto !== obj || + !builtInObjects.has(descriptor.value.name)) + ) { + addPrototypeProperties( + ctx, + tmp, + firstProto || tmp, + recurseTimes, + protoProps, + ); + } + return descriptor.value.name; + } + + obj = Object.getPrototypeOf(obj); + if (firstProto === undefined) { + firstProto = obj; + } + } + + if (firstProto === null) { + return null; + } + + // TODO(wafuwafu13): Implement + // const res = internalGetConstructorName(tmp); + const res = undefined; + + if (recurseTimes > ctx.depth && ctx.depth !== null) { + return `${res} `; + } + + const protoConstr = getConstructorName( + firstProto, + ctx, + recurseTimes + 1, + protoProps, + ); + + if (protoConstr === null) { + return `${res} <${ + inspect(firstProto, { + ...ctx, + customInspect: false, + depth: -1, + }) + }>`; + } + + return `${res} <${protoConstr}>`; +} + +function formatPrimitive(fn, value, ctx) { + if (typeof value === "string") { + let trailer = ""; + if (value.length > ctx.maxStringLength) { + const remaining = value.length - ctx.maxStringLength; + value = value.slice(0, ctx.maxStringLength); + trailer = `... ${remaining} more character${remaining > 1 ? "s" : ""}`; + } + if ( + ctx.compact !== true && + // TODO(BridgeAR): Add unicode support. Use the readline getStringWidth + // function. + value.length > kMinLineLength && + value.length > ctx.breakLength - ctx.indentationLvl - 4 + ) { + return value + .split(/(?<=\n)/) + .map((line) => fn(strEscape(line), "string")) + .join(` +\n${" ".repeat(ctx.indentationLvl + 2)}`) + trailer; + } + return fn(strEscape(value), "string") + trailer; + } + if (typeof value === "number") { + return formatNumber(fn, value); + } + if (typeof value === "bigint") { + return formatBigInt(fn, value); + } + if (typeof value === "boolean") { + return fn(`${value}`, "boolean"); + } + if (typeof value === "undefined") { + return fn("undefined", "undefined"); + } + // es6 symbol primitive + return fn(value.toString(), "symbol"); +} + +// Return a new empty array to push in the results of the default formatter. +function getEmptyFormatArray() { + return []; +} + +function isInstanceof(object, proto) { + try { + return object instanceof proto; + } catch { + return false; + } +} + +function getPrefix(constructor, tag, fallback, size = "") { + if (constructor === null) { + if (tag !== "" && fallback !== tag) { + return `[${fallback}${size}: null prototype] [${tag}] `; + } + return `[${fallback}${size}: null prototype] `; + } + + if (tag !== "" && constructor !== tag) { + return `${constructor}${size} [${tag}] `; + } + return `${constructor}${size} `; +} + +function formatArray(ctx, value, recurseTimes) { + const valLen = value.length; + const len = Math.min(Math.max(0, ctx.maxArrayLength), valLen); + + const remaining = valLen - len; + const output = []; + for (let i = 0; i < len; i++) { + // Special handle sparse arrays. + // deno-lint-ignore no-prototype-builtins + if (!value.hasOwnProperty(i)) { + return formatSpecialArray(ctx, value, recurseTimes, len, output, i); + } + output.push(formatProperty(ctx, value, recurseTimes, i, kArrayType)); + } + if (remaining > 0) { + output.push(`... ${remaining} more item${remaining > 1 ? "s" : ""}`); + } + return output; +} + +function getCtxStyle(_value, constructor, tag) { + let fallback = ""; + if (constructor === null) { + // TODO(wafuwafu13): Implement + // fallback = internalGetConstructorName(value); + if (fallback === tag) { + fallback = "Object"; + } + } + return getPrefix(constructor, tag, fallback); +} + +// Look up the keys of the object. +function getKeys(value, showHidden) { + let keys; + const symbols = Object.getOwnPropertySymbols(value); + if (showHidden) { + keys = Object.getOwnPropertyNames(value); + if (symbols.length !== 0) { + Array.prototype.push.apply(keys, symbols); + } + } else { + // This might throw if `value` is a Module Namespace Object from an + // unevaluated module, but we don't want to perform the actual type + // check because it's expensive. + // TODO(devsnek): track https://github.com/tc39/ecma262/issues/1209 + // and modify this logic as needed. + try { + keys = Object.keys(value); + } catch (_err) { + // TODO(wafuwafu13): Implement + // assert(isNativeError(err) && err.name === 'ReferenceError' && + // isModuleNamespaceObject(value)); + keys = Object.getOwnPropertyNames(value); + } + if (symbols.length !== 0) { + // TODO(wafuwafu13): Implement + // const filter = (key: any) => + // + // Object.prototype.propertyIsEnumerable(value, key); + // Array.prototype.push.apply( + // keys, + // symbols.filter(filter), + // ); + } + } + return keys; +} + +function formatSet(value, ctx, _ignored, recurseTimes) { + const output = []; + ctx.indentationLvl += 2; + for (const v of value) { + Array.prototype.push.call(output, formatValue(ctx, v, recurseTimes)); + } + ctx.indentationLvl -= 2; + return output; +} + +function formatMap(value, ctx, _gnored, recurseTimes) { + const output = []; + ctx.indentationLvl += 2; + for (const { 0: k, 1: v } of value) { + output.push( + `${formatValue(ctx, k, recurseTimes)} => ${ + formatValue(ctx, v, recurseTimes) + }`, + ); + } + ctx.indentationLvl -= 2; + return output; +} + +function formatTypedArray( + value, + length, + ctx, + _ignored, + recurseTimes, +) { + const maxLength = Math.min(Math.max(0, ctx.maxArrayLength), length); + const remaining = value.length - maxLength; + const output = new Array(maxLength); + const elementFormatter = value.length > 0 && typeof value[0] === "number" + ? formatNumber + : formatBigInt; + for (let i = 0; i < maxLength; ++i) { + output[i] = elementFormatter(ctx.stylize, value[i]); + } + if (remaining > 0) { + output[maxLength] = `... ${remaining} more item${remaining > 1 ? "s" : ""}`; + } + if (ctx.showHidden) { + // .buffer goes last, it's not a primitive like the others. + // All besides `BYTES_PER_ELEMENT` are actually getters. + ctx.indentationLvl += 2; + for ( + const key of [ + "BYTES_PER_ELEMENT", + "length", + "byteLength", + "byteOffset", + "buffer", + ] + ) { + const str = formatValue(ctx, value[key], recurseTimes, true); + Array.prototype.push.call(output, `[${key}]: ${str}`); + } + ctx.indentationLvl -= 2; + } + return output; +} + +function getIteratorBraces(type, tag) { + if (tag !== `${type} Iterator`) { + if (tag !== "") { + tag += "] ["; + } + tag += `${type} Iterator`; + } + return [`[${tag}] {`, "}"]; +} + +function formatIterator(braces, ctx, value, recurseTimes) { + // TODO(wafuwafu13): Implement + // const { 0: entries, 1: isKeyValue } = previewEntries(value, true); + const { 0: entries, 1: isKeyValue } = value; + if (isKeyValue) { + // Mark entry iterators as such. + braces[0] = braces[0].replace(/ Iterator] {$/, " Entries] {"); + return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries); + } + + return formatSetIterInner(ctx, recurseTimes, entries, kIterator); +} + +function getFunctionBase(value, constructor, tag) { + const stringified = Function.prototype.toString.call(value); + if (stringified.slice(0, 5) === "class" && stringified.endsWith("}")) { + const slice = stringified.slice(5, -1); + const bracketIndex = slice.indexOf("{"); + if ( + bracketIndex !== -1 && + (!slice.slice(0, bracketIndex).includes("(") || + // Slow path to guarantee that it's indeed a class. + classRegExp.test(slice.replace(stripCommentsRegExp))) + ) { + return getClassBase(value, constructor, tag); + } + } + let type = "Function"; + if (types.isGeneratorFunction(value)) { + type = `Generator${type}`; + } + if (types.isAsyncFunction(value)) { + type = `Async${type}`; + } + let base = `[${type}`; + if (constructor === null) { + base += " (null prototype)"; + } + if (value.name === "") { + base += " (anonymous)"; + } else { + base += `: ${value.name}`; + } + base += "]"; + if (constructor !== type && constructor !== null) { + base += ` ${constructor}`; + } + if (tag !== "" && constructor !== tag) { + base += ` [${tag}]`; + } + return base; +} + +function formatError( + err, + constructor, + tag, + ctx, + keys, +) { + const name = err.name != null ? String(err.name) : "Error"; + let len = name.length; + let stack = err.stack ? String(err.stack) : err.toString(); + + // Do not "duplicate" error properties that are already included in the output + // otherwise. + if (!ctx.showHidden && keys.length !== 0) { + for (const name of ["name", "message", "stack"]) { + const index = keys.indexOf(name); + // Only hide the property in case it's part of the original stack + if (index !== -1 && stack.includes(err[name])) { + keys.splice(index, 1); + } + } + } + + // A stack trace may contain arbitrary data. Only manipulate the output + // for "regular errors" (errors that "look normal") for now. + if ( + constructor === null || + (name.endsWith("Error") && + stack.startsWith(name) && + (stack.length === len || stack[len] === ":" || stack[len] === "\n")) + ) { + let fallback = "Error"; + if (constructor === null) { + const start = stack.match(/^([A-Z][a-z_ A-Z0-9[\]()-]+)(?::|\n {4}at)/) || + stack.match(/^([a-z_A-Z0-9-]*Error)$/); + fallback = (start && start[1]) || ""; + len = fallback.length; + fallback = fallback || "Error"; + } + const prefix = getPrefix(constructor, tag, fallback).slice(0, -1); + if (name !== prefix) { + if (prefix.includes(name)) { + if (len === 0) { + stack = `${prefix}: ${stack}`; + } else { + stack = `${prefix}${stack.slice(len)}`; + } + } else { + stack = `${prefix} [${name}]${stack.slice(len)}`; + } + } + } + // Ignore the error message if it's contained in the stack. + let pos = (err.message && stack.indexOf(err.message)) || -1; + if (pos !== -1) { + pos += err.message.length; + } + // Wrap the error in brackets in case it has no stack trace. + const stackStart = stack.indexOf("\n at", pos); + if (stackStart === -1) { + stack = `[${stack}]`; + } else if (ctx.colors) { + // Highlight userland code and node modules. + let newStack = stack.slice(0, stackStart); + const lines = stack.slice(stackStart + 1).split("\n"); + for (const line of lines) { + // const core = line.match(coreModuleRegExp); + // TODO(wafuwafu13): Implement + // if (core !== null && NativeModule.exists(core[1])) { + // newStack += `\n${ctx.stylize(line, 'undefined')}`; + // } else { + // This adds underscores to all node_modules to quickly identify them. + let nodeModule; + newStack += "\n"; + let pos = 0; + // deno-lint-ignore no-cond-assign + while (nodeModule = nodeModulesRegExp.exec(line)) { + // '/node_modules/'.length === 14 + newStack += line.slice(pos, nodeModule.index + 14); + newStack += ctx.stylize(nodeModule[1], "module"); + pos = nodeModule.index + nodeModule[0].length; + } + newStack += pos === 0 ? line : line.slice(pos); + // } + } + stack = newStack; + } + // The message and the stack have to be indented as well! + if (ctx.indentationLvl !== 0) { + const indentation = " ".repeat(ctx.indentationLvl); + stack = stack.replace(/\n/g, `\n${indentation}`); + } + return stack; +} + +let hexSlice; + +function formatArrayBuffer(ctx, value) { + let buffer; + try { + buffer = new Uint8Array(value); + } catch { + return [ctx.stylize("(detached)", "special")]; + } + // TODO(wafuwafu13): Implement + // if (hexSlice === undefined) + // hexSlice = uncurryThis(require('buffer').Buffer.prototype.hexSlice); + let str = hexSlice(buffer, 0, Math.min(ctx.maxArrayLength, buffer.length)) + .replace(/(.{2})/g, "$1 ").trim(); + + const remaining = buffer.length - ctx.maxArrayLength; + if (remaining > 0) { + str += ` ... ${remaining} more byte${remaining > 1 ? "s" : ""}`; + } + return [`${ctx.stylize("[Uint8Contents]", "special")}: <${str}>`]; +} + +function formatNumber(fn, value) { + // Format -0 as '-0'. Checking `value === -0` won't distinguish 0 from -0. + return fn(Object.is(value, -0) ? "-0" : `${value}`, "number"); +} + +function formatPromise(ctx, value, recurseTimes) { + let output; + // TODO(wafuwafu13): Implement + // const { 0: state, 1: result } = getPromiseDetails(value); + const { 0: state, 1: result } = value; + if (state === kPending) { + output = [ctx.stylize("", "special")]; + } else { + ctx.indentationLvl += 2; + const str = formatValue(ctx, result, recurseTimes); + ctx.indentationLvl -= 2; + output = [ + state === kRejected + ? `${ctx.stylize("", "special")} ${str}` + : str, + ]; + } + return output; +} + +function formatWeakCollection(ctx) { + return [ctx.stylize("", "special")]; +} + +function formatWeakSet(ctx, value, recurseTimes) { + // TODO(wafuwafu13): Implement + // const entries = previewEntries(value); + const entries = value; + return formatSetIterInner(ctx, recurseTimes, entries, kWeak); +} + +function formatWeakMap(ctx, value, recurseTimes) { + // TODO(wafuwafu13): Implement + // const entries = previewEntries(value); + const entries = value; + return formatMapIterInner(ctx, recurseTimes, entries, kWeak); +} + +function formatProperty( + ctx, + value, + recurseTimes, + key, + type, + desc, + original = value, +) { + let name, str; + let extra = " "; + desc = desc || Object.getOwnPropertyDescriptor(value, key) || + { value: value[key], enumerable: true }; + if (desc.value !== undefined) { + const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3; + ctx.indentationLvl += diff; + str = formatValue(ctx, desc.value, recurseTimes); + if (diff === 3 && ctx.breakLength < getStringWidth(str, ctx.colors)) { + extra = `\n${" ".repeat(ctx.indentationLvl)}`; + } + ctx.indentationLvl -= diff; + } else if (desc.get !== undefined) { + const label = desc.set !== undefined ? "Getter/Setter" : "Getter"; + const s = ctx.stylize; + const sp = "special"; + if ( + ctx.getters && (ctx.getters === true || + (ctx.getters === "get" && desc.set === undefined) || + (ctx.getters === "set" && desc.set !== undefined)) + ) { + try { + const tmp = desc.get.call(original); + ctx.indentationLvl += 2; + if (tmp === null) { + str = `${s(`[${label}:`, sp)} ${s("null", "null")}${s("]", sp)}`; + } else if (typeof tmp === "object") { + str = `${s(`[${label}]`, sp)} ${formatValue(ctx, tmp, recurseTimes)}`; + } else { + const primitive = formatPrimitive(s, tmp, ctx); + str = `${s(`[${label}:`, sp)} ${primitive}${s("]", sp)}`; + } + ctx.indentationLvl -= 2; + } catch (err) { + const message = ``; + str = `${s(`[${label}:`, sp)} ${message}${s("]", sp)}`; + } + } else { + str = ctx.stylize(`[${label}]`, sp); + } + } else if (desc.set !== undefined) { + str = ctx.stylize("[Setter]", "special"); + } else { + str = ctx.stylize("undefined", "undefined"); + } + if (type === kArrayType) { + return str; + } + if (typeof key === "symbol") { + const tmp = key.toString().replace(strEscapeSequencesReplacer, escapeFn); + + name = `[${ctx.stylize(tmp, "symbol")}]`; + } else if (key === "__proto__") { + name = "['__proto__']"; + } else if (desc.enumerable === false) { + const tmp = key.replace(strEscapeSequencesReplacer, escapeFn); + + name = `[${tmp}]`; + } else if (keyStrRegExp.test(key)) { + name = ctx.stylize(key, "name"); + } else { + name = ctx.stylize(strEscape(key), "string"); + } + return `${name}:${extra}${str}`; +} + +function handleMaxCallStackSize( + _ctx, + _err, + _constructorName, + _indentationLvl, +) { + // TODO(wafuwafu13): Implement + // if (types.isStackOverflowError(err)) { + // ctx.seen.pop(); + // ctx.indentationLvl = indentationLvl; + // return ctx.stylize( + // `[${constructorName}: Inspection interrupted ` + + // 'prematurely. Maximum call stack size exceeded.]', + // 'special' + // ); + // } + // /* c8 ignore next */ + // assert.fail(err.stack); +} + +// deno-lint-ignore no-control-regex +const colorRegExp = /\u001b\[\d\d?m/g; +function removeColors(str) { + return str.replace(colorRegExp, ""); +} + +function isBelowBreakLength(ctx, output, start, base) { + // Each entry is separated by at least a comma. Thus, we start with a total + // length of at least `output.length`. In addition, some cases have a + // whitespace in-between each other that is added to the total as well. + // TODO(BridgeAR): Add unicode support. Use the readline getStringWidth + // function. Check the performance overhead and make it an opt-in in case it's + // significant. + let totalLength = output.length + start; + if (totalLength + output.length > ctx.breakLength) { + return false; + } + for (let i = 0; i < output.length; i++) { + if (ctx.colors) { + totalLength += removeColors(output[i]).length; + } else { + totalLength += output[i].length; + } + if (totalLength > ctx.breakLength) { + return false; + } + } + // Do not line up properties on the same line if `base` contains line breaks. + return base === "" || !base.includes("\n"); +} + +function formatBigInt(fn, value) { + return fn(`${value}n`, "bigint"); +} + +function formatNamespaceObject( + keys, + ctx, + value, + recurseTimes, +) { + const output = new Array(keys.length); + for (let i = 0; i < keys.length; i++) { + try { + output[i] = formatProperty( + ctx, + value, + recurseTimes, + keys[i], + kObjectType, + ); + } catch (_err) { + // TODO(wafuwfu13): Implement + // assert(isNativeError(err) && err.name === 'ReferenceError'); + // Use the existing functionality. This makes sure the indentation and + // line breaks are always correct. Otherwise it is very difficult to keep + // this aligned, even though this is a hacky way of dealing with this. + const tmp = { [keys[i]]: "" }; + output[i] = formatProperty(ctx, tmp, recurseTimes, keys[i], kObjectType); + const pos = output[i].lastIndexOf(" "); + // We have to find the last whitespace and have to replace that value as + // it will be visualized as a regular string. + output[i] = output[i].slice(0, pos + 1) + + ctx.stylize("", "special"); + } + } + // Reset the keys to an empty array. This prevents duplicated inspection. + keys.length = 0; + return output; +} + +// The array is sparse and/or has extra keys +function formatSpecialArray( + ctx, + value, + recurseTimes, + maxLength, + output, + i, +) { + const keys = Object.keys(value); + let index = i; + for (; i < keys.length && output.length < maxLength; i++) { + const key = keys[i]; + const tmp = +key; + // Arrays can only have up to 2^32 - 1 entries + if (tmp > 2 ** 32 - 2) { + break; + } + if (`${index}` !== key) { + if (!numberRegExp.test(key)) { + break; + } + const emptyItems = tmp - index; + const ending = emptyItems > 1 ? "s" : ""; + const message = `<${emptyItems} empty item${ending}>`; + output.push(ctx.stylize(message, "undefined")); + index = tmp; + if (output.length === maxLength) { + break; + } + } + output.push(formatProperty(ctx, value, recurseTimes, key, kArrayType)); + index++; + } + const remaining = value.length - index; + if (output.length !== maxLength) { + if (remaining > 0) { + const ending = remaining > 1 ? "s" : ""; + const message = `<${remaining} empty item${ending}>`; + output.push(ctx.stylize(message, "undefined")); + } + } else if (remaining > 0) { + output.push(`... ${remaining} more item${remaining > 1 ? "s" : ""}`); + } + return output; +} + +function getBoxedBase( + value, + ctx, + keys, + constructor, + tag, +) { + let type; + if (types.isNumberObject(value)) { + type = "Number"; + } else if (types.isStringObject(value)) { + type = "String"; + // For boxed Strings, we have to remove the 0-n indexed entries, + // since they just noisy up the output and are redundant + // Make boxed primitive Strings look like such + keys.splice(0, value.length); + } else if (types.isBooleanObject(value)) { + type = "Boolean"; + } else if (types.isBigIntObject(value)) { + type = "BigInt"; + } else { + type = "Symbol"; + } + let base = `[${type}`; + if (type !== constructor) { + if (constructor === null) { + base += " (null prototype)"; + } else { + base += ` (${constructor})`; + } + } + + base += `: ${formatPrimitive(stylizeNoColor, value.valueOf(), ctx)}]`; + if (tag !== "" && tag !== constructor) { + base += ` [${tag}]`; + } + if (keys.length !== 0 || ctx.stylize === stylizeNoColor) { + return base; + } + return ctx.stylize(base, type.toLowerCase()); +} + +function getClassBase(value, constructor, tag) { + // deno-lint-ignore no-prototype-builtins + const hasName = value.hasOwnProperty("name"); + const name = (hasName && value.name) || "(anonymous)"; + let base = `class ${name}`; + if (constructor !== "Function" && constructor !== null) { + base += ` [${constructor}]`; + } + if (tag !== "" && constructor !== tag) { + base += ` [${tag}]`; + } + if (constructor !== null) { + const superName = Object.getPrototypeOf(value).name; + if (superName) { + base += ` extends ${superName}`; + } + } else { + base += " extends [null prototype]"; + } + return `[${base}]`; +} + +function reduceToSingleString( + ctx, + output, + base, + braces, + extrasType, + recurseTimes, + value, +) { + if (ctx.compact !== true) { + if (typeof ctx.compact === "number" && ctx.compact >= 1) { + // Memorize the original output length. In case the output is grouped, + // prevent lining up the entries on a single line. + const entries = output.length; + // Group array elements together if the array contains at least six + // separate entries. + if (extrasType === kArrayExtrasType && entries > 6) { + output = groupArrayElements(ctx, output, value); + } + // `ctx.currentDepth` is set to the most inner depth of the currently + // inspected object part while `recurseTimes` is the actual current depth + // that is inspected. + // + // Example: + // + // const a = { first: [ 1, 2, 3 ], second: { inner: [ 1, 2, 3 ] } } + // + // The deepest depth of `a` is 2 (a.second.inner) and `a.first` has a max + // depth of 1. + // + // Consolidate all entries of the local most inner depth up to + // `ctx.compact`, as long as the properties are smaller than + // `ctx.breakLength`. + if ( + ctx.currentDepth - recurseTimes < ctx.compact && + entries === output.length + ) { + // Line up all entries on a single line in case the entries do not + // exceed `breakLength`. Add 10 as constant to start next to all other + // factors that may reduce `breakLength`. + const start = output.length + ctx.indentationLvl + + braces[0].length + base.length + 10; + if (isBelowBreakLength(ctx, output, start, base)) { + return `${base ? `${base} ` : ""}${braces[0]} ${join(output, ", ")}` + + ` ${braces[1]}`; + } + } + } + // Line up each entry on an individual line. + const indentation = `\n${" ".repeat(ctx.indentationLvl)}`; + return `${base ? `${base} ` : ""}${braces[0]}${indentation} ` + + `${join(output, `,${indentation} `)}${indentation}${braces[1]}`; + } + // Line up all entries on a single line in case the entries do not exceed + // `breakLength`. + if (isBelowBreakLength(ctx, output, 0, base)) { + return `${braces[0]}${base ? ` ${base}` : ""} ${join(output, ", ")} ` + + braces[1]; + } + const indentation = " ".repeat(ctx.indentationLvl); + // If the opening "brace" is too large, like in the case of "Set {", + // we need to force the first item to be on the next line or the + // items will not line up correctly. + const ln = base === "" && braces[0].length === 1 + ? " " + : `${base ? ` ${base}` : ""}\n${indentation} `; + // Line up each entry on an individual line. + return `${braces[0]}${ln}${join(output, `,\n${indentation} `)} ${braces[1]}`; +} + +// The built-in Array#join is slower in v8 6.0 +function join(output, separator) { + let str = ""; + if (output.length !== 0) { + const lastIndex = output.length - 1; + for (let i = 0; i < lastIndex; i++) { + // It is faster not to use a template string here + str += output[i]; + str += separator; + } + str += output[lastIndex]; + } + return str; +} + +function groupArrayElements(ctx, output, value) { + let totalLength = 0; + let maxLength = 0; + let i = 0; + let outputLength = output.length; + if (ctx.maxArrayLength < output.length) { + // This makes sure the "... n more items" part is not taken into account. + outputLength--; + } + const separatorSpace = 2; // Add 1 for the space and 1 for the separator. + const dataLen = new Array(outputLength); + // Calculate the total length of all output entries and the individual max + // entries length of all output entries. We have to remove colors first, + // otherwise the length would not be calculated properly. + for (; i < outputLength; i++) { + const len = getStringWidth(output[i], ctx.colors); + dataLen[i] = len; + totalLength += len + separatorSpace; + if (maxLength < len) { + maxLength = len; + } + } + // Add two to `maxLength` as we add a single whitespace character plus a comma + // in-between two entries. + const actualMax = maxLength + separatorSpace; + // Check if at least three entries fit next to each other and prevent grouping + // of arrays that contains entries of very different length (i.e., if a single + // entry is longer than 1/5 of all other entries combined). Otherwise the + // space in-between small entries would be enormous. + if ( + actualMax * 3 + ctx.indentationLvl < ctx.breakLength && + (totalLength / actualMax > 5 || maxLength <= 6) + ) { + const approxCharHeights = 2.5; + const averageBias = Math.sqrt(actualMax - totalLength / output.length); + const biasedMax = Math.max(actualMax - 3 - averageBias, 1); + // Dynamically check how many columns seem possible. + const columns = Math.min( + // Ideally a square should be drawn. We expect a character to be about 2.5 + // times as high as wide. This is the area formula to calculate a square + // which contains n rectangles of size `actualMax * approxCharHeights`. + // Divide that by `actualMax` to receive the correct number of columns. + // The added bias increases the columns for short entries. + Math.round( + Math.sqrt( + approxCharHeights * biasedMax * outputLength, + ) / biasedMax, + ), + // Do not exceed the breakLength. + Math.floor((ctx.breakLength - ctx.indentationLvl) / actualMax), + // Limit array grouping for small `compact` modes as the user requested + // minimal grouping. + ctx.compact * 4, + // Limit the columns to a maximum of fifteen. + 15, + ); + // Return with the original output if no grouping should happen. + if (columns <= 1) { + return output; + } + const tmp = []; + const maxLineLength = []; + for (let i = 0; i < columns; i++) { + let lineMaxLength = 0; + for (let j = i; j < output.length; j += columns) { + if (dataLen[j] > lineMaxLength) { + lineMaxLength = dataLen[j]; + } + } + lineMaxLength += separatorSpace; + maxLineLength[i] = lineMaxLength; + } + let order = String.prototype.padStart; + if (value !== undefined) { + for (let i = 0; i < output.length; i++) { + if (typeof value[i] !== "number" && typeof value[i] !== "bigint") { + order = String.prototype.padEnd; + break; + } + } + } + // Each iteration creates a single line of grouped entries. + for (let i = 0; i < outputLength; i += columns) { + // The last lines may contain less entries than columns. + const max = Math.min(i + columns, outputLength); + let str = ""; + let j = i; + for (; j < max - 1; j++) { + // Calculate extra color padding in case it's active. This has to be + // done line by line as some lines might contain more colors than + // others. + const padding = maxLineLength[j - i] + output[j].length - dataLen[j]; + str += `${output[j]}, `.padStart(padding, " "); + } + if (order === String.prototype.padStart) { + const padding = maxLineLength[j - i] + + output[j].length - + dataLen[j] - + separatorSpace; + str += output[j].padStart(padding, " "); + } else { + str += output[j]; + } + Array.prototype.push.call(tmp, str); + } + if (ctx.maxArrayLength < output.length) { + Array.prototype.push.call(tmp, output[outputLength]); + } + output = tmp; + } + return output; +} + +function formatMapIterInner( + ctx, + recurseTimes, + entries, + state, +) { + const maxArrayLength = Math.max(ctx.maxArrayLength, 0); + // Entries exist as [key1, val1, key2, val2, ...] + const len = entries.length / 2; + const remaining = len - maxArrayLength; + const maxLength = Math.min(maxArrayLength, len); + let output = new Array(maxLength); + let i = 0; + ctx.indentationLvl += 2; + if (state === kWeak) { + for (; i < maxLength; i++) { + const pos = i * 2; + output[i] = `${formatValue(ctx, entries[pos], recurseTimes)} => ${ + formatValue(ctx, entries[pos + 1], recurseTimes) + }`; + } + // Sort all entries to have a halfway reliable output (if more entries than + // retrieved ones exist, we can not reliably return the same output) if the + // output is not sorted anyway. + if (!ctx.sorted) { + output = output.sort(); + } + } else { + for (; i < maxLength; i++) { + const pos = i * 2; + const res = [ + formatValue(ctx, entries[pos], recurseTimes), + formatValue(ctx, entries[pos + 1], recurseTimes), + ]; + output[i] = reduceToSingleString( + ctx, + res, + "", + ["[", "]"], + kArrayExtrasType, + recurseTimes, + ); + } + } + ctx.indentationLvl -= 2; + if (remaining > 0) { + output.push(`... ${remaining} more item${remaining > 1 ? "s" : ""}`); + } + return output; +} + +function formatSetIterInner( + ctx, + recurseTimes, + entries, + state, +) { + const maxArrayLength = Math.max(ctx.maxArrayLength, 0); + const maxLength = Math.min(maxArrayLength, entries.length); + const output = new Array(maxLength); + ctx.indentationLvl += 2; + for (let i = 0; i < maxLength; i++) { + output[i] = formatValue(ctx, entries[i], recurseTimes); + } + ctx.indentationLvl -= 2; + if (state === kWeak && !ctx.sorted) { + // Sort all entries to have a halfway reliable output (if more entries than + // retrieved ones exist, we can not reliably return the same output) if the + // output is not sorted anyway. + output.sort(); + } + const remaining = entries.length - maxLength; + if (remaining > 0) { + Array.prototype.push.call( + output, + `... ${remaining} more item${remaining > 1 ? "s" : ""}`, + ); + } + return output; +} + +// Regex used for ansi escape code splitting +// Adopted from https://github.com/chalk/ansi-regex/blob/HEAD/index.js +// License: MIT, authors: @sindresorhus, Qix-, arjunmehta and LitoMore +// Matches all ansi escape code sequences in a string +const ansiPattern = "[\\u001B\\u009B][[\\]()#;?]*" + + "(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*" + + "|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)" + + "|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))"; +const ansi = new RegExp(ansiPattern, "g"); + +/** + * Returns the number of columns required to display the given string. + */ +export function getStringWidth(str, removeControlChars = true) { + let width = 0; + + if (removeControlChars) { + str = stripVTControlCharacters(str); + } + str = str.normalize("NFC"); + for (const char of str[Symbol.iterator]()) { + const code = char.codePointAt(0); + if (isFullWidthCodePoint(code)) { + width += 2; + } else if (!isZeroWidthCodePoint(code)) { + width++; + } + } + + return width; +} + +/** + * Returns true if the character represented by a given + * Unicode code point is full-width. Otherwise returns false. + */ +const isFullWidthCodePoint = (code) => { + // Code points are partially derived from: + // https://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt + return code >= 0x1100 && ( + code <= 0x115f || // Hangul Jamo + code === 0x2329 || // LEFT-POINTING ANGLE BRACKET + code === 0x232a || // RIGHT-POINTING ANGLE BRACKET + // CJK Radicals Supplement .. Enclosed CJK Letters and Months + (code >= 0x2e80 && code <= 0x3247 && code !== 0x303f) || + // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A + (code >= 0x3250 && code <= 0x4dbf) || + // CJK Unified Ideographs .. Yi Radicals + (code >= 0x4e00 && code <= 0xa4c6) || + // Hangul Jamo Extended-A + (code >= 0xa960 && code <= 0xa97c) || + // Hangul Syllables + (code >= 0xac00 && code <= 0xd7a3) || + // CJK Compatibility Ideographs + (code >= 0xf900 && code <= 0xfaff) || + // Vertical Forms + (code >= 0xfe10 && code <= 0xfe19) || + // CJK Compatibility Forms .. Small Form Variants + (code >= 0xfe30 && code <= 0xfe6b) || + // Halfwidth and Fullwidth Forms + (code >= 0xff01 && code <= 0xff60) || + (code >= 0xffe0 && code <= 0xffe6) || + // Kana Supplement + (code >= 0x1b000 && code <= 0x1b001) || + // Enclosed Ideographic Supplement + (code >= 0x1f200 && code <= 0x1f251) || + // Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff + // Emoticons 0x1f600 - 0x1f64f + (code >= 0x1f300 && code <= 0x1f64f) || + // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane + (code >= 0x20000 && code <= 0x3fffd) + ); +}; + +const isZeroWidthCodePoint = (code) => { + return code <= 0x1F || // C0 control codes + (code >= 0x7F && code <= 0x9F) || // C1 control codes + (code >= 0x300 && code <= 0x36F) || // Combining Diacritical Marks + (code >= 0x200B && code <= 0x200F) || // Modifying Invisible Characters + // Combining Diacritical Marks for Symbols + (code >= 0x20D0 && code <= 0x20FF) || + (code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors + (code >= 0xFE20 && code <= 0xFE2F) || // Combining Half Marks + (code >= 0xE0100 && code <= 0xE01EF); // Variation Selectors +}; + +function hasBuiltInToString(value) { + // TODO(wafuwafu13): Implement + // // Prevent triggering proxy traps. + // const getFullProxy = false; + // const proxyTarget = getProxyDetails(value, getFullProxy); + const proxyTarget = undefined; + if (proxyTarget !== undefined) { + value = proxyTarget; + } + + // Count objects that have no `toString` function as built-in. + if (typeof value.toString !== "function") { + return true; + } + + // The object has a own `toString` property. Thus it's not not a built-in one. + if (Object.prototype.hasOwnProperty.call(value, "toString")) { + return false; + } + + // Find the object that has the `toString` property as own property in the + // prototype chain. + let pointer = value; + do { + pointer = Object.getPrototypeOf(pointer); + } while (!Object.prototype.hasOwnProperty.call(pointer, "toString")); + + // Check closer if the object is a built-in. + const descriptor = Object.getOwnPropertyDescriptor(pointer, "constructor"); + return descriptor !== undefined && + typeof descriptor.value === "function" && + builtInObjects.has(descriptor.value.name); +} + +const firstErrorLine = (error) => error.message.split("\n", 1)[0]; +let CIRCULAR_ERROR_MESSAGE; +function tryStringify(arg) { + try { + return JSON.stringify(arg); + } catch (err) { + // Populate the circular error message lazily + if (!CIRCULAR_ERROR_MESSAGE) { + try { + const a = {}; + a.a = a; + JSON.stringify(a); + } catch (circularError) { + CIRCULAR_ERROR_MESSAGE = firstErrorLine(circularError); + } + } + if ( + err.name === "TypeError" && + firstErrorLine(err) === CIRCULAR_ERROR_MESSAGE + ) { + return "[Circular]"; + } + throw err; + } +} + +export function format(...args) { + return formatWithOptionsInternal(undefined, args); +} + +export function formatWithOptions(inspectOptions, ...args) { + if (typeof inspectOptions !== "object" || inspectOptions === null) { + throw new codes.ERR_INVALID_ARG_TYPE( + "inspectOptions", + "object", + inspectOptions, + ); + } + return formatWithOptionsInternal(inspectOptions, args); +} + +function formatNumberNoColor(number, options) { + return formatNumber( + stylizeNoColor, + number, + options?.numericSeparator ?? inspectDefaultOptions.numericSeparator, + ); +} + +function formatBigIntNoColor(bigint, options) { + return formatBigInt( + stylizeNoColor, + bigint, + options?.numericSeparator ?? inspectDefaultOptions.numericSeparator, + ); +} + +function formatWithOptionsInternal(inspectOptions, args) { + const first = args[0]; + let a = 0; + let str = ""; + let join = ""; + + if (typeof first === "string") { + if (args.length === 1) { + return first; + } + let tempStr; + let lastPos = 0; + + for (let i = 0; i < first.length - 1; i++) { + if (first.charCodeAt(i) === 37) { // '%' + const nextChar = first.charCodeAt(++i); + if (a + 1 !== args.length) { + switch (nextChar) { + // deno-lint-ignore no-case-declarations + case 115: // 's' + const tempArg = args[++a]; + if (typeof tempArg === "number") { + tempStr = formatNumberNoColor(tempArg, inspectOptions); + } else if (typeof tempArg === "bigint") { + tempStr = formatBigIntNoColor(tempArg, inspectOptions); + } else if ( + typeof tempArg !== "object" || + tempArg === null || + !hasBuiltInToString(tempArg) + ) { + tempStr = String(tempArg); + } else { + tempStr = inspect(tempArg, { + ...inspectOptions, + compact: 3, + colors: false, + depth: 0, + }); + } + break; + case 106: // 'j' + tempStr = tryStringify(args[++a]); + break; + // deno-lint-ignore no-case-declarations + case 100: // 'd' + const tempNum = args[++a]; + if (typeof tempNum === "bigint") { + tempStr = formatBigIntNoColor(tempNum, inspectOptions); + } else if (typeof tempNum === "symbol") { + tempStr = "NaN"; + } else { + tempStr = formatNumberNoColor(Number(tempNum), inspectOptions); + } + break; + case 79: // 'O' + tempStr = inspect(args[++a], inspectOptions); + break; + case 111: // 'o' + tempStr = inspect(args[++a], { + ...inspectOptions, + showHidden: true, + showProxy: true, + depth: 4, + }); + break; + // deno-lint-ignore no-case-declarations + case 105: // 'i' + const tempInteger = args[++a]; + if (typeof tempInteger === "bigint") { + tempStr = formatBigIntNoColor(tempInteger, inspectOptions); + } else if (typeof tempInteger === "symbol") { + tempStr = "NaN"; + } else { + tempStr = formatNumberNoColor( + Number.parseInt(tempInteger), + inspectOptions, + ); + } + break; + // deno-lint-ignore no-case-declarations + case 102: // 'f' + const tempFloat = args[++a]; + if (typeof tempFloat === "symbol") { + tempStr = "NaN"; + } else { + tempStr = formatNumberNoColor( + Number.parseFloat(tempFloat), + inspectOptions, + ); + } + break; + case 99: // 'c' + a += 1; + tempStr = ""; + break; + case 37: // '%' + str += first.slice(lastPos, i); + lastPos = i + 1; + continue; + default: // Any other character is not a correct placeholder + continue; + } + if (lastPos !== i - 1) { + str += first.slice(lastPos, i - 1); + } + str += tempStr; + lastPos = i + 1; + } else if (nextChar === 37) { + str += first.slice(lastPos, i); + lastPos = i + 1; + } + } + } + if (lastPos !== 0) { + a++; + join = " "; + if (lastPos < first.length) { + str += first.slice(lastPos); + } + } + } + + while (a < args.length) { + const value = args[a]; + str += join; + str += typeof value !== "string" ? inspect(value, inspectOptions) : value; + join = " "; + a++; + } + return str; +} + +/** + * Remove all VT control characters. Use to estimate displayed string width. + */ +export function stripVTControlCharacters(str) { + validateString(str, "str"); + + return str.replace(ansi, ""); +} + +export default { + format, + getStringWidth, + inspect, + stripVTControlCharacters, + formatWithOptions, +}; diff --git a/ext/node/polyfills/internal/util/types.ts b/ext/node/polyfills/internal/util/types.ts new file mode 100644 index 0000000000..299493bc92 --- /dev/null +++ b/ext/node/polyfills/internal/util/types.ts @@ -0,0 +1,143 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// +// Adapted from Node.js. 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. + +import * as bindingTypes from "internal:deno_node/polyfills/internal_binding/types.ts"; +export { + isCryptoKey, + isKeyObject, +} from "internal:deno_node/polyfills/internal/crypto/_keys.ts"; + +// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag +const _getTypedArrayToStringTag = Object.getOwnPropertyDescriptor( + Object.getPrototypeOf(Uint8Array).prototype, + Symbol.toStringTag, +)!.get!; + +export function isArrayBufferView( + value: unknown, +): value is + | DataView + | BigInt64Array + | BigUint64Array + | Float32Array + | Float64Array + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array { + return ArrayBuffer.isView(value); +} + +export function isBigInt64Array(value: unknown): value is BigInt64Array { + return _getTypedArrayToStringTag.call(value) === "BigInt64Array"; +} + +export function isBigUint64Array(value: unknown): value is BigUint64Array { + return _getTypedArrayToStringTag.call(value) === "BigUint64Array"; +} + +export function isFloat32Array(value: unknown): value is Float32Array { + return _getTypedArrayToStringTag.call(value) === "Float32Array"; +} + +export function isFloat64Array(value: unknown): value is Float64Array { + return _getTypedArrayToStringTag.call(value) === "Float64Array"; +} + +export function isInt8Array(value: unknown): value is Int8Array { + return _getTypedArrayToStringTag.call(value) === "Int8Array"; +} + +export function isInt16Array(value: unknown): value is Int16Array { + return _getTypedArrayToStringTag.call(value) === "Int16Array"; +} + +export function isInt32Array(value: unknown): value is Int32Array { + return _getTypedArrayToStringTag.call(value) === "Int32Array"; +} + +export function isTypedArray(value: unknown): value is + | BigInt64Array + | BigUint64Array + | Float32Array + | Float64Array + | Int8Array + | Int16Array + | Int32Array + | Uint8Array + | Uint8ClampedArray + | Uint16Array + | Uint32Array { + return _getTypedArrayToStringTag.call(value) !== undefined; +} + +export function isUint8Array(value: unknown): value is Uint8Array { + return _getTypedArrayToStringTag.call(value) === "Uint8Array"; +} + +export function isUint8ClampedArray( + value: unknown, +): value is Uint8ClampedArray { + return _getTypedArrayToStringTag.call(value) === "Uint8ClampedArray"; +} + +export function isUint16Array(value: unknown): value is Uint16Array { + return _getTypedArrayToStringTag.call(value) === "Uint16Array"; +} + +export function isUint32Array(value: unknown): value is Uint32Array { + return _getTypedArrayToStringTag.call(value) === "Uint32Array"; +} + +export const { + // isExternal, + isDate, + isArgumentsObject, + isBigIntObject, + isBooleanObject, + isNumberObject, + isStringObject, + isSymbolObject, + isNativeError, + isRegExp, + isAsyncFunction, + isGeneratorFunction, + isGeneratorObject, + isPromise, + isMap, + isSet, + isMapIterator, + isSetIterator, + isWeakMap, + isWeakSet, + isArrayBuffer, + isDataView, + isSharedArrayBuffer, + isProxy, + isModuleNamespaceObject, + isAnyArrayBuffer, + isBoxedPrimitive, +} = bindingTypes; diff --git a/ext/node/polyfills/internal/validators.mjs b/ext/node/polyfills/internal/validators.mjs new file mode 100644 index 0000000000..bea9e881ad --- /dev/null +++ b/ext/node/polyfills/internal/validators.mjs @@ -0,0 +1,317 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; +import { hideStackFrames } from "internal:deno_node/polyfills/internal/hide_stack_frames.ts"; +import { isArrayBufferView } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { normalizeEncoding } from "internal:deno_node/polyfills/internal/normalize_encoding.mjs"; + +/** + * @param {number} value + * @returns {boolean} + */ +function isInt32(value) { + return value === (value | 0); +} + +/** + * @param {unknown} value + * @returns {boolean} + */ +function isUint32(value) { + return value === (value >>> 0); +} + +const octalReg = /^[0-7]+$/; +const modeDesc = "must be a 32-bit unsigned integer or an octal string"; + +/** + * Parse and validate values that will be converted into mode_t (the S_* + * constants). Only valid numbers and octal strings are allowed. They could be + * converted to 32-bit unsigned integers or non-negative signed integers in the + * C++ land, but any value higher than 0o777 will result in platform-specific + * behaviors. + * + * @param {*} value Values to be validated + * @param {string} name Name of the argument + * @param {number} [def] If specified, will be returned for invalid values + * @returns {number} + */ +function parseFileMode(value, name, def) { + value ??= def; + if (typeof value === "string") { + if (!octalReg.test(value)) { + throw new codes.ERR_INVALID_ARG_VALUE(name, value, modeDesc); + } + value = Number.parseInt(value, 8); + } + + validateInt32(value, name, 0, 2 ** 32 - 1); + return value; +} + +const validateBuffer = hideStackFrames((buffer, name = "buffer") => { + if (!isArrayBufferView(buffer)) { + throw new codes.ERR_INVALID_ARG_TYPE( + name, + ["Buffer", "TypedArray", "DataView"], + buffer, + ); + } +}); + +const validateInteger = hideStackFrames( + ( + value, + name, + min = Number.MIN_SAFE_INTEGER, + max = Number.MAX_SAFE_INTEGER, + ) => { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value); + } + if (value < min || value > max) { + throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + * @param {{ + * allowArray?: boolean, + * allowFunction?: boolean, + * nullable?: boolean + * }} [options] + */ +const validateObject = hideStackFrames((value, name, options) => { + const useDefaultOptions = options == null; + const allowArray = useDefaultOptions ? false : options.allowArray; + const allowFunction = useDefaultOptions ? false : options.allowFunction; + const nullable = useDefaultOptions ? false : options.nullable; + if ( + (!nullable && value === null) || + (!allowArray && Array.isArray(value)) || + (typeof value !== "object" && ( + !allowFunction || typeof value !== "function" + )) + ) { + throw new codes.ERR_INVALID_ARG_TYPE(name, "Object", value); + } +}); + +const validateInt32 = hideStackFrames( + (value, name, min = -2147483648, max = 2147483647) => { + // The defaults for min and max correspond to the limits of 32-bit integers. + if (!isInt32(value)) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } + + if (!Number.isInteger(value)) { + throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value); + } + + throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } + + if (value < min || value > max) { + throw new codes.ERR_OUT_OF_RANGE(name, `>= ${min} && <= ${max}`, value); + } + }, +); + +const validateUint32 = hideStackFrames( + (value, name, positive) => { + if (!isUint32(value)) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } + if (!Number.isInteger(value)) { + throw new codes.ERR_OUT_OF_RANGE(name, "an integer", value); + } + const min = positive ? 1 : 0; + // 2 ** 32 === 4294967296 + throw new codes.ERR_OUT_OF_RANGE( + name, + `>= ${min} && < 4294967296`, + value, + ); + } + if (positive && value === 0) { + throw new codes.ERR_OUT_OF_RANGE(name, ">= 1 && < 4294967296", value); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + */ +function validateString(value, name) { + if (typeof value !== "string") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "string", value); + } +} + +/** + * @param {unknown} value + * @param {string} name + */ +function validateNumber(value, name) { + if (typeof value !== "number") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "number", value); + } +} + +/** + * @param {unknown} value + * @param {string} name + */ +function validateBoolean(value, name) { + if (typeof value !== "boolean") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "boolean", value); + } +} + +/** + * @param {unknown} value + * @param {string} name + * @param {unknown[]} oneOf + */ +const validateOneOf = hideStackFrames( + (value, name, oneOf) => { + if (!Array.prototype.includes.call(oneOf, value)) { + const allowed = Array.prototype.join.call( + Array.prototype.map.call( + oneOf, + (v) => (typeof v === "string" ? `'${v}'` : String(v)), + ), + ", ", + ); + const reason = "must be one of: " + allowed; + + throw new codes.ERR_INVALID_ARG_VALUE(name, value, reason); + } + }, +); + +export function validateEncoding(data, encoding) { + const normalizedEncoding = normalizeEncoding(encoding); + const length = data.length; + + if (normalizedEncoding === "hex" && length % 2 !== 0) { + throw new codes.ERR_INVALID_ARG_VALUE( + "encoding", + encoding, + `is invalid for data of length ${length}`, + ); + } +} + +// Check that the port number is not NaN when coerced to a number, +// is an integer and that it falls within the legal range of port numbers. +/** + * @param {string} name + * @returns {number} + */ +function validatePort(port, name = "Port", allowZero = true) { + if ( + (typeof port !== "number" && typeof port !== "string") || + (typeof port === "string" && + String.prototype.trim.call(port).length === 0) || + +port !== (+port >>> 0) || + port > 0xFFFF || + (port === 0 && !allowZero) + ) { + throw new codes.ERR_SOCKET_BAD_PORT(name, port, allowZero); + } + + return port; +} + +/** + * @param {unknown} signal + * @param {string} name + */ +const validateAbortSignal = hideStackFrames( + (signal, name) => { + if ( + signal !== undefined && + (signal === null || + typeof signal !== "object" || + !("aborted" in signal)) + ) { + throw new codes.ERR_INVALID_ARG_TYPE(name, "AbortSignal", signal); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + */ +const validateFunction = hideStackFrames( + (value, name) => { + if (typeof value !== "function") { + throw new codes.ERR_INVALID_ARG_TYPE(name, "Function", value); + } + }, +); + +/** + * @param {unknown} value + * @param {string} name + */ +const validateArray = hideStackFrames( + (value, name, minLength = 0) => { + if (!Array.isArray(value)) { + throw new codes.ERR_INVALID_ARG_TYPE(name, "Array", value); + } + if (value.length < minLength) { + const reason = `must be longer than ${minLength}`; + throw new codes.ERR_INVALID_ARG_VALUE(name, value, reason); + } + }, +); + +export default { + isInt32, + isUint32, + parseFileMode, + validateAbortSignal, + validateArray, + validateBoolean, + validateBuffer, + validateFunction, + validateInt32, + validateInteger, + validateNumber, + validateObject, + validateOneOf, + validatePort, + validateString, + validateUint32, +}; +export { + isInt32, + isUint32, + parseFileMode, + validateAbortSignal, + validateArray, + validateBoolean, + validateBuffer, + validateFunction, + validateInt32, + validateInteger, + validateNumber, + validateObject, + validateOneOf, + validatePort, + validateString, + validateUint32, +}; diff --git a/ext/node/polyfills/internal_binding/README.md b/ext/node/polyfills/internal_binding/README.md new file mode 100644 index 0000000000..903d33cd66 --- /dev/null +++ b/ext/node/polyfills/internal_binding/README.md @@ -0,0 +1,11 @@ +# Internal Bindings + +The modules in this directory implement (simulate) C++ bindings implemented in +the `./src/` directory of the [Node.js](https://github.com/nodejs/node) +repository. + +These bindings are created in the Node.js source code by using +`NODE_MODULE_CONTEXT_AWARE_INTERNAL`. + +Please refer to for +further information. diff --git a/ext/node/polyfills/internal_binding/_libuv_winerror.ts b/ext/node/polyfills/internal_binding/_libuv_winerror.ts new file mode 100644 index 0000000000..cb489bf322 --- /dev/null +++ b/ext/node/polyfills/internal_binding/_libuv_winerror.ts @@ -0,0 +1,229 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * 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. + */ + +// This module ports: +// - https://github.com/libuv/libuv/blob/master/src/win/error.c + +import * as winErrors from "internal:deno_node/polyfills/internal_binding/_winerror.ts"; + +export function uvTranslateSysError(sysErrno: number): string { + switch (sysErrno) { + case winErrors.ERROR_ACCESS_DENIED: + return "EACCES"; + case winErrors.ERROR_NOACCESS: + return "EACCES"; + case winErrors.WSAEACCES: + return "EACCES"; + // case winErrors.ERROR_ELEVATION_REQUIRED: return "EACCES"; + case winErrors.ERROR_CANT_ACCESS_FILE: + return "EACCES"; + case winErrors.ERROR_ADDRESS_ALREADY_ASSOCIATED: + return "EADDRINUSE"; + case winErrors.WSAEADDRINUSE: + return "EADDRINUSE"; + case winErrors.WSAEADDRNOTAVAIL: + return "EADDRNOTAVAIL"; + case winErrors.WSAEAFNOSUPPORT: + return "EAFNOSUPPORT"; + case winErrors.WSAEWOULDBLOCK: + return "EAGAIN"; + case winErrors.WSAEALREADY: + return "EALREADY"; + case winErrors.ERROR_INVALID_FLAGS: + return "EBADF"; + case winErrors.ERROR_INVALID_HANDLE: + return "EBADF"; + case winErrors.ERROR_LOCK_VIOLATION: + return "EBUSY"; + case winErrors.ERROR_PIPE_BUSY: + return "EBUSY"; + case winErrors.ERROR_SHARING_VIOLATION: + return "EBUSY"; + case winErrors.ERROR_OPERATION_ABORTED: + return "ECANCELED"; + case winErrors.WSAEINTR: + return "ECANCELED"; + case winErrors.ERROR_NO_UNICODE_TRANSLATION: + return "ECHARSET"; + case winErrors.ERROR_CONNECTION_ABORTED: + return "ECONNABORTED"; + case winErrors.WSAECONNABORTED: + return "ECONNABORTED"; + case winErrors.ERROR_CONNECTION_REFUSED: + return "ECONNREFUSED"; + case winErrors.WSAECONNREFUSED: + return "ECONNREFUSED"; + case winErrors.ERROR_NETNAME_DELETED: + return "ECONNRESET"; + case winErrors.WSAECONNRESET: + return "ECONNRESET"; + case winErrors.ERROR_ALREADY_EXISTS: + return "EEXIST"; + case winErrors.ERROR_FILE_EXISTS: + return "EEXIST"; + case winErrors.ERROR_BUFFER_OVERFLOW: + return "EFAULT"; + case winErrors.WSAEFAULT: + return "EFAULT"; + case winErrors.ERROR_HOST_UNREACHABLE: + return "EHOSTUNREACH"; + case winErrors.WSAEHOSTUNREACH: + return "EHOSTUNREACH"; + case winErrors.ERROR_INSUFFICIENT_BUFFER: + return "EINVAL"; + case winErrors.ERROR_INVALID_DATA: + return "EINVAL"; + case winErrors.ERROR_INVALID_NAME: + return "EINVAL"; + case winErrors.ERROR_INVALID_PARAMETER: + return "EINVAL"; + // case winErrors.ERROR_SYMLINK_NOT_SUPPORTED: return "EINVAL"; + case winErrors.WSAEINVAL: + return "EINVAL"; + case winErrors.WSAEPFNOSUPPORT: + return "EINVAL"; + case winErrors.ERROR_BEGINNING_OF_MEDIA: + return "EIO"; + case winErrors.ERROR_BUS_RESET: + return "EIO"; + case winErrors.ERROR_CRC: + return "EIO"; + case winErrors.ERROR_DEVICE_DOOR_OPEN: + return "EIO"; + case winErrors.ERROR_DEVICE_REQUIRES_CLEANING: + return "EIO"; + case winErrors.ERROR_DISK_CORRUPT: + return "EIO"; + case winErrors.ERROR_EOM_OVERFLOW: + return "EIO"; + case winErrors.ERROR_FILEMARK_DETECTED: + return "EIO"; + case winErrors.ERROR_GEN_FAILURE: + return "EIO"; + case winErrors.ERROR_INVALID_BLOCK_LENGTH: + return "EIO"; + case winErrors.ERROR_IO_DEVICE: + return "EIO"; + case winErrors.ERROR_NO_DATA_DETECTED: + return "EIO"; + case winErrors.ERROR_NO_SIGNAL_SENT: + return "EIO"; + case winErrors.ERROR_OPEN_FAILED: + return "EIO"; + case winErrors.ERROR_SETMARK_DETECTED: + return "EIO"; + case winErrors.ERROR_SIGNAL_REFUSED: + return "EIO"; + case winErrors.WSAEISCONN: + return "EISCONN"; + case winErrors.ERROR_CANT_RESOLVE_FILENAME: + return "ELOOP"; + case winErrors.ERROR_TOO_MANY_OPEN_FILES: + return "EMFILE"; + case winErrors.WSAEMFILE: + return "EMFILE"; + case winErrors.WSAEMSGSIZE: + return "EMSGSIZE"; + case winErrors.ERROR_FILENAME_EXCED_RANGE: + return "ENAMETOOLONG"; + case winErrors.ERROR_NETWORK_UNREACHABLE: + return "ENETUNREACH"; + case winErrors.WSAENETUNREACH: + return "ENETUNREACH"; + case winErrors.WSAENOBUFS: + return "ENOBUFS"; + case winErrors.ERROR_BAD_PATHNAME: + return "ENOENT"; + case winErrors.ERROR_DIRECTORY: + return "ENOTDIR"; + case winErrors.ERROR_ENVVAR_NOT_FOUND: + return "ENOENT"; + case winErrors.ERROR_FILE_NOT_FOUND: + return "ENOENT"; + case winErrors.ERROR_INVALID_DRIVE: + return "ENOENT"; + case winErrors.ERROR_INVALID_REPARSE_DATA: + return "ENOENT"; + case winErrors.ERROR_MOD_NOT_FOUND: + return "ENOENT"; + case winErrors.ERROR_PATH_NOT_FOUND: + return "ENOENT"; + case winErrors.WSAHOST_NOT_FOUND: + return "ENOENT"; + case winErrors.WSANO_DATA: + return "ENOENT"; + case winErrors.ERROR_NOT_ENOUGH_MEMORY: + return "ENOMEM"; + case winErrors.ERROR_OUTOFMEMORY: + return "ENOMEM"; + case winErrors.ERROR_CANNOT_MAKE: + return "ENOSPC"; + case winErrors.ERROR_DISK_FULL: + return "ENOSPC"; + case winErrors.ERROR_EA_TABLE_FULL: + return "ENOSPC"; + case winErrors.ERROR_END_OF_MEDIA: + return "ENOSPC"; + case winErrors.ERROR_HANDLE_DISK_FULL: + return "ENOSPC"; + case winErrors.ERROR_NOT_CONNECTED: + return "ENOTCONN"; + case winErrors.WSAENOTCONN: + return "ENOTCONN"; + case winErrors.ERROR_DIR_NOT_EMPTY: + return "ENOTEMPTY"; + case winErrors.WSAENOTSOCK: + return "ENOTSOCK"; + case winErrors.ERROR_NOT_SUPPORTED: + return "ENOTSUP"; + case winErrors.ERROR_BROKEN_PIPE: + return "EOF"; + case winErrors.ERROR_PRIVILEGE_NOT_HELD: + return "EPERM"; + case winErrors.ERROR_BAD_PIPE: + return "EPIPE"; + case winErrors.ERROR_NO_DATA: + return "EPIPE"; + case winErrors.ERROR_PIPE_NOT_CONNECTED: + return "EPIPE"; + case winErrors.WSAESHUTDOWN: + return "EPIPE"; + case winErrors.WSAEPROTONOSUPPORT: + return "EPROTONOSUPPORT"; + case winErrors.ERROR_WRITE_PROTECT: + return "EROFS"; + case winErrors.ERROR_SEM_TIMEOUT: + return "ETIMEDOUT"; + case winErrors.WSAETIMEDOUT: + return "ETIMEDOUT"; + case winErrors.ERROR_NOT_SAME_DEVICE: + return "EXDEV"; + case winErrors.ERROR_INVALID_FUNCTION: + return "EISDIR"; + case winErrors.ERROR_META_EXPANSION_TOO_LONG: + return "E2BIG"; + case winErrors.WSAESOCKTNOSUPPORT: + return "ESOCKTNOSUPPORT"; + default: + return "UNKNOWN"; + } +} diff --git a/ext/node/polyfills/internal_binding/_listen.ts b/ext/node/polyfills/internal_binding/_listen.ts new file mode 100644 index 0000000000..d801b05484 --- /dev/null +++ b/ext/node/polyfills/internal_binding/_listen.ts @@ -0,0 +1,16 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +/** + * @param n Number to act on. + * @return The number rounded up to the nearest power of 2. + */ +export function ceilPowOf2(n: number) { + const roundPowOf2 = 1 << (31 - Math.clz32(n)); + + return roundPowOf2 < n ? roundPowOf2 * 2 : roundPowOf2; +} + +/** Initial backoff delay of 5ms following a temporary accept failure. */ +export const INITIAL_ACCEPT_BACKOFF_DELAY = 5; + +/** Max backoff delay of 1s following a temporary accept failure. */ +export const MAX_ACCEPT_BACKOFF_DELAY = 1000; diff --git a/ext/node/polyfills/internal_binding/_node.ts b/ext/node/polyfills/internal_binding/_node.ts new file mode 100644 index 0000000000..47a289dda7 --- /dev/null +++ b/ext/node/polyfills/internal_binding/_node.ts @@ -0,0 +1,18 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// This file contains C++ node globals accesed in internal binding calls + +/** + * Adapted from + * https://github.com/nodejs/node/blob/3b72788afb7365e10ae1e97c71d1f60ee29f09f2/src/node.h#L728-L738 + */ +export enum Encodings { + ASCII, // 0 + UTF8, // 1 + BASE64, // 2 + UCS2, // 3 + BINARY, // 4 + HEX, // 5 + BUFFER, // 6 + BASE64URL, // 7 + LATIN1 = 4, // 4 = BINARY +} diff --git a/ext/node/polyfills/internal_binding/_timingSafeEqual.ts b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts new file mode 100644 index 0000000000..9002300d13 --- /dev/null +++ b/ext/node/polyfills/internal_binding/_timingSafeEqual.ts @@ -0,0 +1,43 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +function assert(cond) { + if (!cond) { + throw new Error("assertion failed"); + } +} + +/** Compare to array buffers or data views in a way that timing based attacks + * cannot gain information about the platform. */ +function stdTimingSafeEqual( + a: ArrayBufferView | ArrayBufferLike | DataView, + b: ArrayBufferView | ArrayBufferLike | DataView, +): boolean { + if (a.byteLength !== b.byteLength) { + return false; + } + if (!(a instanceof DataView)) { + a = new DataView(ArrayBuffer.isView(a) ? a.buffer : a); + } + if (!(b instanceof DataView)) { + b = new DataView(ArrayBuffer.isView(b) ? b.buffer : b); + } + assert(a instanceof DataView); + assert(b instanceof DataView); + const length = a.byteLength; + let out = 0; + let i = -1; + while (++i < length) { + out |= a.getUint8(i) ^ b.getUint8(i); + } + return out === 0; +} + +export const timingSafeEqual = ( + a: Buffer | DataView | ArrayBuffer, + b: Buffer | DataView | ArrayBuffer, +): boolean => { + if (a instanceof Buffer) a = new DataView(a.buffer); + if (a instanceof Buffer) b = new DataView(a.buffer); + return stdTimingSafeEqual(a, b); +}; diff --git a/ext/node/polyfills/internal_binding/_utils.ts b/ext/node/polyfills/internal_binding/_utils.ts new file mode 100644 index 0000000000..4d545df0e2 --- /dev/null +++ b/ext/node/polyfills/internal_binding/_utils.ts @@ -0,0 +1,88 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { + forgivingBase64Decode, + forgivingBase64UrlEncode, +} from "internal:deno_web/00_infra.js"; + +export function asciiToBytes(str: string) { + const byteArray = []; + for (let i = 0; i < str.length; ++i) { + byteArray.push(str.charCodeAt(i) & 255); + } + return new Uint8Array(byteArray); +} + +export function base64ToBytes(str: string) { + str = base64clean(str); + str = str.replaceAll("-", "+").replaceAll("_", "/"); + return forgivingBase64Decode(str); +} + +const INVALID_BASE64_RE = /[^+/0-9A-Za-z-_]/g; +function base64clean(str: string) { + // Node takes equal signs as end of the Base64 encoding + str = str.split("=")[0]; + // Node strips out invalid characters like \n and \t from the string, std/base64 does not + str = str.trim().replace(INVALID_BASE64_RE, ""); + // Node converts strings with length < 2 to '' + if (str.length < 2) return ""; + // Node allows for non-padded base64 strings (missing trailing ===), std/base64 does not + while (str.length % 4 !== 0) { + str = str + "="; + } + return str; +} + +export function base64UrlToBytes(str: string) { + str = base64clean(str); + str = str.replaceAll("+", "-").replaceAll("/", "_"); + return forgivingBase64UrlEncode(str); +} + +export function hexToBytes(str: string) { + const byteArray = new Uint8Array(Math.floor((str || "").length / 2)); + let i; + for (i = 0; i < byteArray.length; i++) { + const a = Number.parseInt(str[i * 2], 16); + const b = Number.parseInt(str[i * 2 + 1], 16); + if (Number.isNaN(a) && Number.isNaN(b)) { + break; + } + byteArray[i] = (a << 4) | b; + } + return new Uint8Array( + i === byteArray.length ? byteArray : byteArray.slice(0, i), + ); +} + +export function utf16leToBytes(str: string, units: number) { + let c, hi, lo; + const byteArray = []; + for (let i = 0; i < str.length; ++i) { + if ((units -= 2) < 0) { + break; + } + c = str.charCodeAt(i); + hi = c >> 8; + lo = c % 256; + byteArray.push(lo); + byteArray.push(hi); + } + return new Uint8Array(byteArray); +} + +export function bytesToAscii(bytes: Uint8Array) { + let ret = ""; + for (let i = 0; i < bytes.length; ++i) { + ret += String.fromCharCode(bytes[i] & 127); + } + return ret; +} + +export function bytesToUtf16le(bytes: Uint8Array) { + let res = ""; + for (let i = 0; i < bytes.length - 1; i += 2) { + res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256); + } + return res; +} diff --git a/ext/node/polyfills/internal_binding/_winerror.ts b/ext/node/polyfills/internal_binding/_winerror.ts new file mode 100644 index 0000000000..e8c381302b --- /dev/null +++ b/ext/node/polyfills/internal_binding/_winerror.ts @@ -0,0 +1,16873 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved. + +You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt). +If you do not agree to the terms, do not use the code. + +Module: + + winderror.h + +Abstract: + + Win32 API functions + +--*/ + +// This module ports: +// - https://raw.githubusercontent.com/mic101/windows/master/WRK-v1.2/public/sdk/inc/winerror.h + +// MessageId: ERROR_SUCCESS +// +// MessageText: +// +// The operation completed successfully. +// +export const ERROR_SUCCESS = 0; + +// +// MessageId: ERROR_INVALID_FUNCTION +// +// MessageText: +// +// Incorrect function. +// +export const ERROR_INVALID_FUNCTION = 1; // dderror + +// +// MessageId: ERROR_FILE_NOT_FOUND +// +// MessageText: +// +// The system cannot find the file specified. +// +export const ERROR_FILE_NOT_FOUND = 2; + +// +// MessageId: ERROR_PATH_NOT_FOUND +// +// MessageText: +// +// The system cannot find the path specified. +// +export const ERROR_PATH_NOT_FOUND = 3; + +// +// MessageId: ERROR_TOO_MANY_OPEN_FILES +// +// MessageText: +// +// The system cannot open the file. +// +export const ERROR_TOO_MANY_OPEN_FILES = 4; + +// +// MessageId: ERROR_ACCESS_DENIED +// +// MessageText: +// +// Access is denied. +// +export const ERROR_ACCESS_DENIED = 5; + +// +// MessageId: ERROR_INVALID_HANDLE +// +// MessageText: +// +// The handle is invalid. +// +export const ERROR_INVALID_HANDLE = 6; + +// +// MessageId: ERROR_ARENA_TRASHED +// +// MessageText: +// +// The storage control blocks were destroyed. +// +export const ERROR_ARENA_TRASHED = 7; + +// +// MessageId: ERROR_NOT_ENOUGH_MEMORY +// +// MessageText: +// +// Not enough storage is available to process this command. +// +export const ERROR_NOT_ENOUGH_MEMORY = 8; // dderror + +// +// MessageId: ERROR_INVALID_BLOCK +// +// MessageText: +// +// The storage control block address is invalid. +// +export const ERROR_INVALID_BLOCK = 9; + +// +// MessageId: ERROR_BAD_ENVIRONMENT +// +// MessageText: +// +// The environment is incorrect. +// +export const ERROR_BAD_ENVIRONMENT = 10; + +// +// MessageId: ERROR_BAD_FORMAT +// +// MessageText: +// +// An attempt was made to load a program with an incorrect format. +// +export const ERROR_BAD_FORMAT = 11; + +// +// MessageId: ERROR_INVALID_ACCESS +// +// MessageText: +// +// The access code is invalid. +// +export const ERROR_INVALID_ACCESS = 12; + +// +// MessageId: ERROR_INVALID_DATA +// +// MessageText: +// +// The data is invalid. +// +export const ERROR_INVALID_DATA = 13; + +// +// MessageId: ERROR_OUTOFMEMORY +// +// MessageText: +// +// Not enough storage is available to complete this operation. +// +export const ERROR_OUTOFMEMORY = 14; + +// +// MessageId: ERROR_INVALID_DRIVE +// +// MessageText: +// +// The system cannot find the drive specified. +// +export const ERROR_INVALID_DRIVE = 15; + +// +// MessageId: ERROR_CURRENT_DIRECTORY +// +// MessageText: +// +// The directory cannot be removed. +// +export const ERROR_CURRENT_DIRECTORY = 16; + +// +// MessageId: ERROR_NOT_SAME_DEVICE +// +// MessageText: +// +// The system cannot move the file to a different disk drive. +// +export const ERROR_NOT_SAME_DEVICE = 17; + +// +// MessageId: ERROR_NO_MORE_FILES +// +// MessageText: +// +// There are no more files. +// +export const ERROR_NO_MORE_FILES = 18; + +// +// MessageId: ERROR_WRITE_PROTECT +// +// MessageText: +// +// The media is write protected. +// +export const ERROR_WRITE_PROTECT = 19; + +// +// MessageId: ERROR_BAD_UNIT +// +// MessageText: +// +// The system cannot find the device specified. +// +export const ERROR_BAD_UNIT = 20; + +// +// MessageId: ERROR_NOT_READY +// +// MessageText: +// +// The device is not ready. +// +export const ERROR_NOT_READY = 21; + +// +// MessageId: ERROR_BAD_COMMAND +// +// MessageText: +// +// The device does not recognize the command. +// +export const ERROR_BAD_COMMAND = 22; + +// +// MessageId: ERROR_CRC +// +// MessageText: +// +// Data error (cyclic redundancy check). +// +export const ERROR_CRC = 23; + +// +// MessageId: ERROR_BAD_LENGTH +// +// MessageText: +// +// The program issued a command but the command length is incorrect. +// +export const ERROR_BAD_LENGTH = 24; + +// +// MessageId: ERROR_SEEK +// +// MessageText: +// +// The drive cannot locate a specific area or track on the disk. +// +export const ERROR_SEEK = 25; + +// +// MessageId: ERROR_NOT_DOS_DISK +// +// MessageText: +// +// The specified disk or diskette cannot be accessed. +// +export const ERROR_NOT_DOS_DISK = 26; + +// +// MessageId: ERROR_SECTOR_NOT_FOUND +// +// MessageText: +// +// The drive cannot find the sector requested. +// +export const ERROR_SECTOR_NOT_FOUND = 27; + +// +// MessageId: ERROR_OUT_OF_PAPER +// +// MessageText: +// +// The printer is out of paper. +// +export const ERROR_OUT_OF_PAPER = 28; + +// +// MessageId: ERROR_WRITE_FAULT +// +// MessageText: +// +// The system cannot write to the specified device. +// +export const ERROR_WRITE_FAULT = 29; + +// +// MessageId: ERROR_READ_FAULT +// +// MessageText: +// +// The system cannot read from the specified device. +// +export const ERROR_READ_FAULT = 30; + +// +// MessageId: ERROR_GEN_FAILURE +// +// MessageText: +// +// A device attached to the system is not functioning. +// +export const ERROR_GEN_FAILURE = 31; + +// +// MessageId: ERROR_SHARING_VIOLATION +// +// MessageText: +// +// The process cannot access the file because it is being used by another process. +// +export const ERROR_SHARING_VIOLATION = 32; + +// +// MessageId: ERROR_LOCK_VIOLATION +// +// MessageText: +// +// The process cannot access the file because another process has locked a portion of the file. +// +export const ERROR_LOCK_VIOLATION = 33; + +// +// MessageId: ERROR_WRONG_DISK +// +// MessageText: +// +// The wrong diskette is in the drive. +// Insert %2 (Volume Serial Number: %3) into drive %1. +// +export const ERROR_WRONG_DISK = 34; + +// +// MessageId: ERROR_SHARING_BUFFER_EXCEEDED +// +// MessageText: +// +// Too many files opened for sharing. +// +export const ERROR_SHARING_BUFFER_EXCEEDED = 36; + +// +// MessageId: ERROR_HANDLE_EOF +// +// MessageText: +// +// Reached the end of the file. +// +export const ERROR_HANDLE_EOF = 38; + +// +// MessageId: ERROR_HANDLE_DISK_FULL +// +// MessageText: +// +// The disk is full. +// +export const ERROR_HANDLE_DISK_FULL = 39; + +// +// MessageId: ERROR_NOT_SUPPORTED +// +// MessageText: +// +// The request is not supported. +// +export const ERROR_NOT_SUPPORTED = 50; + +// +// MessageId: ERROR_REM_NOT_LIST +// +// MessageText: +// +// Windows cannot find the network path. Verify that the network path is correct and the destination computer is not busy or turned off. If Windows still cannot find the network path, contact your network administrator. +// +export const ERROR_REM_NOT_LIST = 51; + +// +// MessageId: ERROR_DUP_NAME +// +// MessageText: +// +// You were not connected because a duplicate name exists on the network. Go to System in Control Panel to change the computer name and try again. +// +export const ERROR_DUP_NAME = 52; + +// +// MessageId: ERROR_BAD_NETPATH +// +// MessageText: +// +// The network path was not found. +// +export const ERROR_BAD_NETPATH = 53; + +// +// MessageId: ERROR_NETWORK_BUSY +// +// MessageText: +// +// The network is busy. +// +export const ERROR_NETWORK_BUSY = 54; + +// +// MessageId: ERROR_DEV_NOT_EXIST +// +// MessageText: +// +// The specified network resource or device is no longer available. +// +export const ERROR_DEV_NOT_EXIST = 55; // dderror + +// +// MessageId: ERROR_TOO_MANY_CMDS +// +// MessageText: +// +// The network BIOS command limit has been reached. +// +export const ERROR_TOO_MANY_CMDS = 56; + +// +// MessageId: ERROR_ADAP_HDW_ERR +// +// MessageText: +// +// A network adapter hardware error occurred. +// +export const ERROR_ADAP_HDW_ERR = 57; + +// +// MessageId: ERROR_BAD_NET_RESP +// +// MessageText: +// +// The specified server cannot perform the requested operation. +// +export const ERROR_BAD_NET_RESP = 58; + +// +// MessageId: ERROR_UNEXP_NET_ERR +// +// MessageText: +// +// An unexpected network error occurred. +// +export const ERROR_UNEXP_NET_ERR = 59; + +// +// MessageId: ERROR_BAD_REM_ADAP +// +// MessageText: +// +// The remote adapter is not compatible. +// +export const ERROR_BAD_REM_ADAP = 60; + +// +// MessageId: ERROR_PRINTQ_FULL +// +// MessageText: +// +// The printer queue is full. +// +export const ERROR_PRINTQ_FULL = 61; + +// +// MessageId: ERROR_NO_SPOOL_SPACE +// +// MessageText: +// +// Space to store the file waiting to be printed is not available on the server. +// +export const ERROR_NO_SPOOL_SPACE = 62; + +// +// MessageId: ERROR_PRINT_CANCELLED +// +// MessageText: +// +// Your file waiting to be printed was deleted. +// +export const ERROR_PRINT_CANCELLED = 63; + +// +// MessageId: ERROR_NETNAME_DELETED +// +// MessageText: +// +// The specified network name is no longer available. +// +export const ERROR_NETNAME_DELETED = 64; + +// +// MessageId: ERROR_NETWORK_ACCESS_DENIED +// +// MessageText: +// +// Network access is denied. +// +export const ERROR_NETWORK_ACCESS_DENIED = 65; + +// +// MessageId: ERROR_BAD_DEV_TYPE +// +// MessageText: +// +// The network resource type is not correct. +// +export const ERROR_BAD_DEV_TYPE = 66; + +// +// MessageId: ERROR_BAD_NET_NAME +// +// MessageText: +// +// The network name cannot be found. +// +export const ERROR_BAD_NET_NAME = 67; + +// +// MessageId: ERROR_TOO_MANY_NAMES +// +// MessageText: +// +// The name limit for the local computer network adapter card was exceeded. +// +export const ERROR_TOO_MANY_NAMES = 68; + +// +// MessageId: ERROR_TOO_MANY_SESS +// +// MessageText: +// +// The network BIOS session limit was exceeded. +// +export const ERROR_TOO_MANY_SESS = 69; + +// +// MessageId: ERROR_SHARING_PAUSED +// +// MessageText: +// +// The remote server has been paused or is in the process of being started. +// +export const ERROR_SHARING_PAUSED = 70; + +// +// MessageId: ERROR_REQ_NOT_ACCEP +// +// MessageText: +// +// No more connections can be made to this remote computer at this time because there are already as many connections as the computer can accept. +// +export const ERROR_REQ_NOT_ACCEP = 71; + +// +// MessageId: ERROR_REDIR_PAUSED +// +// MessageText: +// +// The specified printer or disk device has been paused. +// +export const ERROR_REDIR_PAUSED = 72; + +// +// MessageId: ERROR_FILE_EXISTS +// +// MessageText: +// +// The file exists. +// +export const ERROR_FILE_EXISTS = 80; + +// +// MessageId: ERROR_CANNOT_MAKE +// +// MessageText: +// +// The directory or file cannot be created. +// +export const ERROR_CANNOT_MAKE = 82; + +// +// MessageId: ERROR_FAIL_I24 +// +// MessageText: +// +// Fail on INT 24. +// +export const ERROR_FAIL_I24 = 83; + +// +// MessageId: ERROR_OUT_OF_STRUCTURES +// +// MessageText: +// +// Storage to process this request is not available. +// +export const ERROR_OUT_OF_STRUCTURES = 84; + +// +// MessageId: ERROR_ALREADY_ASSIGNED +// +// MessageText: +// +// The local device name is already in use. +// +export const ERROR_ALREADY_ASSIGNED = 85; + +// +// MessageId: ERROR_INVALID_PASSWORD +// +// MessageText: +// +// The specified network password is not correct. +// +export const ERROR_INVALID_PASSWORD = 86; + +// +// MessageId: ERROR_INVALID_PARAMETER +// +// MessageText: +// +// The parameter is incorrect. +// +export const ERROR_INVALID_PARAMETER = 87; // dderror + +// +// MessageId: ERROR_NET_WRITE_FAULT +// +// MessageText: +// +// A write fault occurred on the network. +// +export const ERROR_NET_WRITE_FAULT = 88; + +// +// MessageId: ERROR_NO_PROC_SLOTS +// +// MessageText: +// +// The system cannot start another process at this time. +// +export const ERROR_NO_PROC_SLOTS = 89; + +// +// MessageId: ERROR_TOO_MANY_SEMAPHORES +// +// MessageText: +// +// Cannot create another system semaphore. +// +export const ERROR_TOO_MANY_SEMAPHORES = 100; + +// +// MessageId: ERROR_EXCL_SEM_ALREADY_OWNED +// +// MessageText: +// +// The exclusive semaphore is owned by another process. +// +export const ERROR_EXCL_SEM_ALREADY_OWNED = 101; + +// +// MessageId: ERROR_SEM_IS_SET +// +// MessageText: +// +// The semaphore is set and cannot be closed. +// +export const ERROR_SEM_IS_SET = 102; + +// +// MessageId: ERROR_TOO_MANY_SEM_REQUESTS +// +// MessageText: +// +// The semaphore cannot be set again. +// +export const ERROR_TOO_MANY_SEM_REQUESTS = 103; + +// +// MessageId: ERROR_INVALID_AT_INTERRUPT_TIME +// +// MessageText: +// +// Cannot request exclusive semaphores at interrupt time. +// +export const ERROR_INVALID_AT_INTERRUPT_TIME = 104; + +// +// MessageId: ERROR_SEM_OWNER_DIED +// +// MessageText: +// +// The previous ownership of this semaphore has ended. +// +export const ERROR_SEM_OWNER_DIED = 105; + +// +// MessageId: ERROR_SEM_USER_LIMIT +// +// MessageText: +// +// Insert the diskette for drive %1. +// +export const ERROR_SEM_USER_LIMIT = 106; + +// +// MessageId: ERROR_DISK_CHANGE +// +// MessageText: +// +// The program stopped because an alternate diskette was not inserted. +// +export const ERROR_DISK_CHANGE = 107; + +// +// MessageId: ERROR_DRIVE_LOCKED +// +// MessageText: +// +// The disk is in use or locked by another process. +// +export const ERROR_DRIVE_LOCKED = 108; + +// +// MessageId: ERROR_BROKEN_PIPE +// +// MessageText: +// +// The pipe has been ended. +// +export const ERROR_BROKEN_PIPE = 109; + +// +// MessageId: ERROR_OPEN_FAILED +// +// MessageText: +// +// The system cannot open the device or file specified. +// +export const ERROR_OPEN_FAILED = 110; + +// +// MessageId: ERROR_BUFFER_OVERFLOW +// +// MessageText: +// +// The file name is too long. +// +export const ERROR_BUFFER_OVERFLOW = 111; + +// +// MessageId: ERROR_DISK_FULL +// +// MessageText: +// +// There is not enough space on the disk. +// +export const ERROR_DISK_FULL = 112; + +// +// MessageId: ERROR_NO_MORE_SEARCH_HANDLES +// +// MessageText: +// +// No more internal file identifiers available. +// +export const ERROR_NO_MORE_SEARCH_HANDLES = 113; + +// +// MessageId: ERROR_INVALID_TARGET_HANDLE +// +// MessageText: +// +// The target internal file identifier is incorrect. +// +export const ERROR_INVALID_TARGET_HANDLE = 114; + +// +// MessageId: ERROR_INVALID_CATEGORY +// +// MessageText: +// +// The IOCTL call made by the application program is not correct. +// +export const ERROR_INVALID_CATEGORY = 117; + +// +// MessageId: ERROR_INVALID_VERIFY_SWITCH +// +// MessageText: +// +// The verify-on-write switch parameter value is not correct. +// +export const ERROR_INVALID_VERIFY_SWITCH = 118; + +// +// MessageId: ERROR_BAD_DRIVER_LEVEL +// +// MessageText: +// +// The system does not support the command requested. +// +export const ERROR_BAD_DRIVER_LEVEL = 119; + +// +// MessageId: ERROR_CALL_NOT_IMPLEMENTED +// +// MessageText: +// +// This function is not supported on this system. +// +export const ERROR_CALL_NOT_IMPLEMENTED = 120; + +// +// MessageId: ERROR_SEM_TIMEOUT +// +// MessageText: +// +// The semaphore timeout period has expired. +// +export const ERROR_SEM_TIMEOUT = 121; + +// +// MessageId: ERROR_INSUFFICIENT_BUFFER +// +// MessageText: +// +// The data area passed to a system call is too small. +// +export const ERROR_INSUFFICIENT_BUFFER = 122; // dderror + +// +// MessageId: ERROR_INVALID_NAME +// +// MessageText: +// +// The filename, directory name, or volume label syntax is incorrect. +// +export const ERROR_INVALID_NAME = 123; // dderror + +// +// MessageId: ERROR_INVALID_LEVEL +// +// MessageText: +// +// The system call level is not correct. +// +export const ERROR_INVALID_LEVEL = 124; + +// +// MessageId: ERROR_NO_VOLUME_LABEL +// +// MessageText: +// +// The disk has no volume label. +// +export const ERROR_NO_VOLUME_LABEL = 125; + +// +// MessageId: ERROR_MOD_NOT_FOUND +// +// MessageText: +// +// The specified module could not be found. +// +export const ERROR_MOD_NOT_FOUND = 126; + +// +// MessageId: ERROR_PROC_NOT_FOUND +// +// MessageText: +// +// The specified procedure could not be found. +// +export const ERROR_PROC_NOT_FOUND = 127; + +// +// MessageId: ERROR_WAIT_NO_CHILDREN +// +// MessageText: +// +// There are no child processes to wait for. +// +export const ERROR_WAIT_NO_CHILDREN = 128; + +// +// MessageId: ERROR_CHILD_NOT_COMPLETE +// +// MessageText: +// +// The %1 application cannot be run in Win32 mode. +// +export const ERROR_CHILD_NOT_COMPLETE = 129; + +// +// MessageId: ERROR_DIRECT_ACCESS_HANDLE +// +// MessageText: +// +// Attempt to use a file handle to an open disk partition for an operation other than raw disk I/O. +// +export const ERROR_DIRECT_ACCESS_HANDLE = 130; + +// +// MessageId: ERROR_NEGATIVE_SEEK +// +// MessageText: +// +// An attempt was made to move the file pointer before the beginning of the file. +// +export const ERROR_NEGATIVE_SEEK = 131; + +// +// MessageId: ERROR_SEEK_ON_DEVICE +// +// MessageText: +// +// The file pointer cannot be set on the specified device or file. +// +export const ERROR_SEEK_ON_DEVICE = 132; + +// +// MessageId: ERROR_IS_JOIN_TARGET +// +// MessageText: +// +// A JOIN or SUBST command cannot be used for a drive that contains previously joined drives. +// +export const ERROR_IS_JOIN_TARGET = 133; + +// +// MessageId: ERROR_IS_JOINED +// +// MessageText: +// +// An attempt was made to use a JOIN or SUBST command on a drive that has already been joined. +// +export const ERROR_IS_JOINED = 134; + +// +// MessageId: ERROR_IS_SUBSTED +// +// MessageText: +// +// An attempt was made to use a JOIN or SUBST command on a drive that has already been substituted. +// +export const ERROR_IS_SUBSTED = 135; + +// +// MessageId: ERROR_NOT_JOINED +// +// MessageText: +// +// The system tried to delete the JOIN of a drive that is not joined. +// +export const ERROR_NOT_JOINED = 136; + +// +// MessageId: ERROR_NOT_SUBSTED +// +// MessageText: +// +// The system tried to delete the substitution of a drive that is not substituted. +// +export const ERROR_NOT_SUBSTED = 137; + +// +// MessageId: ERROR_JOIN_TO_JOIN +// +// MessageText: +// +// The system tried to join a drive to a directory on a joined drive. +// +export const ERROR_JOIN_TO_JOIN = 138; + +// +// MessageId: ERROR_SUBST_TO_SUBST +// +// MessageText: +// +// The system tried to substitute a drive to a directory on a substituted drive. +// +export const ERROR_SUBST_TO_SUBST = 139; + +// +// MessageId: ERROR_JOIN_TO_SUBST +// +// MessageText: +// +// The system tried to join a drive to a directory on a substituted drive. +// +export const ERROR_JOIN_TO_SUBST = 140; + +// +// MessageId: ERROR_SUBST_TO_JOIN +// +// MessageText: +// +// The system tried to SUBST a drive to a directory on a joined drive. +// +export const ERROR_SUBST_TO_JOIN = 141; + +// +// MessageId: ERROR_BUSY_DRIVE +// +// MessageText: +// +// The system cannot perform a JOIN or SUBST at this time. +// +export const ERROR_BUSY_DRIVE = 142; + +// +// MessageId: ERROR_SAME_DRIVE +// +// MessageText: +// +// The system cannot join or substitute a drive to or for a directory on the same drive. +// +export const ERROR_SAME_DRIVE = 143; + +// +// MessageId: ERROR_DIR_NOT_ROOT +// +// MessageText: +// +// The directory is not a subdirectory of the root directory. +// +export const ERROR_DIR_NOT_ROOT = 144; + +// +// MessageId: ERROR_DIR_NOT_EMPTY +// +// MessageText: +// +// The directory is not empty. +// +export const ERROR_DIR_NOT_EMPTY = 145; + +// +// MessageId: ERROR_IS_SUBST_PATH +// +// MessageText: +// +// The path specified is being used in a substitute. +// +export const ERROR_IS_SUBST_PATH = 146; + +// +// MessageId: ERROR_IS_JOIN_PATH +// +// MessageText: +// +// Not enough resources are available to process this command. +// +export const ERROR_IS_JOIN_PATH = 147; + +// +// MessageId: ERROR_PATH_BUSY +// +// MessageText: +// +// The path specified cannot be used at this time. +// +export const ERROR_PATH_BUSY = 148; + +// +// MessageId: ERROR_IS_SUBST_TARGET +// +// MessageText: +// +// An attempt was made to join or substitute a drive for which a directory on the drive is the target of a previous substitute. +// +export const ERROR_IS_SUBST_TARGET = 149; + +// +// MessageId: ERROR_SYSTEM_TRACE +// +// MessageText: +// +// System trace information was not specified in your CONFIG.SYS file, or tracing is disallowed. +// +export const ERROR_SYSTEM_TRACE = 150; + +// +// MessageId: ERROR_INVALID_EVENT_COUNT +// +// MessageText: +// +// The number of specified semaphore events for DosMuxSemWait is not correct. +// +export const ERROR_INVALID_EVENT_COUNT = 151; + +// +// MessageId: ERROR_TOO_MANY_MUXWAITERS +// +// MessageText: +// +// DosMuxSemWait did not execute; too many semaphores are already set. +// +export const ERROR_TOO_MANY_MUXWAITERS = 152; + +// +// MessageId: ERROR_INVALID_LIST_FORMAT +// +// MessageText: +// +// The DosMuxSemWait list is not correct. +// +export const ERROR_INVALID_LIST_FORMAT = 153; + +// +// MessageId: ERROR_LABEL_TOO_LONG +// +// MessageText: +// +// The volume label you entered exceeds the label character limit of the target file system. +// +export const ERROR_LABEL_TOO_LONG = 154; + +// +// MessageId: ERROR_TOO_MANY_TCBS +// +// MessageText: +// +// Cannot create another thread. +// +export const ERROR_TOO_MANY_TCBS = 155; + +// +// MessageId: ERROR_SIGNAL_REFUSED +// +// MessageText: +// +// The recipient process has refused the signal. +// +export const ERROR_SIGNAL_REFUSED = 156; + +// +// MessageId: ERROR_DISCARDED +// +// MessageText: +// +// The segment is already discarded and cannot be locked. +// +export const ERROR_DISCARDED = 157; + +// +// MessageId: ERROR_NOT_LOCKED +// +// MessageText: +// +// The segment is already unlocked. +// +export const ERROR_NOT_LOCKED = 158; + +// +// MessageId: ERROR_BAD_THREADID_ADDR +// +// MessageText: +// +// The address for the thread ID is not correct. +// +export const ERROR_BAD_THREADID_ADDR = 159; + +// +// MessageId: ERROR_BAD_ARGUMENTS +// +// MessageText: +// +// One or more arguments are not correct. +// +export const ERROR_BAD_ARGUMENTS = 160; + +// +// MessageId: ERROR_BAD_PATHNAME +// +// MessageText: +// +// The specified path is invalid. +// +export const ERROR_BAD_PATHNAME = 161; + +// +// MessageId: ERROR_SIGNAL_PENDING +// +// MessageText: +// +// A signal is already pending. +// +export const ERROR_SIGNAL_PENDING = 162; + +// +// MessageId: ERROR_MAX_THRDS_REACHED +// +// MessageText: +// +// No more threads can be created in the system. +// +export const ERROR_MAX_THRDS_REACHED = 164; + +// +// MessageId: ERROR_LOCK_FAILED +// +// MessageText: +// +// Unable to lock a region of a file. +// +export const ERROR_LOCK_FAILED = 167; + +// +// MessageId: ERROR_BUSY +// +// MessageText: +// +// The requested resource is in use. +// +export const ERROR_BUSY = 170; // dderror + +// +// MessageId: ERROR_CANCEL_VIOLATION +// +// MessageText: +// +// A lock request was not outstanding for the supplied cancel region. +// +export const ERROR_CANCEL_VIOLATION = 173; + +// +// MessageId: ERROR_ATOMIC_LOCKS_NOT_SUPPORTED +// +// MessageText: +// +// The file system does not support atomic changes to the lock type. +// +export const ERROR_ATOMIC_LOCKS_NOT_SUPPORTED = 174; + +// +// MessageId: ERROR_INVALID_SEGMENT_NUMBER +// +// MessageText: +// +// The system detected a segment number that was not correct. +// +export const ERROR_INVALID_SEGMENT_NUMBER = 180; + +// +// MessageId: ERROR_INVALID_ORDINAL +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_INVALID_ORDINAL = 182; + +// +// MessageId: ERROR_ALREADY_EXISTS +// +// MessageText: +// +// Cannot create a file when that file already exists. +// +export const ERROR_ALREADY_EXISTS = 183; + +// +// MessageId: ERROR_INVALID_FLAG_NUMBER +// +// MessageText: +// +// The flag passed is not correct. +// +export const ERROR_INVALID_FLAG_NUMBER = 186; + +// +// MessageId: ERROR_SEM_NOT_FOUND +// +// MessageText: +// +// The specified system semaphore name was not found. +// +export const ERROR_SEM_NOT_FOUND = 187; + +// +// MessageId: ERROR_INVALID_STARTING_CODESEG +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_INVALID_STARTING_CODESEG = 188; + +// +// MessageId: ERROR_INVALID_STACKSEG +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_INVALID_STACKSEG = 189; + +// +// MessageId: ERROR_INVALID_MODULETYPE +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_INVALID_MODULETYPE = 190; + +// +// MessageId: ERROR_INVALID_EXE_SIGNATURE +// +// MessageText: +// +// Cannot run %1 in Win32 mode. +// +export const ERROR_INVALID_EXE_SIGNATURE = 191; + +// +// MessageId: ERROR_EXE_MARKED_INVALID +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_EXE_MARKED_INVALID = 192; + +// +// MessageId: ERROR_BAD_EXE_FORMAT +// +// MessageText: +// +// %1 is not a valid Win32 application. +// +export const ERROR_BAD_EXE_FORMAT = 193; + +// +// MessageId: ERROR_ITERATED_DATA_EXCEEDS_64k +// +// MessageText: +// +// The operating system cannot run %1. +// +// deno-lint-ignore camelcase +export const ERROR_ITERATED_DATA_EXCEEDS_64k = 194; + +// +// MessageId: ERROR_INVALID_MINALLOCSIZE +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_INVALID_MINALLOCSIZE = 195; + +// +// MessageId: ERROR_DYNLINK_FROM_INVALID_RING +// +// MessageText: +// +// The operating system cannot run this application program. +// +export const ERROR_DYNLINK_FROM_INVALID_RING = 196; + +// +// MessageId: ERROR_IOPL_NOT_ENABLED +// +// MessageText: +// +// The operating system is not presently configured to run this application. +// +export const ERROR_IOPL_NOT_ENABLED = 197; + +// +// MessageId: ERROR_INVALID_SEGDPL +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_INVALID_SEGDPL = 198; + +// +// MessageId: ERROR_AUTODATASEG_EXCEEDS_64k +// +// MessageText: +// +// The operating system cannot run this application program. +// +// deno-lint-ignore camelcase +export const ERROR_AUTODATASEG_EXCEEDS_64k = 199; + +// +// MessageId: ERROR_RING2SEG_MUST_BE_MOVABLE +// +// MessageText: +// +// The code segment cannot be greater than or equal to 64K. +// +export const ERROR_RING2SEG_MUST_BE_MOVABLE = 200; + +// +// MessageId: ERROR_RELOC_CHAIN_XEEDS_SEGLIM +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_RELOC_CHAIN_XEEDS_SEGLIM = 201; + +// +// MessageId: ERROR_INFLOOP_IN_RELOC_CHAIN +// +// MessageText: +// +// The operating system cannot run %1. +// +export const ERROR_INFLOOP_IN_RELOC_CHAIN = 202; + +// +// MessageId: ERROR_ENVVAR_NOT_FOUND +// +// MessageText: +// +// The system could not find the environment option that was entered. +// +export const ERROR_ENVVAR_NOT_FOUND = 203; + +// +// MessageId: ERROR_NO_SIGNAL_SENT +// +// MessageText: +// +// No process in the command subtree has a signal handler. +// +export const ERROR_NO_SIGNAL_SENT = 205; + +// +// MessageId: ERROR_FILENAME_EXCED_RANGE +// +// MessageText: +// +// The filename or extension is too long. +// +export const ERROR_FILENAME_EXCED_RANGE = 206; + +// +// MessageId: ERROR_RING2_STACK_IN_USE +// +// MessageText: +// +// The ring 2 stack is in use. +// +export const ERROR_RING2_STACK_IN_USE = 207; + +// +// MessageId: ERROR_META_EXPANSION_TOO_LONG +// +// MessageText: +// +// The global filename characters, * or ?, are entered incorrectly or too many global filename characters are specified. +// +export const ERROR_META_EXPANSION_TOO_LONG = 208; + +// +// MessageId: ERROR_INVALID_SIGNAL_NUMBER +// +// MessageText: +// +// The signal being posted is not correct. +// +export const ERROR_INVALID_SIGNAL_NUMBER = 209; + +// +// MessageId: ERROR_THREAD_1_INACTIVE +// +// MessageText: +// +// The signal handler cannot be set. +// +export const ERROR_THREAD_1_INACTIVE = 210; + +// +// MessageId: ERROR_LOCKED +// +// MessageText: +// +// The segment is locked and cannot be reallocated. +// +export const ERROR_LOCKED = 212; + +// +// MessageId: ERROR_TOO_MANY_MODULES +// +// MessageText: +// +// Too many dynamic-link modules are attached to this program or dynamic-link module. +// +export const ERROR_TOO_MANY_MODULES = 214; + +// +// MessageId: ERROR_NESTING_NOT_ALLOWED +// +// MessageText: +// +// Cannot nest calls to LoadModule. +// +export const ERROR_NESTING_NOT_ALLOWED = 215; + +// +// MessageId: ERROR_EXE_MACHINE_TYPE_MISMATCH +// +// MessageText: +// +// The image file %1 is valid, but is for a machine type other than the current machine. +// +export const ERROR_EXE_MACHINE_TYPE_MISMATCH = 216; + +// +// MessageId: ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY +// +// MessageText: +// +// The image file %1 is signed, unable to modify. +// +export const ERROR_EXE_CANNOT_MODIFY_SIGNED_BINARY = 217; + +// +// MessageId: ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY +// +// MessageText: +// +// The image file %1 is strong signed, unable to modify. +// +export const ERROR_EXE_CANNOT_MODIFY_STRONG_SIGNED_BINARY = 218; + +// +// MessageId: ERROR_BAD_PIPE +// +// MessageText: +// +// The pipe state is invalid. +// +export const ERROR_BAD_PIPE = 230; + +// +// MessageId: ERROR_PIPE_BUSY +// +// MessageText: +// +// All pipe instances are busy. +// +export const ERROR_PIPE_BUSY = 231; + +// +// MessageId: ERROR_NO_DATA +// +// MessageText: +// +// The pipe is being closed. +// +export const ERROR_NO_DATA = 232; + +// +// MessageId: ERROR_PIPE_NOT_CONNECTED +// +// MessageText: +// +// No process is on the other end of the pipe. +// +export const ERROR_PIPE_NOT_CONNECTED = 233; + +// +// MessageId: ERROR_MORE_DATA +// +// MessageText: +// +// More data is available. +// +export const ERROR_MORE_DATA = 234; // dderror + +// +// MessageId: ERROR_VC_DISCONNECTED +// +// MessageText: +// +// The session was canceled. +// +export const ERROR_VC_DISCONNECTED = 240; + +// +// MessageId: ERROR_INVALID_EA_NAME +// +// MessageText: +// +// The specified extended attribute name was invalid. +// +export const ERROR_INVALID_EA_NAME = 254; + +// +// MessageId: ERROR_EA_LIST_INCONSISTENT +// +// MessageText: +// +// The extended attributes are inconsistent. +// +export const ERROR_EA_LIST_INCONSISTENT = 255; + +// +// MessageId: WAIT_TIMEOUT +// +// MessageText: +// +// The wait operation timed out. +// +export const WAIT_TIMEOUT = 258; // dderror + +// +// MessageId: ERROR_NO_MORE_ITEMS +// +// MessageText: +// +// No more data is available. +// +export const ERROR_NO_MORE_ITEMS = 259; + +// +// MessageId: ERROR_CANNOT_COPY +// +// MessageText: +// +// The copy functions cannot be used. +// +export const ERROR_CANNOT_COPY = 266; + +// +// MessageId: ERROR_DIRECTORY +// +// MessageText: +// +// The directory name is invalid. +// +export const ERROR_DIRECTORY = 267; + +// +// MessageId: ERROR_EAS_DIDNT_FIT +// +// MessageText: +// +// The extended attributes did not fit in the buffer. +// +export const ERROR_EAS_DIDNT_FIT = 275; + +// +// MessageId: ERROR_EA_FILE_CORRUPT +// +// MessageText: +// +// The extended attribute file on the mounted file system is corrupt. +// +export const ERROR_EA_FILE_CORRUPT = 276; + +// +// MessageId: ERROR_EA_TABLE_FULL +// +// MessageText: +// +// The extended attribute table file is full. +// +export const ERROR_EA_TABLE_FULL = 277; + +// +// MessageId: ERROR_INVALID_EA_HANDLE +// +// MessageText: +// +// The specified extended attribute handle is invalid. +// +export const ERROR_INVALID_EA_HANDLE = 278; + +// +// MessageId: ERROR_EAS_NOT_SUPPORTED +// +// MessageText: +// +// The mounted file system does not support extended attributes. +// +export const ERROR_EAS_NOT_SUPPORTED = 282; + +// +// MessageId: ERROR_NOT_OWNER +// +// MessageText: +// +// Attempt to release mutex not owned by caller. +// +export const ERROR_NOT_OWNER = 288; + +// +// MessageId: ERROR_TOO_MANY_POSTS +// +// MessageText: +// +// Too many posts were made to a semaphore. +// +export const ERROR_TOO_MANY_POSTS = 298; + +// +// MessageId: ERROR_PARTIAL_COPY +// +// MessageText: +// +// Only part of a ReadProcessMemory or WriteProcessMemory request was completed. +// +export const ERROR_PARTIAL_COPY = 299; + +// +// MessageId: ERROR_OPLOCK_NOT_GRANTED +// +// MessageText: +// +// The oplock request is denied. +// +export const ERROR_OPLOCK_NOT_GRANTED = 300; + +// +// MessageId: ERROR_INVALID_OPLOCK_PROTOCOL +// +// MessageText: +// +// An invalid oplock acknowledgment was received by the system. +// +export const ERROR_INVALID_OPLOCK_PROTOCOL = 301; + +// +// MessageId: ERROR_DISK_TOO_FRAGMENTED +// +// MessageText: +// +// The volume is too fragmented to complete this operation. +// +export const ERROR_DISK_TOO_FRAGMENTED = 302; + +// +// MessageId: ERROR_DELETE_PENDING +// +// MessageText: +// +// The file cannot be opened because it is in the process of being deleted. +// +export const ERROR_DELETE_PENDING = 303; + +// +// MessageId: ERROR_MR_MID_NOT_FOUND +// +// MessageText: +// +// The system cannot find message text for message number 0x%1 in the message file for %2. +// +export const ERROR_MR_MID_NOT_FOUND = 317; + +// +// MessageId: ERROR_SCOPE_NOT_FOUND +// +// MessageText: +// +// The scope specified was not found. +// +export const ERROR_SCOPE_NOT_FOUND = 318; + +// +// MessageId: ERROR_INVALID_ADDRESS +// +// MessageText: +// +// Attempt to access invalid address. +// +export const ERROR_INVALID_ADDRESS = 487; + +// +// MessageId: ERROR_ARITHMETIC_OVERFLOW +// +// MessageText: +// +// Arithmetic result exceeded 32 bits. +// +export const ERROR_ARITHMETIC_OVERFLOW = 534; + +// +// MessageId: ERROR_PIPE_CONNECTED +// +// MessageText: +// +// There is a process on other end of the pipe. +// +export const ERROR_PIPE_CONNECTED = 535; + +// +// MessageId: ERROR_PIPE_LISTENING +// +// MessageText: +// +// Waiting for a process to open the other end of the pipe. +// +export const ERROR_PIPE_LISTENING = 536; + +// +// MessageId: ERROR_EA_ACCESS_DENIED +// +// MessageText: +// +// Access to the extended attribute was denied. +// +export const ERROR_EA_ACCESS_DENIED = 994; + +// +// MessageId: ERROR_OPERATION_ABORTED +// +// MessageText: +// +// The I/O operation has been aborted because of either a thread exit or an application request. +// +export const ERROR_OPERATION_ABORTED = 995; + +// +// MessageId: ERROR_IO_INCOMPLETE +// +// MessageText: +// +// Overlapped I/O event is not in a signaled state. +// +export const ERROR_IO_INCOMPLETE = 996; + +// +// MessageId: ERROR_IO_PENDING +// +// MessageText: +// +// Overlapped I/O operation is in progress. +// +export const ERROR_IO_PENDING = 997; // dderror + +// +// MessageId: ERROR_NOACCESS +// +// MessageText: +// +// Invalid access to memory location. +// +export const ERROR_NOACCESS = 998; + +// +// MessageId: ERROR_SWAPERROR +// +// MessageText: +// +// Error performing inpage operation. +// +export const ERROR_SWAPERROR = 999; + +// +// MessageId: ERROR_STACK_OVERFLOW +// +// MessageText: +// +// Recursion too deep; the stack overflowed. +// +export const ERROR_STACK_OVERFLOW = 1001; + +// +// MessageId: ERROR_INVALID_MESSAGE +// +// MessageText: +// +// The window cannot act on the sent message. +// +export const ERROR_INVALID_MESSAGE = 1002; + +// +// MessageId: ERROR_CAN_NOT_COMPLETE +// +// MessageText: +// +// Cannot complete this function. +// +export const ERROR_CAN_NOT_COMPLETE = 1003; + +// +// MessageId: ERROR_INVALID_FLAGS +// +// MessageText: +// +// Invalid flags. +// +export const ERROR_INVALID_FLAGS = 1004; + +// +// MessageId: ERROR_UNRECOGNIZED_VOLUME +// +// MessageText: +// +// The volume does not contain a recognized file system. +// Please make sure that all required file system drivers are loaded and that the volume is not corrupted. +// +export const ERROR_UNRECOGNIZED_VOLUME = 1005; + +// +// MessageId: ERROR_FILE_INVALID +// +// MessageText: +// +// The volume for a file has been externally altered so that the opened file is no longer valid. +// +export const ERROR_FILE_INVALID = 1006; + +// +// MessageId: ERROR_FULLSCREEN_MODE +// +// MessageText: +// +// The requested operation cannot be performed in full-screen mode. +// +export const ERROR_FULLSCREEN_MODE = 1007; + +// +// MessageId: ERROR_NO_TOKEN +// +// MessageText: +// +// An attempt was made to reference a token that does not exist. +// +export const ERROR_NO_TOKEN = 1008; + +// +// MessageId: ERROR_BADDB +// +// MessageText: +// +// The configuration registry database is corrupt. +// +export const ERROR_BADDB = 1009; + +// +// MessageId: ERROR_BADKEY +// +// MessageText: +// +// The configuration registry key is invalid. +// +export const ERROR_BADKEY = 1010; + +// +// MessageId: ERROR_CANTOPEN +// +// MessageText: +// +// The configuration registry key could not be opened. +// +export const ERROR_CANTOPEN = 1011; + +// +// MessageId: ERROR_CANTREAD +// +// MessageText: +// +// The configuration registry key could not be read. +// +export const ERROR_CANTREAD = 1012; + +// +// MessageId: ERROR_CANTWRITE +// +// MessageText: +// +// The configuration registry key could not be written. +// +export const ERROR_CANTWRITE = 1013; + +// +// MessageId: ERROR_REGISTRY_RECOVERED +// +// MessageText: +// +// One of the files in the registry database had to be recovered by use of a log or alternate copy. The recovery was successful. +// +export const ERROR_REGISTRY_RECOVERED = 1014; + +// +// MessageId: ERROR_REGISTRY_CORRUPT +// +// MessageText: +// +// The registry is corrupted. The structure of one of the files containing registry data is corrupted, or the system's memory image of the file is corrupted, or the file could not be recovered because the alternate copy or log was absent or corrupted. +// +export const ERROR_REGISTRY_CORRUPT = 1015; + +// +// MessageId: ERROR_REGISTRY_IO_FAILED +// +// MessageText: +// +// An I/O operation initiated by the registry failed unrecoverably. The registry could not read in, or write out, or flush, one of the files that contain the system's image of the registry. +// +export const ERROR_REGISTRY_IO_FAILED = 1016; + +// +// MessageId: ERROR_NOT_REGISTRY_FILE +// +// MessageText: +// +// The system has attempted to load or restore a file into the registry, but the specified file is not in a registry file format. +// +export const ERROR_NOT_REGISTRY_FILE = 1017; + +// +// MessageId: ERROR_KEY_DELETED +// +// MessageText: +// +// Illegal operation attempted on a registry key that has been marked for deletion. +// +export const ERROR_KEY_DELETED = 1018; + +// +// MessageId: ERROR_NO_LOG_SPACE +// +// MessageText: +// +// System could not allocate the required space in a registry log. +// +export const ERROR_NO_LOG_SPACE = 1019; + +// +// MessageId: ERROR_KEY_HAS_CHILDREN +// +// MessageText: +// +// Cannot create a symbolic link in a registry key that already has subkeys or values. +// +export const ERROR_KEY_HAS_CHILDREN = 1020; + +// +// MessageId: ERROR_CHILD_MUST_BE_VOLATILE +// +// MessageText: +// +// Cannot create a stable subkey under a volatile parent key. +// +export const ERROR_CHILD_MUST_BE_VOLATILE = 1021; + +// +// MessageId: ERROR_NOTIFY_ENUM_DIR +// +// MessageText: +// +// A notify change request is being completed and the information is not being returned in the caller's buffer. The caller now needs to enumerate the files to find the changes. +// +export const ERROR_NOTIFY_ENUM_DIR = 1022; + +// +// MessageId: ERROR_DEPENDENT_SERVICES_RUNNING +// +// MessageText: +// +// A stop control has been sent to a service that other running services are dependent on. +// +export const ERROR_DEPENDENT_SERVICES_RUNNING = 1051; + +// +// MessageId: ERROR_INVALID_SERVICE_CONTROL +// +// MessageText: +// +// The requested control is not valid for this service. +// +export const ERROR_INVALID_SERVICE_CONTROL = 1052; + +// +// MessageId: ERROR_SERVICE_REQUEST_TIMEOUT +// +// MessageText: +// +// The service did not respond to the start or control request in a timely fashion. +// +export const ERROR_SERVICE_REQUEST_TIMEOUT = 1053; + +// +// MessageId: ERROR_SERVICE_NO_THREAD +// +// MessageText: +// +// A thread could not be created for the service. +// +export const ERROR_SERVICE_NO_THREAD = 1054; + +// +// MessageId: ERROR_SERVICE_DATABASE_LOCKED +// +// MessageText: +// +// The service database is locked. +// +export const ERROR_SERVICE_DATABASE_LOCKED = 1055; + +// +// MessageId: ERROR_SERVICE_ALREADY_RUNNING +// +// MessageText: +// +// An instance of the service is already running. +// +export const ERROR_SERVICE_ALREADY_RUNNING = 1056; + +// +// MessageId: ERROR_INVALID_SERVICE_ACCOUNT +// +// MessageText: +// +// The account name is invalid or does not exist, or the password is invalid for the account name specified. +// +export const ERROR_INVALID_SERVICE_ACCOUNT = 1057; + +// +// MessageId: ERROR_SERVICE_DISABLED +// +// MessageText: +// +// The service cannot be started, either because it is disabled or because it has no enabled devices associated with it. +// +export const ERROR_SERVICE_DISABLED = 1058; + +// +// MessageId: ERROR_CIRCULAR_DEPENDENCY +// +// MessageText: +// +// Circular service dependency was specified. +// +export const ERROR_CIRCULAR_DEPENDENCY = 1059; + +// +// MessageId: ERROR_SERVICE_DOES_NOT_EXIST +// +// MessageText: +// +// The specified service does not exist as an installed service. +// +export const ERROR_SERVICE_DOES_NOT_EXIST = 1060; + +// +// MessageId: ERROR_SERVICE_CANNOT_ACCEPT_CTRL +// +// MessageText: +// +// The service cannot accept control messages at this time. +// +export const ERROR_SERVICE_CANNOT_ACCEPT_CTRL = 1061; + +// +// MessageId: ERROR_SERVICE_NOT_ACTIVE +// +// MessageText: +// +// The service has not been started. +// +export const ERROR_SERVICE_NOT_ACTIVE = 1062; + +// +// MessageId: ERROR_FAILED_SERVICE_CONTROLLER_CONNECT +// +// MessageText: +// +// The service process could not connect to the service controller. +// +export const ERROR_FAILED_SERVICE_CONTROLLER_CONNECT = 1063; + +// +// MessageId: ERROR_EXCEPTION_IN_SERVICE +// +// MessageText: +// +// An exception occurred in the service when handling the control request. +// +export const ERROR_EXCEPTION_IN_SERVICE = 1064; + +// +// MessageId: ERROR_DATABASE_DOES_NOT_EXIST +// +// MessageText: +// +// The database specified does not exist. +// +export const ERROR_DATABASE_DOES_NOT_EXIST = 1065; + +// +// MessageId: ERROR_SERVICE_SPECIFIC_ERROR +// +// MessageText: +// +// The service has returned a service-specific error code. +// +export const ERROR_SERVICE_SPECIFIC_ERROR = 1066; + +// +// MessageId: ERROR_PROCESS_ABORTED +// +// MessageText: +// +// The process terminated unexpectedly. +// +export const ERROR_PROCESS_ABORTED = 1067; + +// +// MessageId: ERROR_SERVICE_DEPENDENCY_FAIL +// +// MessageText: +// +// The dependency service or group failed to start. +// +export const ERROR_SERVICE_DEPENDENCY_FAIL = 1068; + +// +// MessageId: ERROR_SERVICE_LOGON_FAILED +// +// MessageText: +// +// The service did not start due to a logon failure. +// +export const ERROR_SERVICE_LOGON_FAILED = 1069; + +// +// MessageId: ERROR_SERVICE_START_HANG +// +// MessageText: +// +// After starting, the service hung in a start-pending state. +// +export const ERROR_SERVICE_START_HANG = 1070; + +// +// MessageId: ERROR_INVALID_SERVICE_LOCK +// +// MessageText: +// +// The specified service database lock is invalid. +// +export const ERROR_INVALID_SERVICE_LOCK = 1071; + +// +// MessageId: ERROR_SERVICE_MARKED_FOR_DELETE +// +// MessageText: +// +// The specified service has been marked for deletion. +// +export const ERROR_SERVICE_MARKED_FOR_DELETE = 1072; + +// +// MessageId: ERROR_SERVICE_EXISTS +// +// MessageText: +// +// The specified service already exists. +// +export const ERROR_SERVICE_EXISTS = 1073; + +// +// MessageId: ERROR_ALREADY_RUNNING_LKG +// +// MessageText: +// +// The system is currently running with the last-known-good configuration. +// +export const ERROR_ALREADY_RUNNING_LKG = 1074; + +// +// MessageId: ERROR_SERVICE_DEPENDENCY_DELETED +// +// MessageText: +// +// The dependency service does not exist or has been marked for deletion. +// +export const ERROR_SERVICE_DEPENDENCY_DELETED = 1075; + +// +// MessageId: ERROR_BOOT_ALREADY_ACCEPTED +// +// MessageText: +// +// The current boot has already been accepted for use as the last-known-good control set. +// +export const ERROR_BOOT_ALREADY_ACCEPTED = 1076; + +// +// MessageId: ERROR_SERVICE_NEVER_STARTED +// +// MessageText: +// +// No attempts to start the service have been made since the last boot. +// +export const ERROR_SERVICE_NEVER_STARTED = 1077; + +// +// MessageId: ERROR_DUPLICATE_SERVICE_NAME +// +// MessageText: +// +// The name is already in use as either a service name or a service display name. +// +export const ERROR_DUPLICATE_SERVICE_NAME = 1078; + +// +// MessageId: ERROR_DIFFERENT_SERVICE_ACCOUNT +// +// MessageText: +// +// The account specified for this service is different from the account specified for other services running in the same process. +// +export const ERROR_DIFFERENT_SERVICE_ACCOUNT = 1079; + +// +// MessageId: ERROR_CANNOT_DETECT_DRIVER_FAILURE +// +// MessageText: +// +// Failure actions can only be set for Win32 services, not for drivers. +// +export const ERROR_CANNOT_DETECT_DRIVER_FAILURE = 1080; + +// +// MessageId: ERROR_CANNOT_DETECT_PROCESS_ABORT +// +// MessageText: +// +// This service runs in the same process as the service control manager. +// Therefore, the service control manager cannot take action if this service's process terminates unexpectedly. +// +export const ERROR_CANNOT_DETECT_PROCESS_ABORT = 1081; + +// +// MessageId: ERROR_NO_RECOVERY_PROGRAM +// +// MessageText: +// +// No recovery program has been configured for this service. +// +export const ERROR_NO_RECOVERY_PROGRAM = 1082; + +// +// MessageId: ERROR_SERVICE_NOT_IN_EXE +// +// MessageText: +// +// The executable program that this service is configured to run in does not implement the service. +// +export const ERROR_SERVICE_NOT_IN_EXE = 1083; + +// +// MessageId: ERROR_NOT_SAFEBOOT_SERVICE +// +// MessageText: +// +// This service cannot be started in Safe Mode +// +export const ERROR_NOT_SAFEBOOT_SERVICE = 1084; + +// +// MessageId: ERROR_END_OF_MEDIA +// +// MessageText: +// +// The physical end of the tape has been reached. +// +export const ERROR_END_OF_MEDIA = 1100; + +// +// MessageId: ERROR_FILEMARK_DETECTED +// +// MessageText: +// +// A tape access reached a filemark. +// +export const ERROR_FILEMARK_DETECTED = 1101; + +// +// MessageId: ERROR_BEGINNING_OF_MEDIA +// +// MessageText: +// +// The beginning of the tape or a partition was encountered. +// +export const ERROR_BEGINNING_OF_MEDIA = 1102; + +// +// MessageId: ERROR_SETMARK_DETECTED +// +// MessageText: +// +// A tape access reached the end of a set of files. +// +export const ERROR_SETMARK_DETECTED = 1103; + +// +// MessageId: ERROR_NO_DATA_DETECTED +// +// MessageText: +// +// No more data is on the tape. +// +export const ERROR_NO_DATA_DETECTED = 1104; + +// +// MessageId: ERROR_PARTITION_FAILURE +// +// MessageText: +// +// Tape could not be partitioned. +// +export const ERROR_PARTITION_FAILURE = 1105; + +// +// MessageId: ERROR_INVALID_BLOCK_LENGTH +// +// MessageText: +// +// When accessing a new tape of a multivolume partition, the current block size is incorrect. +// +export const ERROR_INVALID_BLOCK_LENGTH = 1106; + +// +// MessageId: ERROR_DEVICE_NOT_PARTITIONED +// +// MessageText: +// +// Tape partition information could not be found when loading a tape. +// +export const ERROR_DEVICE_NOT_PARTITIONED = 1107; + +// +// MessageId: ERROR_UNABLE_TO_LOCK_MEDIA +// +// MessageText: +// +// Unable to lock the media eject mechanism. +// +export const ERROR_UNABLE_TO_LOCK_MEDIA = 1108; + +// +// MessageId: ERROR_UNABLE_TO_UNLOAD_MEDIA +// +// MessageText: +// +// Unable to unload the media. +// +export const ERROR_UNABLE_TO_UNLOAD_MEDIA = 1109; + +// +// MessageId: ERROR_MEDIA_CHANGED +// +// MessageText: +// +// The media in the drive may have changed. +// +export const ERROR_MEDIA_CHANGED = 1110; + +// +// MessageId: ERROR_BUS_RESET +// +// MessageText: +// +// The I/O bus was reset. +// +export const ERROR_BUS_RESET = 1111; + +// +// MessageId: ERROR_NO_MEDIA_IN_DRIVE +// +// MessageText: +// +// No media in drive. +// +export const ERROR_NO_MEDIA_IN_DRIVE = 1112; + +// +// MessageId: ERROR_NO_UNICODE_TRANSLATION +// +// MessageText: +// +// No mapping for the Unicode character exists in the target multi-byte code page. +// +export const ERROR_NO_UNICODE_TRANSLATION = 1113; + +// +// MessageId: ERROR_DLL_INIT_FAILED +// +// MessageText: +// +// A dynamic link library (DLL) initialization routine failed. +// +export const ERROR_DLL_INIT_FAILED = 1114; + +// +// MessageId: ERROR_SHUTDOWN_IN_PROGRESS +// +// MessageText: +// +// A system shutdown is in progress. +// +export const ERROR_SHUTDOWN_IN_PROGRESS = 1115; + +// +// MessageId: ERROR_NO_SHUTDOWN_IN_PROGRESS +// +// MessageText: +// +// Unable to abort the system shutdown because no shutdown was in progress. +// +export const ERROR_NO_SHUTDOWN_IN_PROGRESS = 1116; + +// +// MessageId: ERROR_IO_DEVICE +// +// MessageText: +// +// The request could not be performed because of an I/O device error. +// +export const ERROR_IO_DEVICE = 1117; + +// +// MessageId: ERROR_SERIAL_NO_DEVICE +// +// MessageText: +// +// No serial device was successfully initialized. The serial driver will unload. +// +export const ERROR_SERIAL_NO_DEVICE = 1118; + +// +// MessageId: ERROR_IRQ_BUSY +// +// MessageText: +// +// Unable to open a device that was sharing an interrupt request (IRQ) with other devices. At least one other device that uses that IRQ was already opened. +// +export const ERROR_IRQ_BUSY = 1119; + +// +// MessageId: ERROR_MORE_WRITES +// +// MessageText: +// +// A serial I/O operation was completed by another write to the serial port. +// (The IOCTL_SERIAL_XOFF_COUNTER reached zero.) +// +export const ERROR_MORE_WRITES = 1120; + +// +// MessageId: ERROR_COUNTER_TIMEOUT +// +// MessageText: +// +// A serial I/O operation completed because the timeout period expired. +// (The IOCTL_SERIAL_XOFF_COUNTER did not reach zero.) +// +export const ERROR_COUNTER_TIMEOUT = 1121; + +// +// MessageId: ERROR_FLOPPY_ID_MARK_NOT_FOUND +// +// MessageText: +// +// No ID address mark was found on the floppy disk. +// +export const ERROR_FLOPPY_ID_MARK_NOT_FOUND = 1122; + +// +// MessageId: ERROR_FLOPPY_WRONG_CYLINDER +// +// MessageText: +// +// Mismatch between the floppy disk sector ID field and the floppy disk controller track address. +// +export const ERROR_FLOPPY_WRONG_CYLINDER = 1123; + +// +// MessageId: ERROR_FLOPPY_UNKNOWN_ERROR +// +// MessageText: +// +// The floppy disk controller reported an error that is not recognized by the floppy disk driver. +// +export const ERROR_FLOPPY_UNKNOWN_ERROR = 1124; + +// +// MessageId: ERROR_FLOPPY_BAD_REGISTERS +// +// MessageText: +// +// The floppy disk controller returned inconsistent results in its registers. +// +export const ERROR_FLOPPY_BAD_REGISTERS = 1125; + +// +// MessageId: ERROR_DISK_RECALIBRATE_FAILED +// +// MessageText: +// +// While accessing the hard disk, a recalibrate operation failed, even after retries. +// +export const ERROR_DISK_RECALIBRATE_FAILED = 1126; + +// +// MessageId: ERROR_DISK_OPERATION_FAILED +// +// MessageText: +// +// While accessing the hard disk, a disk operation failed even after retries. +// +export const ERROR_DISK_OPERATION_FAILED = 1127; + +// +// MessageId: ERROR_DISK_RESET_FAILED +// +// MessageText: +// +// While accessing the hard disk, a disk controller reset was needed, but even that failed. +// +export const ERROR_DISK_RESET_FAILED = 1128; + +// +// MessageId: ERROR_EOM_OVERFLOW +// +// MessageText: +// +// Physical end of tape encountered. +// +export const ERROR_EOM_OVERFLOW = 1129; + +// +// MessageId: ERROR_NOT_ENOUGH_SERVER_MEMORY +// +// MessageText: +// +// Not enough server storage is available to process this command. +// +export const ERROR_NOT_ENOUGH_SERVER_MEMORY = 1130; + +// +// MessageId: ERROR_POSSIBLE_DEADLOCK +// +// MessageText: +// +// A potential deadlock condition has been detected. +// +export const ERROR_POSSIBLE_DEADLOCK = 1131; + +// +// MessageId: ERROR_MAPPED_ALIGNMENT +// +// MessageText: +// +// The base address or the file offset specified does not have the proper alignment. +// +export const ERROR_MAPPED_ALIGNMENT = 1132; + +// +// MessageId: ERROR_SET_POWER_STATE_VETOED +// +// MessageText: +// +// An attempt to change the system power state was vetoed by another application or driver. +// +export const ERROR_SET_POWER_STATE_VETOED = 1140; + +// +// MessageId: ERROR_SET_POWER_STATE_FAILED +// +// MessageText: +// +// The system BIOS failed an attempt to change the system power state. +// +export const ERROR_SET_POWER_STATE_FAILED = 1141; + +// +// MessageId: ERROR_TOO_MANY_LINKS +// +// MessageText: +// +// An attempt was made to create more links on a file than the file system supports. +// +export const ERROR_TOO_MANY_LINKS = 1142; + +// +// MessageId: ERROR_OLD_WIN_VERSION +// +// MessageText: +// +// The specified program requires a newer version of Windows. +// +export const ERROR_OLD_WIN_VERSION = 1150; + +// +// MessageId: ERROR_APP_WRONG_OS +// +// MessageText: +// +// The specified program is not a Windows or MS-DOS program. +// +export const ERROR_APP_WRONG_OS = 1151; + +// +// MessageId: ERROR_SINGLE_INSTANCE_APP +// +// MessageText: +// +// Cannot start more than one instance of the specified program. +// +export const ERROR_SINGLE_INSTANCE_APP = 1152; + +// +// MessageId: ERROR_RMODE_APP +// +// MessageText: +// +// The specified program was written for an earlier version of Windows. +// +export const ERROR_RMODE_APP = 1153; + +// +// MessageId: ERROR_INVALID_DLL +// +// MessageText: +// +// One of the library files needed to run this application is damaged. +// +export const ERROR_INVALID_DLL = 1154; + +// +// MessageId: ERROR_NO_ASSOCIATION +// +// MessageText: +// +// No application is associated with the specified file for this operation. +// +export const ERROR_NO_ASSOCIATION = 1155; + +// +// MessageId: ERROR_DDE_FAIL +// +// MessageText: +// +// An error occurred in sending the command to the application. +// +export const ERROR_DDE_FAIL = 1156; + +// +// MessageId: ERROR_DLL_NOT_FOUND +// +// MessageText: +// +// One of the library files needed to run this application cannot be found. +// +export const ERROR_DLL_NOT_FOUND = 1157; + +// +// MessageId: ERROR_NO_MORE_USER_HANDLES +// +// MessageText: +// +// The current process has used all of its system allowance of handles for Window Manager objects. +// +export const ERROR_NO_MORE_USER_HANDLES = 1158; + +// +// MessageId: ERROR_MESSAGE_SYNC_ONLY +// +// MessageText: +// +// The message can be used only with synchronous operations. +// +export const ERROR_MESSAGE_SYNC_ONLY = 1159; + +// +// MessageId: ERROR_SOURCE_ELEMENT_EMPTY +// +// MessageText: +// +// The indicated source element has no media. +// +export const ERROR_SOURCE_ELEMENT_EMPTY = 1160; + +// +// MessageId: ERROR_DESTINATION_ELEMENT_FULL +// +// MessageText: +// +// The indicated destination element already contains media. +// +export const ERROR_DESTINATION_ELEMENT_FULL = 1161; + +// +// MessageId: ERROR_ILLEGAL_ELEMENT_ADDRESS +// +// MessageText: +// +// The indicated element does not exist. +// +export const ERROR_ILLEGAL_ELEMENT_ADDRESS = 1162; + +// +// MessageId: ERROR_MAGAZINE_NOT_PRESENT +// +// MessageText: +// +// The indicated element is part of a magazine that is not present. +// +export const ERROR_MAGAZINE_NOT_PRESENT = 1163; + +// +// MessageId: ERROR_DEVICE_REINITIALIZATION_NEEDED +// +// MessageText: +// +// The indicated device requires reinitialization due to hardware errors. +// +export const ERROR_DEVICE_REINITIALIZATION_NEEDED = 1164; // dderror + +// +// MessageId: ERROR_DEVICE_REQUIRES_CLEANING +// +// MessageText: +// +// The device has indicated that cleaning is required before further operations are attempted. +// +export const ERROR_DEVICE_REQUIRES_CLEANING = 1165; + +// +// MessageId: ERROR_DEVICE_DOOR_OPEN +// +// MessageText: +// +// The device has indicated that its door is open. +// +export const ERROR_DEVICE_DOOR_OPEN = 1166; + +// +// MessageId: ERROR_DEVICE_NOT_CONNECTED +// +// MessageText: +// +// The device is not connected. +// +export const ERROR_DEVICE_NOT_CONNECTED = 1167; + +// +// MessageId: ERROR_NOT_FOUND +// +// MessageText: +// +// Element not found. +// +export const ERROR_NOT_FOUND = 1168; + +// +// MessageId: ERROR_NO_MATCH +// +// MessageText: +// +// There was no match for the specified key in the index. +// +export const ERROR_NO_MATCH = 1169; + +// +// MessageId: ERROR_SET_NOT_FOUND +// +// MessageText: +// +// The property set specified does not exist on the object. +// +export const ERROR_SET_NOT_FOUND = 1170; + +// +// MessageId: ERROR_POINT_NOT_FOUND +// +// MessageText: +// +// The point passed to GetMouseMovePoints is not in the buffer. +// +export const ERROR_POINT_NOT_FOUND = 1171; + +// +// MessageId: ERROR_NO_TRACKING_SERVICE +// +// MessageText: +// +// The tracking (workstation) service is not running. +// +export const ERROR_NO_TRACKING_SERVICE = 1172; + +// +// MessageId: ERROR_NO_VOLUME_ID +// +// MessageText: +// +// The Volume ID could not be found. +// +export const ERROR_NO_VOLUME_ID = 1173; + +// +// MessageId: ERROR_UNABLE_TO_REMOVE_REPLACED +// +// MessageText: +// +// Unable to remove the file to be replaced. +// +export const ERROR_UNABLE_TO_REMOVE_REPLACED = 1175; + +// +// MessageId: ERROR_UNABLE_TO_MOVE_REPLACEMENT +// +// MessageText: +// +// Unable to move the replacement file to the file to be replaced. The file to be replaced has retained its original name. +// +export const ERROR_UNABLE_TO_MOVE_REPLACEMENT = 1176; + +// +// MessageId: ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 +// +// MessageText: +// +// Unable to move the replacement file to the file to be replaced. The file to be replaced has been renamed using the backup name. +// +export const ERROR_UNABLE_TO_MOVE_REPLACEMENT_2 = 1177; + +// +// MessageId: ERROR_JOURNAL_DELETE_IN_PROGRESS +// +// MessageText: +// +// The volume change journal is being deleted. +// +export const ERROR_JOURNAL_DELETE_IN_PROGRESS = 1178; + +// +// MessageId: ERROR_JOURNAL_NOT_ACTIVE +// +// MessageText: +// +// The volume change journal is not active. +// +export const ERROR_JOURNAL_NOT_ACTIVE = 1179; + +// +// MessageId: ERROR_POTENTIAL_FILE_FOUND +// +// MessageText: +// +// A file was found, but it may not be the correct file. +// +export const ERROR_POTENTIAL_FILE_FOUND = 1180; + +// +// MessageId: ERROR_JOURNAL_ENTRY_DELETED +// +// MessageText: +// +// The journal entry has been deleted from the journal. +// +export const ERROR_JOURNAL_ENTRY_DELETED = 1181; + +// +// MessageId: ERROR_BAD_DEVICE +// +// MessageText: +// +// The specified device name is invalid. +// +export const ERROR_BAD_DEVICE = 1200; + +// +// MessageId: ERROR_CONNECTION_UNAVAIL +// +// MessageText: +// +// The device is not currently connected but it is a remembered connection. +// +export const ERROR_CONNECTION_UNAVAIL = 1201; + +// +// MessageId: ERROR_DEVICE_ALREADY_REMEMBERED +// +// MessageText: +// +// The local device name has a remembered connection to another network resource. +// +export const ERROR_DEVICE_ALREADY_REMEMBERED = 1202; + +// +// MessageId: ERROR_NO_NET_OR_BAD_PATH +// +// MessageText: +// +// No network provider accepted the given network path. +// +export const ERROR_NO_NET_OR_BAD_PATH = 1203; + +// +// MessageId: ERROR_BAD_PROVIDER +// +// MessageText: +// +// The specified network provider name is invalid. +// +export const ERROR_BAD_PROVIDER = 1204; + +// +// MessageId: ERROR_CANNOT_OPEN_PROFILE +// +// MessageText: +// +// Unable to open the network connection profile. +// +export const ERROR_CANNOT_OPEN_PROFILE = 1205; + +// +// MessageId: ERROR_BAD_PROFILE +// +// MessageText: +// +// The network connection profile is corrupted. +// +export const ERROR_BAD_PROFILE = 1206; + +// +// MessageId: ERROR_NOT_CONTAINER +// +// MessageText: +// +// Cannot enumerate a noncontainer. +// +export const ERROR_NOT_CONTAINER = 1207; + +// +// MessageId: ERROR_EXTENDED_ERROR +// +// MessageText: +// +// An extended error has occurred. +// +export const ERROR_EXTENDED_ERROR = 1208; + +// +// MessageId: ERROR_INVALID_GROUPNAME +// +// MessageText: +// +// The format of the specified group name is invalid. +// +export const ERROR_INVALID_GROUPNAME = 1209; + +// +// MessageId: ERROR_INVALID_COMPUTERNAME +// +// MessageText: +// +// The format of the specified computer name is invalid. +// +export const ERROR_INVALID_COMPUTERNAME = 1210; + +// +// MessageId: ERROR_INVALID_EVENTNAME +// +// MessageText: +// +// The format of the specified event name is invalid. +// +export const ERROR_INVALID_EVENTNAME = 1211; + +// +// MessageId: ERROR_INVALID_DOMAINNAME +// +// MessageText: +// +// The format of the specified domain name is invalid. +// +export const ERROR_INVALID_DOMAINNAME = 1212; + +// +// MessageId: ERROR_INVALID_SERVICENAME +// +// MessageText: +// +// The format of the specified service name is invalid. +// +export const ERROR_INVALID_SERVICENAME = 1213; + +// +// MessageId: ERROR_INVALID_NETNAME +// +// MessageText: +// +// The format of the specified network name is invalid. +// +export const ERROR_INVALID_NETNAME = 1214; + +// +// MessageId: ERROR_INVALID_SHARENAME +// +// MessageText: +// +// The format of the specified share name is invalid. +// +export const ERROR_INVALID_SHARENAME = 1215; + +// +// MessageId: ERROR_INVALID_PASSWORDNAME +// +// MessageText: +// +// The format of the specified password is invalid. +// +export const ERROR_INVALID_PASSWORDNAME = 1216; + +// +// MessageId: ERROR_INVALID_MESSAGENAME +// +// MessageText: +// +// The format of the specified message name is invalid. +// +export const ERROR_INVALID_MESSAGENAME = 1217; + +// +// MessageId: ERROR_INVALID_MESSAGEDEST +// +// MessageText: +// +// The format of the specified message destination is invalid. +// +export const ERROR_INVALID_MESSAGEDEST = 1218; + +// +// MessageId: ERROR_SESSION_CREDENTIAL_CONFLICT +// +// MessageText: +// +// Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again. +// +export const ERROR_SESSION_CREDENTIAL_CONFLICT = 1219; + +// +// MessageId: ERROR_REMOTE_SESSION_LIMIT_EXCEEDED +// +// MessageText: +// +// An attempt was made to establish a session to a network server, but there are already too many sessions established to that server. +// +export const ERROR_REMOTE_SESSION_LIMIT_EXCEEDED = 1220; + +// +// MessageId: ERROR_DUP_DOMAINNAME +// +// MessageText: +// +// The workgroup or domain name is already in use by another computer on the network. +// +export const ERROR_DUP_DOMAINNAME = 1221; + +// +// MessageId: ERROR_NO_NETWORK +// +// MessageText: +// +// The network is not present or not started. +// +export const ERROR_NO_NETWORK = 1222; + +// +// MessageId: ERROR_CANCELLED +// +// MessageText: +// +// The operation was canceled by the user. +// +export const ERROR_CANCELLED = 1223; + +// +// MessageId: ERROR_USER_MAPPED_FILE +// +// MessageText: +// +// The requested operation cannot be performed on a file with a user-mapped section open. +// +export const ERROR_USER_MAPPED_FILE = 1224; + +// +// MessageId: ERROR_CONNECTION_REFUSED +// +// MessageText: +// +// The remote system refused the network connection. +// +export const ERROR_CONNECTION_REFUSED = 1225; + +// +// MessageId: ERROR_GRACEFUL_DISCONNECT +// +// MessageText: +// +// The network connection was gracefully closed. +// +export const ERROR_GRACEFUL_DISCONNECT = 1226; + +// +// MessageId: ERROR_ADDRESS_ALREADY_ASSOCIATED +// +// MessageText: +// +// The network transport endpoint already has an address associated with it. +// +export const ERROR_ADDRESS_ALREADY_ASSOCIATED = 1227; + +// +// MessageId: ERROR_ADDRESS_NOT_ASSOCIATED +// +// MessageText: +// +// An address has not yet been associated with the network endpoint. +// +export const ERROR_ADDRESS_NOT_ASSOCIATED = 1228; + +// +// MessageId: ERROR_CONNECTION_INVALID +// +// MessageText: +// +// An operation was attempted on a nonexistent network connection. +// +export const ERROR_CONNECTION_INVALID = 1229; + +// +// MessageId: ERROR_CONNECTION_ACTIVE +// +// MessageText: +// +// An invalid operation was attempted on an active network connection. +// +export const ERROR_CONNECTION_ACTIVE = 1230; + +// +// MessageId: ERROR_NETWORK_UNREACHABLE +// +// MessageText: +// +// The network location cannot be reached. For information about network troubleshooting, see Windows Help. +// +export const ERROR_NETWORK_UNREACHABLE = 1231; + +// +// MessageId: ERROR_HOST_UNREACHABLE +// +// MessageText: +// +// The network location cannot be reached. For information about network troubleshooting, see Windows Help. +// +export const ERROR_HOST_UNREACHABLE = 1232; + +// +// MessageId: ERROR_PROTOCOL_UNREACHABLE +// +// MessageText: +// +// The network location cannot be reached. For information about network troubleshooting, see Windows Help. +// +export const ERROR_PROTOCOL_UNREACHABLE = 1233; + +// +// MessageId: ERROR_PORT_UNREACHABLE +// +// MessageText: +// +// No service is operating at the destination network endpoint on the remote system. +// +export const ERROR_PORT_UNREACHABLE = 1234; + +// +// MessageId: ERROR_REQUEST_ABORTED +// +// MessageText: +// +// The request was aborted. +// +export const ERROR_REQUEST_ABORTED = 1235; + +// +// MessageId: ERROR_CONNECTION_ABORTED +// +// MessageText: +// +// The network connection was aborted by the local system. +// +export const ERROR_CONNECTION_ABORTED = 1236; + +// +// MessageId: ERROR_RETRY +// +// MessageText: +// +// The operation could not be completed. A retry should be performed. +// +export const ERROR_RETRY = 1237; + +// +// MessageId: ERROR_CONNECTION_COUNT_LIMIT +// +// MessageText: +// +// A connection to the server could not be made because the limit on the number of concurrent connections for this account has been reached. +// +export const ERROR_CONNECTION_COUNT_LIMIT = 1238; + +// +// MessageId: ERROR_LOGIN_TIME_RESTRICTION +// +// MessageText: +// +// Attempting to log in during an unauthorized time of day for this account. +// +export const ERROR_LOGIN_TIME_RESTRICTION = 1239; + +// +// MessageId: ERROR_LOGIN_WKSTA_RESTRICTION +// +// MessageText: +// +// The account is not authorized to log in from this station. +// +export const ERROR_LOGIN_WKSTA_RESTRICTION = 1240; + +// +// MessageId: ERROR_INCORRECT_ADDRESS +// +// MessageText: +// +// The network address could not be used for the operation requested. +// +export const ERROR_INCORRECT_ADDRESS = 1241; + +// +// MessageId: ERROR_ALREADY_REGISTERED +// +// MessageText: +// +// The service is already registered. +// +export const ERROR_ALREADY_REGISTERED = 1242; + +// +// MessageId: ERROR_SERVICE_NOT_FOUND +// +// MessageText: +// +// The specified service does not exist. +// +export const ERROR_SERVICE_NOT_FOUND = 1243; + +// +// MessageId: ERROR_NOT_AUTHENTICATED +// +// MessageText: +// +// The operation being requested was not performed because the user has not been authenticated. +// +export const ERROR_NOT_AUTHENTICATED = 1244; + +// +// MessageId: ERROR_NOT_LOGGED_ON +// +// MessageText: +// +// The operation being requested was not performed because the user has not logged on to the network. +// The specified service does not exist. +// +export const ERROR_NOT_LOGGED_ON = 1245; + +// +// MessageId: ERROR_CONTINUE +// +// MessageText: +// +// Continue with work in progress. +// +export const ERROR_CONTINUE = 1246; // dderror + +// +// MessageId: ERROR_ALREADY_INITIALIZED +// +// MessageText: +// +// An attempt was made to perform an initialization operation when initialization has already been completed. +// +export const ERROR_ALREADY_INITIALIZED = 1247; + +// +// MessageId: ERROR_NO_MORE_DEVICES +// +// MessageText: +// +// No more local devices. +// +export const ERROR_NO_MORE_DEVICES = 1248; // dderror + +// +// MessageId: ERROR_NO_SUCH_SITE +// +// MessageText: +// +// The specified site does not exist. +// +export const ERROR_NO_SUCH_SITE = 1249; + +// +// MessageId: ERROR_DOMAIN_CONTROLLER_EXISTS +// +// MessageText: +// +// A domain controller with the specified name already exists. +// +export const ERROR_DOMAIN_CONTROLLER_EXISTS = 1250; + +// +// MessageId: ERROR_ONLY_IF_CONNECTED +// +// MessageText: +// +// This operation is supported only when you are connected to the server. +// +export const ERROR_ONLY_IF_CONNECTED = 1251; + +// +// MessageId: ERROR_OVERRIDE_NOCHANGES +// +// MessageText: +// +// The group policy framework should call the extension even if there are no changes. +// +export const ERROR_OVERRIDE_NOCHANGES = 1252; + +// +// MessageId: ERROR_BAD_USER_PROFILE +// +// MessageText: +// +// The specified user does not have a valid profile. +// +export const ERROR_BAD_USER_PROFILE = 1253; + +// +// MessageId: ERROR_NOT_SUPPORTED_ON_SBS +// +// MessageText: +// +// This operation is not supported on a computer running Windows Server 2003 for Small Business Server +// +export const ERROR_NOT_SUPPORTED_ON_SBS = 1254; + +// +// MessageId: ERROR_SERVER_SHUTDOWN_IN_PROGRESS +// +// MessageText: +// +// The server machine is shutting down. +// +export const ERROR_SERVER_SHUTDOWN_IN_PROGRESS = 1255; + +// +// MessageId: ERROR_HOST_DOWN +// +// MessageText: +// +// The remote system is not available. For information about network troubleshooting, see Windows Help. +// +export const ERROR_HOST_DOWN = 1256; + +// +// MessageId: ERROR_NON_ACCOUNT_SID +// +// MessageText: +// +// The security identifier provided is not from an account domain. +// +export const ERROR_NON_ACCOUNT_SID = 1257; + +// +// MessageId: ERROR_NON_DOMAIN_SID +// +// MessageText: +// +// The security identifier provided does not have a domain component. +// +export const ERROR_NON_DOMAIN_SID = 1258; + +// +// MessageId: ERROR_APPHELP_BLOCK +// +// MessageText: +// +// AppHelp dialog canceled thus preventing the application from starting. +// +export const ERROR_APPHELP_BLOCK = 1259; + +// +// MessageId: ERROR_ACCESS_DISABLED_BY_POLICY +// +// MessageText: +// +// Windows cannot open this program because it has been prevented by a software restriction policy. For more information, open Event Viewer or contact your system administrator. +// +export const ERROR_ACCESS_DISABLED_BY_POLICY = 1260; + +// +// MessageId: ERROR_REG_NAT_CONSUMPTION +// +// MessageText: +// +// A program attempt to use an invalid register value. Normally caused by an uninitialized register. This error is Itanium specific. +// +export const ERROR_REG_NAT_CONSUMPTION = 1261; + +// +// MessageId: ERROR_CSCSHARE_OFFLINE +// +// MessageText: +// +// The share is currently offline or does not exist. +// +export const ERROR_CSCSHARE_OFFLINE = 1262; + +// +// MessageId: ERROR_PKINIT_FAILURE +// +// MessageText: +// +// The kerberos protocol encountered an error while validating the +// KDC certificate during smartcard logon. There is more information in the +// system event log. +// +export const ERROR_PKINIT_FAILURE = 1263; + +// +// MessageId: ERROR_SMARTCARD_SUBSYSTEM_FAILURE +// +// MessageText: +// +// The kerberos protocol encountered an error while attempting to utilize +// the smartcard subsystem. +// +export const ERROR_SMARTCARD_SUBSYSTEM_FAILURE = 1264; + +// +// MessageId: ERROR_DOWNGRADE_DETECTED +// +// MessageText: +// +// The system detected a possible attempt to compromise security. Please ensure that you can contact the server that authenticated you. +// +export const ERROR_DOWNGRADE_DETECTED = 1265; + +// +// Do not use ID's 1266 - 1270 as the symbolicNames have been moved to SEC_E_* +// +// +// MessageId: ERROR_MACHINE_LOCKED +// +// MessageText: +// +// The machine is locked and can not be shut down without the force option. +// +export const ERROR_MACHINE_LOCKED = 1271; + +// +// MessageId: ERROR_CALLBACK_SUPPLIED_INVALID_DATA +// +// MessageText: +// +// An application-defined callback gave invalid data when called. +// +export const ERROR_CALLBACK_SUPPLIED_INVALID_DATA = 1273; + +// +// MessageId: ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED +// +// MessageText: +// +// The group policy framework should call the extension in the synchronous foreground policy refresh. +// +export const ERROR_SYNC_FOREGROUND_REFRESH_REQUIRED = 1274; + +// +// MessageId: ERROR_DRIVER_BLOCKED +// +// MessageText: +// +// This driver has been blocked from loading +// +export const ERROR_DRIVER_BLOCKED = 1275; + +// +// MessageId: ERROR_INVALID_IMPORT_OF_NON_DLL +// +// MessageText: +// +// A dynamic link library (DLL) referenced a module that was neither a DLL nor the process's executable image. +// +export const ERROR_INVALID_IMPORT_OF_NON_DLL = 1276; + +// +// MessageId: ERROR_ACCESS_DISABLED_WEBBLADE +// +// MessageText: +// +// Windows cannot open this program since it has been disabled. +// +export const ERROR_ACCESS_DISABLED_WEBBLADE = 1277; + +// +// MessageId: ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER +// +// MessageText: +// +// Windows cannot open this program because the license enforcement system has been tampered with or become corrupted. +// +export const ERROR_ACCESS_DISABLED_WEBBLADE_TAMPER = 1278; + +// +// MessageId: ERROR_RECOVERY_FAILURE +// +// MessageText: +// +// A transaction recover failed. +// +export const ERROR_RECOVERY_FAILURE = 1279; + +// +// MessageId: ERROR_ALREADY_FIBER +// +// MessageText: +// +// The current thread has already been converted to a fiber. +// +export const ERROR_ALREADY_FIBER = 1280; + +// +// MessageId: ERROR_ALREADY_THREAD +// +// MessageText: +// +// The current thread has already been converted from a fiber. +// +export const ERROR_ALREADY_THREAD = 1281; + +// +// MessageId: ERROR_STACK_BUFFER_OVERRUN +// +// MessageText: +// +// The system detected an overrun of a stack-based buffer in this application. This +// overrun could potentially allow a malicious user to gain control of this application. +// +export const ERROR_STACK_BUFFER_OVERRUN = 1282; + +// +// MessageId: ERROR_PARAMETER_QUOTA_EXCEEDED +// +// MessageText: +// +// Data present in one of the parameters is more than the function can operate on. +// +export const ERROR_PARAMETER_QUOTA_EXCEEDED = 1283; + +// +// MessageId: ERROR_DEBUGGER_INACTIVE +// +// MessageText: +// +// An attempt to do an operation on a debug object failed because the object is in the process of being deleted. +// +export const ERROR_DEBUGGER_INACTIVE = 1284; + +// +// MessageId: ERROR_DELAY_LOAD_FAILED +// +// MessageText: +// +// An attempt to delay-load a .dll or get a function address in a delay-loaded .dll failed. +// +export const ERROR_DELAY_LOAD_FAILED = 1285; + +// +// MessageId: ERROR_VDM_DISALLOWED +// +// MessageText: +// +// %1 is a 16-bit application. You do not have permissions to execute 16-bit applications. Check your permissions with your system administrator. +// +export const ERROR_VDM_DISALLOWED = 1286; + +// +// MessageId: ERROR_UNIDENTIFIED_ERROR +// +// MessageText: +// +// Insufficient information exists to identify the cause of failure. +// +export const ERROR_UNIDENTIFIED_ERROR = 1287; + +/////////////////////////// +// +// Add new status codes before this point unless there is a component specific section below. +// +/////////////////////////// + +/////////////////////////// +// // +// Security Status Codes // +// // +/////////////////////////// + +// +// MessageId: ERROR_NOT_ALL_ASSIGNED +// +// MessageText: +// +// Not all privileges referenced are assigned to the caller. +// +export const ERROR_NOT_ALL_ASSIGNED = 1300; + +// +// MessageId: ERROR_SOME_NOT_MAPPED +// +// MessageText: +// +// Some mapping between account names and security IDs was not done. +// +export const ERROR_SOME_NOT_MAPPED = 1301; + +// +// MessageId: ERROR_NO_QUOTAS_FOR_ACCOUNT +// +// MessageText: +// +// No system quota limits are specifically set for this account. +// +export const ERROR_NO_QUOTAS_FOR_ACCOUNT = 1302; + +// +// MessageId: ERROR_LOCAL_USER_SESSION_KEY +// +// MessageText: +// +// No encryption key is available. A well-known encryption key was returned. +// +export const ERROR_LOCAL_USER_SESSION_KEY = 1303; + +// +// MessageId: ERROR_NULL_LM_PASSWORD +// +// MessageText: +// +// The password is too complex to be converted to a LAN Manager password. The LAN Manager password returned is a NULL string. +// +export const ERROR_NULL_LM_PASSWORD = 1304; + +// +// MessageId: ERROR_UNKNOWN_REVISION +// +// MessageText: +// +// The revision level is unknown. +// +export const ERROR_UNKNOWN_REVISION = 1305; + +// +// MessageId: ERROR_REVISION_MISMATCH +// +// MessageText: +// +// Indicates two revision levels are incompatible. +// +export const ERROR_REVISION_MISMATCH = 1306; + +// +// MessageId: ERROR_INVALID_OWNER +// +// MessageText: +// +// This security ID may not be assigned as the owner of this object. +// +export const ERROR_INVALID_OWNER = 1307; + +// +// MessageId: ERROR_INVALID_PRIMARY_GROUP +// +// MessageText: +// +// This security ID may not be assigned as the primary group of an object. +// +export const ERROR_INVALID_PRIMARY_GROUP = 1308; + +// +// MessageId: ERROR_NO_IMPERSONATION_TOKEN +// +// MessageText: +// +// An attempt has been made to operate on an impersonation token by a thread that is not currently impersonating a client. +// +export const ERROR_NO_IMPERSONATION_TOKEN = 1309; + +// +// MessageId: ERROR_CANT_DISABLE_MANDATORY +// +// MessageText: +// +// The group may not be disabled. +// +export const ERROR_CANT_DISABLE_MANDATORY = 1310; + +// +// MessageId: ERROR_NO_LOGON_SERVERS +// +// MessageText: +// +// There are currently no logon servers available to service the logon request. +// +export const ERROR_NO_LOGON_SERVERS = 1311; + +// +// MessageId: ERROR_NO_SUCH_LOGON_SESSION +// +// MessageText: +// +// A specified logon session does not exist. It may already have been terminated. +// +export const ERROR_NO_SUCH_LOGON_SESSION = 1312; + +// +// MessageId: ERROR_NO_SUCH_PRIVILEGE +// +// MessageText: +// +// A specified privilege does not exist. +// +export const ERROR_NO_SUCH_PRIVILEGE = 1313; + +// +// MessageId: ERROR_PRIVILEGE_NOT_HELD +// +// MessageText: +// +// A required privilege is not held by the client. +// +export const ERROR_PRIVILEGE_NOT_HELD = 1314; + +// +// MessageId: ERROR_INVALID_ACCOUNT_NAME +// +// MessageText: +// +// The name provided is not a properly formed account name. +// +export const ERROR_INVALID_ACCOUNT_NAME = 1315; + +// +// MessageId: ERROR_USER_EXISTS +// +// MessageText: +// +// The specified user already exists. +// +export const ERROR_USER_EXISTS = 1316; + +// +// MessageId: ERROR_NO_SUCH_USER +// +// MessageText: +// +// The specified user does not exist. +// +export const ERROR_NO_SUCH_USER = 1317; + +// +// MessageId: ERROR_GROUP_EXISTS +// +// MessageText: +// +// The specified group already exists. +// +export const ERROR_GROUP_EXISTS = 1318; + +// +// MessageId: ERROR_NO_SUCH_GROUP +// +// MessageText: +// +// The specified group does not exist. +// +export const ERROR_NO_SUCH_GROUP = 1319; + +// +// MessageId: ERROR_MEMBER_IN_GROUP +// +// MessageText: +// +// Either the specified user account is already a member of the specified group, or the specified group cannot be deleted because it contains a member. +// +export const ERROR_MEMBER_IN_GROUP = 1320; + +// +// MessageId: ERROR_MEMBER_NOT_IN_GROUP +// +// MessageText: +// +// The specified user account is not a member of the specified group account. +// +export const ERROR_MEMBER_NOT_IN_GROUP = 1321; + +// +// MessageId: ERROR_LAST_ADMIN +// +// MessageText: +// +// The last remaining administration account cannot be disabled or deleted. +// +export const ERROR_LAST_ADMIN = 1322; + +// +// MessageId: ERROR_WRONG_PASSWORD +// +// MessageText: +// +// Unable to update the password. The value provided as the current password is incorrect. +// +export const ERROR_WRONG_PASSWORD = 1323; + +// +// MessageId: ERROR_ILL_FORMED_PASSWORD +// +// MessageText: +// +// Unable to update the password. The value provided for the new password contains values that are not allowed in passwords. +// +export const ERROR_ILL_FORMED_PASSWORD = 1324; + +// +// MessageId: ERROR_PASSWORD_RESTRICTION +// +// MessageText: +// +// Unable to update the password. The value provided for the new password does not meet the length, complexity, or history requirement of the domain. +// +export const ERROR_PASSWORD_RESTRICTION = 1325; + +// +// MessageId: ERROR_LOGON_FAILURE +// +// MessageText: +// +// Logon failure: unknown user name or bad password. +// +export const ERROR_LOGON_FAILURE = 1326; + +// +// MessageId: ERROR_ACCOUNT_RESTRICTION +// +// MessageText: +// +// Logon failure: user account restriction. Possible reasons are blank passwords not allowed, logon hour restrictions, or a policy restriction has been enforced. +// +export const ERROR_ACCOUNT_RESTRICTION = 1327; + +// +// MessageId: ERROR_INVALID_LOGON_HOURS +// +// MessageText: +// +// Logon failure: account logon time restriction violation. +// +export const ERROR_INVALID_LOGON_HOURS = 1328; + +// +// MessageId: ERROR_INVALID_WORKSTATION +// +// MessageText: +// +// Logon failure: user not allowed to log on to this computer. +// +export const ERROR_INVALID_WORKSTATION = 1329; + +// +// MessageId: ERROR_PASSWORD_EXPIRED +// +// MessageText: +// +// Logon failure: the specified account password has expired. +// +export const ERROR_PASSWORD_EXPIRED = 1330; + +// +// MessageId: ERROR_ACCOUNT_DISABLED +// +// MessageText: +// +// Logon failure: account currently disabled. +// +export const ERROR_ACCOUNT_DISABLED = 1331; + +// +// MessageId: ERROR_NONE_MAPPED +// +// MessageText: +// +// No mapping between account names and security IDs was done. +// +export const ERROR_NONE_MAPPED = 1332; + +// +// MessageId: ERROR_TOO_MANY_LUIDS_REQUESTED +// +// MessageText: +// +// Too many local user identifiers (LUIDs) were requested at one time. +// +export const ERROR_TOO_MANY_LUIDS_REQUESTED = 1333; + +// +// MessageId: ERROR_LUIDS_EXHAUSTED +// +// MessageText: +// +// No more local user identifiers (LUIDs) are available. +// +export const ERROR_LUIDS_EXHAUSTED = 1334; + +// +// MessageId: ERROR_INVALID_SUB_AUTHORITY +// +// MessageText: +// +// The subauthority part of a security ID is invalid for this particular use. +// +export const ERROR_INVALID_SUB_AUTHORITY = 1335; + +// +// MessageId: ERROR_INVALID_ACL +// +// MessageText: +// +// The access control list (ACL) structure is invalid. +// +export const ERROR_INVALID_ACL = 1336; + +// +// MessageId: ERROR_INVALID_SID +// +// MessageText: +// +// The security ID structure is invalid. +// +export const ERROR_INVALID_SID = 1337; + +// +// MessageId: ERROR_INVALID_SECURITY_DESCR +// +// MessageText: +// +// The security descriptor structure is invalid. +// +export const ERROR_INVALID_SECURITY_DESCR = 1338; + +// +// MessageId: ERROR_BAD_INHERITANCE_ACL +// +// MessageText: +// +// The inherited access control list (ACL) or access control entry (ACE) could not be built. +// +export const ERROR_BAD_INHERITANCE_ACL = 1340; + +// +// MessageId: ERROR_SERVER_DISABLED +// +// MessageText: +// +// The server is currently disabled. +// +export const ERROR_SERVER_DISABLED = 1341; + +// +// MessageId: ERROR_SERVER_NOT_DISABLED +// +// MessageText: +// +// The server is currently enabled. +// +export const ERROR_SERVER_NOT_DISABLED = 1342; + +// +// MessageId: ERROR_INVALID_ID_AUTHORITY +// +// MessageText: +// +// The value provided was an invalid value for an identifier authority. +// +export const ERROR_INVALID_ID_AUTHORITY = 1343; + +// +// MessageId: ERROR_ALLOTTED_SPACE_EXCEEDED +// +// MessageText: +// +// No more memory is available for security information updates. +// +export const ERROR_ALLOTTED_SPACE_EXCEEDED = 1344; + +// +// MessageId: ERROR_INVALID_GROUP_ATTRIBUTES +// +// MessageText: +// +// The specified attributes are invalid, or incompatible with the attributes for the group as a whole. +// +export const ERROR_INVALID_GROUP_ATTRIBUTES = 1345; + +// +// MessageId: ERROR_BAD_IMPERSONATION_LEVEL +// +// MessageText: +// +// Either a required impersonation level was not provided, or the provided impersonation level is invalid. +// +export const ERROR_BAD_IMPERSONATION_LEVEL = 1346; + +// +// MessageId: ERROR_CANT_OPEN_ANONYMOUS +// +// MessageText: +// +// Cannot open an anonymous level security token. +// +export const ERROR_CANT_OPEN_ANONYMOUS = 1347; + +// +// MessageId: ERROR_BAD_VALIDATION_CLASS +// +// MessageText: +// +// The validation information class requested was invalid. +// +export const ERROR_BAD_VALIDATION_CLASS = 1348; + +// +// MessageId: ERROR_BAD_TOKEN_TYPE +// +// MessageText: +// +// The type of the token is inappropriate for its attempted use. +// +export const ERROR_BAD_TOKEN_TYPE = 1349; + +// +// MessageId: ERROR_NO_SECURITY_ON_OBJECT +// +// MessageText: +// +// Unable to perform a security operation on an object that has no associated security. +// +export const ERROR_NO_SECURITY_ON_OBJECT = 1350; + +// +// MessageId: ERROR_CANT_ACCESS_DOMAIN_INFO +// +// MessageText: +// +// Configuration information could not be read from the domain controller, either because the machine is unavailable, or access has been denied. +// +export const ERROR_CANT_ACCESS_DOMAIN_INFO = 1351; + +// +// MessageId: ERROR_INVALID_SERVER_STATE +// +// MessageText: +// +// The security account manager (SAM) or local security authority (LSA) server was in the wrong state to perform the security operation. +// +export const ERROR_INVALID_SERVER_STATE = 1352; + +// +// MessageId: ERROR_INVALID_DOMAIN_STATE +// +// MessageText: +// +// The domain was in the wrong state to perform the security operation. +// +export const ERROR_INVALID_DOMAIN_STATE = 1353; + +// +// MessageId: ERROR_INVALID_DOMAIN_ROLE +// +// MessageText: +// +// This operation is only allowed for the Primary Domain Controller of the domain. +// +export const ERROR_INVALID_DOMAIN_ROLE = 1354; + +// +// MessageId: ERROR_NO_SUCH_DOMAIN +// +// MessageText: +// +// The specified domain either does not exist or could not be contacted. +// +export const ERROR_NO_SUCH_DOMAIN = 1355; + +// +// MessageId: ERROR_DOMAIN_EXISTS +// +// MessageText: +// +// The specified domain already exists. +// +export const ERROR_DOMAIN_EXISTS = 1356; + +// +// MessageId: ERROR_DOMAIN_LIMIT_EXCEEDED +// +// MessageText: +// +// An attempt was made to exceed the limit on the number of domains per server. +// +export const ERROR_DOMAIN_LIMIT_EXCEEDED = 1357; + +// +// MessageId: ERROR_INTERNAL_DB_CORRUPTION +// +// MessageText: +// +// Unable to complete the requested operation because of either a catastrophic media failure or a data structure corruption on the disk. +// +export const ERROR_INTERNAL_DB_CORRUPTION = 1358; + +// +// MessageId: ERROR_INTERNAL_ERROR +// +// MessageText: +// +// An internal error occurred. +// +export const ERROR_INTERNAL_ERROR = 1359; + +// +// MessageId: ERROR_GENERIC_NOT_MAPPED +// +// MessageText: +// +// Generic access types were contained in an access mask which should already be mapped to nongeneric types. +// +export const ERROR_GENERIC_NOT_MAPPED = 1360; + +// +// MessageId: ERROR_BAD_DESCRIPTOR_FORMAT +// +// MessageText: +// +// A security descriptor is not in the right format (absolute or self-relative). +// +export const ERROR_BAD_DESCRIPTOR_FORMAT = 1361; + +// +// MessageId: ERROR_NOT_LOGON_PROCESS +// +// MessageText: +// +// The requested action is restricted for use by logon processes only. The calling process has not registered as a logon process. +// +export const ERROR_NOT_LOGON_PROCESS = 1362; + +// +// MessageId: ERROR_LOGON_SESSION_EXISTS +// +// MessageText: +// +// Cannot start a new logon session with an ID that is already in use. +// +export const ERROR_LOGON_SESSION_EXISTS = 1363; + +// +// MessageId: ERROR_NO_SUCH_PACKAGE +// +// MessageText: +// +// A specified authentication package is unknown. +// +export const ERROR_NO_SUCH_PACKAGE = 1364; + +// +// MessageId: ERROR_BAD_LOGON_SESSION_STATE +// +// MessageText: +// +// The logon session is not in a state that is consistent with the requested operation. +// +export const ERROR_BAD_LOGON_SESSION_STATE = 1365; + +// +// MessageId: ERROR_LOGON_SESSION_COLLISION +// +// MessageText: +// +// The logon session ID is already in use. +// +export const ERROR_LOGON_SESSION_COLLISION = 1366; + +// +// MessageId: ERROR_INVALID_LOGON_TYPE +// +// MessageText: +// +// A logon request contained an invalid logon type value. +// +export const ERROR_INVALID_LOGON_TYPE = 1367; + +// +// MessageId: ERROR_CANNOT_IMPERSONATE +// +// MessageText: +// +// Unable to impersonate using a named pipe until data has been read from that pipe. +// +export const ERROR_CANNOT_IMPERSONATE = 1368; + +// +// MessageId: ERROR_RXACT_INVALID_STATE +// +// MessageText: +// +// The transaction state of a registry subtree is incompatible with the requested operation. +// +export const ERROR_RXACT_INVALID_STATE = 1369; + +// +// MessageId: ERROR_RXACT_COMMIT_FAILURE +// +// MessageText: +// +// An internal security database corruption has been encountered. +// +export const ERROR_RXACT_COMMIT_FAILURE = 1370; + +// +// MessageId: ERROR_SPECIAL_ACCOUNT +// +// MessageText: +// +// Cannot perform this operation on built-in accounts. +// +export const ERROR_SPECIAL_ACCOUNT = 1371; + +// +// MessageId: ERROR_SPECIAL_GROUP +// +// MessageText: +// +// Cannot perform this operation on this built-in special group. +// +export const ERROR_SPECIAL_GROUP = 1372; + +// +// MessageId: ERROR_SPECIAL_USER +// +// MessageText: +// +// Cannot perform this operation on this built-in special user. +// +export const ERROR_SPECIAL_USER = 1373; + +// +// MessageId: ERROR_MEMBERS_PRIMARY_GROUP +// +// MessageText: +// +// The user cannot be removed from a group because the group is currently the user's primary group. +// +export const ERROR_MEMBERS_PRIMARY_GROUP = 1374; + +// +// MessageId: ERROR_TOKEN_ALREADY_IN_USE +// +// MessageText: +// +// The token is already in use as a primary token. +// +export const ERROR_TOKEN_ALREADY_IN_USE = 1375; + +// +// MessageId: ERROR_NO_SUCH_ALIAS +// +// MessageText: +// +// The specified local group does not exist. +// +export const ERROR_NO_SUCH_ALIAS = 1376; + +// +// MessageId: ERROR_MEMBER_NOT_IN_ALIAS +// +// MessageText: +// +// The specified account name is not a member of the local group. +// +export const ERROR_MEMBER_NOT_IN_ALIAS = 1377; + +// +// MessageId: ERROR_MEMBER_IN_ALIAS +// +// MessageText: +// +// The specified account name is already a member of the local group. +// +export const ERROR_MEMBER_IN_ALIAS = 1378; + +// +// MessageId: ERROR_ALIAS_EXISTS +// +// MessageText: +// +// The specified local group already exists. +// +export const ERROR_ALIAS_EXISTS = 1379; + +// +// MessageId: ERROR_LOGON_NOT_GRANTED +// +// MessageText: +// +// Logon failure: the user has not been granted the requested logon type at this computer. +// +export const ERROR_LOGON_NOT_GRANTED = 1380; + +// +// MessageId: ERROR_TOO_MANY_SECRETS +// +// MessageText: +// +// The maximum number of secrets that may be stored in a single system has been exceeded. +// +export const ERROR_TOO_MANY_SECRETS = 1381; + +// +// MessageId: ERROR_SECRET_TOO_LONG +// +// MessageText: +// +// The length of a secret exceeds the maximum length allowed. +// +export const ERROR_SECRET_TOO_LONG = 1382; + +// +// MessageId: ERROR_INTERNAL_DB_ERROR +// +// MessageText: +// +// The local security authority database contains an internal inconsistency. +// +export const ERROR_INTERNAL_DB_ERROR = 1383; + +// +// MessageId: ERROR_TOO_MANY_CONTEXT_IDS +// +// MessageText: +// +// During a logon attempt, the user's security context accumulated too many security IDs. +// +export const ERROR_TOO_MANY_CONTEXT_IDS = 1384; + +// +// MessageId: ERROR_LOGON_TYPE_NOT_GRANTED +// +// MessageText: +// +// Logon failure: the user has not been granted the requested logon type at this computer. +// +export const ERROR_LOGON_TYPE_NOT_GRANTED = 1385; + +// +// MessageId: ERROR_NT_CROSS_ENCRYPTION_REQUIRED +// +// MessageText: +// +// A cross-encrypted password is necessary to change a user password. +// +export const ERROR_NT_CROSS_ENCRYPTION_REQUIRED = 1386; + +// +// MessageId: ERROR_NO_SUCH_MEMBER +// +// MessageText: +// +// A member could not be added to or removed from the local group because the member does not exist. +// +export const ERROR_NO_SUCH_MEMBER = 1387; + +// +// MessageId: ERROR_INVALID_MEMBER +// +// MessageText: +// +// A new member could not be added to a local group because the member has the wrong account type. +// +export const ERROR_INVALID_MEMBER = 1388; + +// +// MessageId: ERROR_TOO_MANY_SIDS +// +// MessageText: +// +// Too many security IDs have been specified. +// +export const ERROR_TOO_MANY_SIDS = 1389; + +// +// MessageId: ERROR_LM_CROSS_ENCRYPTION_REQUIRED +// +// MessageText: +// +// A cross-encrypted password is necessary to change this user password. +// +export const ERROR_LM_CROSS_ENCRYPTION_REQUIRED = 1390; + +// +// MessageId: ERROR_NO_INHERITANCE +// +// MessageText: +// +// Indicates an ACL contains no inheritable components. +// +export const ERROR_NO_INHERITANCE = 1391; + +// +// MessageId: ERROR_FILE_CORRUPT +// +// MessageText: +// +// The file or directory is corrupted and unreadable. +// +export const ERROR_FILE_CORRUPT = 1392; + +// +// MessageId: ERROR_DISK_CORRUPT +// +// MessageText: +// +// The disk structure is corrupted and unreadable. +// +export const ERROR_DISK_CORRUPT = 1393; + +// +// MessageId: ERROR_NO_USER_SESSION_KEY +// +// MessageText: +// +// There is no user session key for the specified logon session. +// +export const ERROR_NO_USER_SESSION_KEY = 1394; + +// +// MessageId: ERROR_LICENSE_QUOTA_EXCEEDED +// +// MessageText: +// +// The service being accessed is licensed for a particular number of connections. +// No more connections can be made to the service at this time because there are already as many connections as the service can accept. +// +export const ERROR_LICENSE_QUOTA_EXCEEDED = 1395; + +// +// MessageId: ERROR_WRONG_TARGET_NAME +// +// MessageText: +// +// Logon Failure: The target account name is incorrect. +// +export const ERROR_WRONG_TARGET_NAME = 1396; + +// +// MessageId: ERROR_MUTUAL_AUTH_FAILED +// +// MessageText: +// +// Mutual Authentication failed. The server's password is out of date at the domain controller. +// +export const ERROR_MUTUAL_AUTH_FAILED = 1397; + +// +// MessageId: ERROR_TIME_SKEW +// +// MessageText: +// +// There is a time and/or date difference between the client and server. +// +export const ERROR_TIME_SKEW = 1398; + +// +// MessageId: ERROR_CURRENT_DOMAIN_NOT_ALLOWED +// +// MessageText: +// +// This operation can not be performed on the current domain. +// +export const ERROR_CURRENT_DOMAIN_NOT_ALLOWED = 1399; + +// End of security error codes + +/////////////////////////// +// // +// WinUser Error Codes // +// // +/////////////////////////// + +// +// MessageId: ERROR_INVALID_WINDOW_HANDLE +// +// MessageText: +// +// Invalid window handle. +// +export const ERROR_INVALID_WINDOW_HANDLE = 1400; + +// +// MessageId: ERROR_INVALID_MENU_HANDLE +// +// MessageText: +// +// Invalid menu handle. +// +export const ERROR_INVALID_MENU_HANDLE = 1401; + +// +// MessageId: ERROR_INVALID_CURSOR_HANDLE +// +// MessageText: +// +// Invalid cursor handle. +// +export const ERROR_INVALID_CURSOR_HANDLE = 1402; + +// +// MessageId: ERROR_INVALID_ACCEL_HANDLE +// +// MessageText: +// +// Invalid accelerator table handle. +// +export const ERROR_INVALID_ACCEL_HANDLE = 1403; + +// +// MessageId: ERROR_INVALID_HOOK_HANDLE +// +// MessageText: +// +// Invalid hook handle. +// +export const ERROR_INVALID_HOOK_HANDLE = 1404; + +// +// MessageId: ERROR_INVALID_DWP_HANDLE +// +// MessageText: +// +// Invalid handle to a multiple-window position structure. +// +export const ERROR_INVALID_DWP_HANDLE = 1405; + +// +// MessageId: ERROR_TLW_WITH_WSCHILD +// +// MessageText: +// +// Cannot create a top-level child window. +// +export const ERROR_TLW_WITH_WSCHILD = 1406; + +// +// MessageId: ERROR_CANNOT_FIND_WND_CLASS +// +// MessageText: +// +// Cannot find window class. +// +export const ERROR_CANNOT_FIND_WND_CLASS = 1407; + +// +// MessageId: ERROR_WINDOW_OF_OTHER_THREAD +// +// MessageText: +// +// Invalid window; it belongs to other thread. +// +export const ERROR_WINDOW_OF_OTHER_THREAD = 1408; + +// +// MessageId: ERROR_HOTKEY_ALREADY_REGISTERED +// +// MessageText: +// +// Hot key is already registered. +// +export const ERROR_HOTKEY_ALREADY_REGISTERED = 1409; + +// +// MessageId: ERROR_CLASS_ALREADY_EXISTS +// +// MessageText: +// +// Class already exists. +// +export const ERROR_CLASS_ALREADY_EXISTS = 1410; + +// +// MessageId: ERROR_CLASS_DOES_NOT_EXIST +// +// MessageText: +// +// Class does not exist. +// +export const ERROR_CLASS_DOES_NOT_EXIST = 1411; + +// +// MessageId: ERROR_CLASS_HAS_WINDOWS +// +// MessageText: +// +// Class still has open windows. +// +export const ERROR_CLASS_HAS_WINDOWS = 1412; + +// +// MessageId: ERROR_INVALID_INDEX +// +// MessageText: +// +// Invalid index. +// +export const ERROR_INVALID_INDEX = 1413; + +// +// MessageId: ERROR_INVALID_ICON_HANDLE +// +// MessageText: +// +// Invalid icon handle. +// +export const ERROR_INVALID_ICON_HANDLE = 1414; + +// +// MessageId: ERROR_PRIVATE_DIALOG_INDEX +// +// MessageText: +// +// Using private DIALOG window words. +// +export const ERROR_PRIVATE_DIALOG_INDEX = 1415; + +// +// MessageId: ERROR_LISTBOX_ID_NOT_FOUND +// +// MessageText: +// +// The list box identifier was not found. +// +export const ERROR_LISTBOX_ID_NOT_FOUND = 1416; + +// +// MessageId: ERROR_NO_WILDCARD_CHARACTERS +// +// MessageText: +// +// No wildcards were found. +// +export const ERROR_NO_WILDCARD_CHARACTERS = 1417; + +// +// MessageId: ERROR_CLIPBOARD_NOT_OPEN +// +// MessageText: +// +// Thread does not have a clipboard open. +// +export const ERROR_CLIPBOARD_NOT_OPEN = 1418; + +// +// MessageId: ERROR_HOTKEY_NOT_REGISTERED +// +// MessageText: +// +// Hot key is not registered. +// +export const ERROR_HOTKEY_NOT_REGISTERED = 1419; + +// +// MessageId: ERROR_WINDOW_NOT_DIALOG +// +// MessageText: +// +// The window is not a valid dialog window. +// +export const ERROR_WINDOW_NOT_DIALOG = 1420; + +// +// MessageId: ERROR_CONTROL_ID_NOT_FOUND +// +// MessageText: +// +// Control ID not found. +// +export const ERROR_CONTROL_ID_NOT_FOUND = 1421; + +// +// MessageId: ERROR_INVALID_COMBOBOX_MESSAGE +// +// MessageText: +// +// Invalid message for a combo box because it does not have an edit control. +// +export const ERROR_INVALID_COMBOBOX_MESSAGE = 1422; + +// +// MessageId: ERROR_WINDOW_NOT_COMBOBOX +// +// MessageText: +// +// The window is not a combo box. +// +export const ERROR_WINDOW_NOT_COMBOBOX = 1423; + +// +// MessageId: ERROR_INVALID_EDIT_HEIGHT +// +// MessageText: +// +// Height must be less than 256. +// +export const ERROR_INVALID_EDIT_HEIGHT = 1424; + +// +// MessageId: ERROR_DC_NOT_FOUND +// +// MessageText: +// +// Invalid device context (DC) handle. +// +export const ERROR_DC_NOT_FOUND = 1425; + +// +// MessageId: ERROR_INVALID_HOOK_FILTER +// +// MessageText: +// +// Invalid hook procedure type. +// +export const ERROR_INVALID_HOOK_FILTER = 1426; + +// +// MessageId: ERROR_INVALID_FILTER_PROC +// +// MessageText: +// +// Invalid hook procedure. +// +export const ERROR_INVALID_FILTER_PROC = 1427; + +// +// MessageId: ERROR_HOOK_NEEDS_HMOD +// +// MessageText: +// +// Cannot set nonlocal hook without a module handle. +// +export const ERROR_HOOK_NEEDS_HMOD = 1428; + +// +// MessageId: ERROR_GLOBAL_ONLY_HOOK +// +// MessageText: +// +// This hook procedure can only be set globally. +// +export const ERROR_GLOBAL_ONLY_HOOK = 1429; + +// +// MessageId: ERROR_JOURNAL_HOOK_SET +// +// MessageText: +// +// The journal hook procedure is already installed. +// +export const ERROR_JOURNAL_HOOK_SET = 1430; + +// +// MessageId: ERROR_HOOK_NOT_INSTALLED +// +// MessageText: +// +// The hook procedure is not installed. +// +export const ERROR_HOOK_NOT_INSTALLED = 1431; + +// +// MessageId: ERROR_INVALID_LB_MESSAGE +// +// MessageText: +// +// Invalid message for single-selection list box. +// +export const ERROR_INVALID_LB_MESSAGE = 1432; + +// +// MessageId: ERROR_SETCOUNT_ON_BAD_LB +// +// MessageText: +// +// LB_SETCOUNT sent to non-lazy list box. +// +export const ERROR_SETCOUNT_ON_BAD_LB = 1433; + +// +// MessageId: ERROR_LB_WITHOUT_TABSTOPS +// +// MessageText: +// +// This list box does not support tab stops. +// +export const ERROR_LB_WITHOUT_TABSTOPS = 1434; + +// +// MessageId: ERROR_DESTROY_OBJECT_OF_OTHER_THREAD +// +// MessageText: +// +// Cannot destroy object created by another thread. +// +export const ERROR_DESTROY_OBJECT_OF_OTHER_THREAD = 1435; + +// +// MessageId: ERROR_CHILD_WINDOW_MENU +// +// MessageText: +// +// Child windows cannot have menus. +// +export const ERROR_CHILD_WINDOW_MENU = 1436; + +// +// MessageId: ERROR_NO_SYSTEM_MENU +// +// MessageText: +// +// The window does not have a system menu. +// +export const ERROR_NO_SYSTEM_MENU = 1437; + +// +// MessageId: ERROR_INVALID_MSGBOX_STYLE +// +// MessageText: +// +// Invalid message box style. +// +export const ERROR_INVALID_MSGBOX_STYLE = 1438; + +// +// MessageId: ERROR_INVALID_SPI_VALUE +// +// MessageText: +// +// Invalid system-wide (SPI_*) parameter. +// +export const ERROR_INVALID_SPI_VALUE = 1439; + +// +// MessageId: ERROR_SCREEN_ALREADY_LOCKED +// +// MessageText: +// +// Screen already locked. +// +export const ERROR_SCREEN_ALREADY_LOCKED = 1440; + +// +// MessageId: ERROR_HWNDS_HAVE_DIFF_PARENT +// +// MessageText: +// +// All handles to windows in a multiple-window position structure must have the same parent. +// +export const ERROR_HWNDS_HAVE_DIFF_PARENT = 1441; + +// +// MessageId: ERROR_NOT_CHILD_WINDOW +// +// MessageText: +// +// The window is not a child window. +// +export const ERROR_NOT_CHILD_WINDOW = 1442; + +// +// MessageId: ERROR_INVALID_GW_COMMAND +// +// MessageText: +// +// Invalid GW_* command. +// +export const ERROR_INVALID_GW_COMMAND = 1443; + +// +// MessageId: ERROR_INVALID_THREAD_ID +// +// MessageText: +// +// Invalid thread identifier. +// +export const ERROR_INVALID_THREAD_ID = 1444; + +// +// MessageId: ERROR_NON_MDICHILD_WINDOW +// +// MessageText: +// +// Cannot process a message from a window that is not a multiple document interface (MDI) window. +// +export const ERROR_NON_MDICHILD_WINDOW = 1445; + +// +// MessageId: ERROR_POPUP_ALREADY_ACTIVE +// +// MessageText: +// +// Popup menu already active. +// +export const ERROR_POPUP_ALREADY_ACTIVE = 1446; + +// +// MessageId: ERROR_NO_SCROLLBARS +// +// MessageText: +// +// The window does not have scroll bars. +// +export const ERROR_NO_SCROLLBARS = 1447; + +// +// MessageId: ERROR_INVALID_SCROLLBAR_RANGE +// +// MessageText: +// +// Scroll bar range cannot be greater than MAXLONG. +// +export const ERROR_INVALID_SCROLLBAR_RANGE = 1448; + +// +// MessageId: ERROR_INVALID_SHOWWIN_COMMAND +// +// MessageText: +// +// Cannot show or remove the window in the way specified. +// +export const ERROR_INVALID_SHOWWIN_COMMAND = 1449; + +// +// MessageId: ERROR_NO_SYSTEM_RESOURCES +// +// MessageText: +// +// Insufficient system resources exist to complete the requested service. +// +export const ERROR_NO_SYSTEM_RESOURCES = 1450; + +// +// MessageId: ERROR_NONPAGED_SYSTEM_RESOURCES +// +// MessageText: +// +// Insufficient system resources exist to complete the requested service. +// +export const ERROR_NONPAGED_SYSTEM_RESOURCES = 1451; + +// +// MessageId: ERROR_PAGED_SYSTEM_RESOURCES +// +// MessageText: +// +// Insufficient system resources exist to complete the requested service. +// +export const ERROR_PAGED_SYSTEM_RESOURCES = 1452; + +// +// MessageId: ERROR_WORKING_SET_QUOTA +// +// MessageText: +// +// Insufficient quota to complete the requested service. +// +export const ERROR_WORKING_SET_QUOTA = 1453; + +// +// MessageId: ERROR_PAGEFILE_QUOTA +// +// MessageText: +// +// Insufficient quota to complete the requested service. +// +export const ERROR_PAGEFILE_QUOTA = 1454; + +// +// MessageId: ERROR_COMMITMENT_LIMIT +// +// MessageText: +// +// The paging file is too small for this operation to complete. +// +export const ERROR_COMMITMENT_LIMIT = 1455; + +// +// MessageId: ERROR_MENU_ITEM_NOT_FOUND +// +// MessageText: +// +// A menu item was not found. +// +export const ERROR_MENU_ITEM_NOT_FOUND = 1456; + +// +// MessageId: ERROR_INVALID_KEYBOARD_HANDLE +// +// MessageText: +// +// Invalid keyboard layout handle. +// +export const ERROR_INVALID_KEYBOARD_HANDLE = 1457; + +// +// MessageId: ERROR_HOOK_TYPE_NOT_ALLOWED +// +// MessageText: +// +// Hook type not allowed. +// +export const ERROR_HOOK_TYPE_NOT_ALLOWED = 1458; + +// +// MessageId: ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION +// +// MessageText: +// +// This operation requires an interactive window station. +// +export const ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION = 1459; + +// +// MessageId: ERROR_TIMEOUT +// +// MessageText: +// +// This operation returned because the timeout period expired. +// +export const ERROR_TIMEOUT = 1460; + +// +// MessageId: ERROR_INVALID_MONITOR_HANDLE +// +// MessageText: +// +// Invalid monitor handle. +// +export const ERROR_INVALID_MONITOR_HANDLE = 1461; + +// +// MessageId: ERROR_INCORRECT_SIZE +// +// MessageText: +// +// Incorrect size argument. +// +export const ERROR_INCORRECT_SIZE = 1462; + +// End of WinUser error codes + +/////////////////////////// +// // +// Eventlog Status Codes // +// // +/////////////////////////// + +// +// MessageId: ERROR_EVENTLOG_FILE_CORRUPT +// +// MessageText: +// +// The event log file is corrupted. +// +export const ERROR_EVENTLOG_FILE_CORRUPT = 1500; + +// +// MessageId: ERROR_EVENTLOG_CANT_START +// +// MessageText: +// +// No event log file could be opened, so the event logging service did not start. +// +export const ERROR_EVENTLOG_CANT_START = 1501; + +// +// MessageId: ERROR_LOG_FILE_FULL +// +// MessageText: +// +// The event log file is full. +// +export const ERROR_LOG_FILE_FULL = 1502; + +// +// MessageId: ERROR_EVENTLOG_FILE_CHANGED +// +// MessageText: +// +// The event log file has changed between read operations. +// +export const ERROR_EVENTLOG_FILE_CHANGED = 1503; + +// End of eventlog error codes + +/////////////////////////// +// // +// MSI Error Codes // +// // +/////////////////////////// + +// +// MessageId: ERROR_INSTALL_SERVICE_FAILURE +// +// MessageText: +// +// The Windows Installer Service could not be accessed. This can occur if you are running Windows in safe mode, or if the Windows Installer is not correctly installed. Contact your support personnel for assistance. +// +export const ERROR_INSTALL_SERVICE_FAILURE = 1601; + +// +// MessageId: ERROR_INSTALL_USEREXIT +// +// MessageText: +// +// User cancelled installation. +// +export const ERROR_INSTALL_USEREXIT = 1602; + +// +// MessageId: ERROR_INSTALL_FAILURE +// +// MessageText: +// +// Fatal error during installation. +// +export const ERROR_INSTALL_FAILURE = 1603; + +// +// MessageId: ERROR_INSTALL_SUSPEND +// +// MessageText: +// +// Installation suspended, incomplete. +// +export const ERROR_INSTALL_SUSPEND = 1604; + +// +// MessageId: ERROR_UNKNOWN_PRODUCT +// +// MessageText: +// +// This action is only valid for products that are currently installed. +// +export const ERROR_UNKNOWN_PRODUCT = 1605; + +// +// MessageId: ERROR_UNKNOWN_FEATURE +// +// MessageText: +// +// Feature ID not registered. +// +export const ERROR_UNKNOWN_FEATURE = 1606; + +// +// MessageId: ERROR_UNKNOWN_COMPONENT +// +// MessageText: +// +// Component ID not registered. +// +export const ERROR_UNKNOWN_COMPONENT = 1607; + +// +// MessageId: ERROR_UNKNOWN_PROPERTY +// +// MessageText: +// +// Unknown property. +// +export const ERROR_UNKNOWN_PROPERTY = 1608; + +// +// MessageId: ERROR_INVALID_HANDLE_STATE +// +// MessageText: +// +// Handle is in an invalid state. +// +export const ERROR_INVALID_HANDLE_STATE = 1609; + +// +// MessageId: ERROR_BAD_CONFIGURATION +// +// MessageText: +// +// The configuration data for this product is corrupt. Contact your support personnel. +// +export const ERROR_BAD_CONFIGURATION = 1610; + +// +// MessageId: ERROR_INDEX_ABSENT +// +// MessageText: +// +// Component qualifier not present. +// +export const ERROR_INDEX_ABSENT = 1611; + +// +// MessageId: ERROR_INSTALL_SOURCE_ABSENT +// +// MessageText: +// +// The installation source for this product is not available. Verify that the source exists and that you can access it. +// +export const ERROR_INSTALL_SOURCE_ABSENT = 1612; + +// +// MessageId: ERROR_INSTALL_PACKAGE_VERSION +// +// MessageText: +// +// This installation package cannot be installed by the Windows Installer service. You must install a Windows service pack that contains a newer version of the Windows Installer service. +// +export const ERROR_INSTALL_PACKAGE_VERSION = 1613; + +// +// MessageId: ERROR_PRODUCT_UNINSTALLED +// +// MessageText: +// +// Product is uninstalled. +// +export const ERROR_PRODUCT_UNINSTALLED = 1614; + +// +// MessageId: ERROR_BAD_QUERY_SYNTAX +// +// MessageText: +// +// SQL query syntax invalid or unsupported. +// +export const ERROR_BAD_QUERY_SYNTAX = 1615; + +// +// MessageId: ERROR_INVALID_FIELD +// +// MessageText: +// +// Record field does not exist. +// +export const ERROR_INVALID_FIELD = 1616; + +// +// MessageId: ERROR_DEVICE_REMOVED +// +// MessageText: +// +// The device has been removed. +// +export const ERROR_DEVICE_REMOVED = 1617; + +// +// MessageId: ERROR_INSTALL_ALREADY_RUNNING +// +// MessageText: +// +// Another installation is already in progress. Complete that installation before proceeding with this install. +// +export const ERROR_INSTALL_ALREADY_RUNNING = 1618; + +// +// MessageId: ERROR_INSTALL_PACKAGE_OPEN_FAILED +// +// MessageText: +// +// This installation package could not be opened. Verify that the package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer package. +// +export const ERROR_INSTALL_PACKAGE_OPEN_FAILED = 1619; + +// +// MessageId: ERROR_INSTALL_PACKAGE_INVALID +// +// MessageText: +// +// This installation package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer package. +// +export const ERROR_INSTALL_PACKAGE_INVALID = 1620; + +// +// MessageId: ERROR_INSTALL_UI_FAILURE +// +// MessageText: +// +// There was an error starting the Windows Installer service user interface. Contact your support personnel. +// +export const ERROR_INSTALL_UI_FAILURE = 1621; + +// +// MessageId: ERROR_INSTALL_LOG_FAILURE +// +// MessageText: +// +// Error opening installation log file. Verify that the specified log file location exists and that you can write to it. +// +export const ERROR_INSTALL_LOG_FAILURE = 1622; + +// +// MessageId: ERROR_INSTALL_LANGUAGE_UNSUPPORTED +// +// MessageText: +// +// The language of this installation package is not supported by your system. +// +export const ERROR_INSTALL_LANGUAGE_UNSUPPORTED = 1623; + +// +// MessageId: ERROR_INSTALL_TRANSFORM_FAILURE +// +// MessageText: +// +// Error applying transforms. Verify that the specified transform paths are valid. +// +export const ERROR_INSTALL_TRANSFORM_FAILURE = 1624; + +// +// MessageId: ERROR_INSTALL_PACKAGE_REJECTED +// +// MessageText: +// +// This installation is forbidden by system policy. Contact your system administrator. +// +export const ERROR_INSTALL_PACKAGE_REJECTED = 1625; + +// +// MessageId: ERROR_FUNCTION_NOT_CALLED +// +// MessageText: +// +// Function could not be executed. +// +export const ERROR_FUNCTION_NOT_CALLED = 1626; + +// +// MessageId: ERROR_FUNCTION_FAILED +// +// MessageText: +// +// Function failed during execution. +// +export const ERROR_FUNCTION_FAILED = 1627; + +// +// MessageId: ERROR_INVALID_TABLE +// +// MessageText: +// +// Invalid or unknown table specified. +// +export const ERROR_INVALID_TABLE = 1628; + +// +// MessageId: ERROR_DATATYPE_MISMATCH +// +// MessageText: +// +// Data supplied is of wrong type. +// +export const ERROR_DATATYPE_MISMATCH = 1629; + +// +// MessageId: ERROR_UNSUPPORTED_TYPE +// +// MessageText: +// +// Data of this type is not supported. +// +export const ERROR_UNSUPPORTED_TYPE = 1630; + +// +// MessageId: ERROR_CREATE_FAILED +// +// MessageText: +// +// The Windows Installer service failed to start. Contact your support personnel. +// +export const ERROR_CREATE_FAILED = 1631; + +// +// MessageId: ERROR_INSTALL_TEMP_UNWRITABLE +// +// MessageText: +// +// The Temp folder is on a drive that is full or is inaccessible. Free up space on the drive or verify that you have write permission on the Temp folder. +// +export const ERROR_INSTALL_TEMP_UNWRITABLE = 1632; + +// +// MessageId: ERROR_INSTALL_PLATFORM_UNSUPPORTED +// +// MessageText: +// +// This installation package is not supported by this processor type. Contact your product vendor. +// +export const ERROR_INSTALL_PLATFORM_UNSUPPORTED = 1633; + +// +// MessageId: ERROR_INSTALL_NOTUSED +// +// MessageText: +// +// Component not used on this computer. +// +export const ERROR_INSTALL_NOTUSED = 1634; + +// +// MessageId: ERROR_PATCH_PACKAGE_OPEN_FAILED +// +// MessageText: +// +// This patch package could not be opened. Verify that the patch package exists and that you can access it, or contact the application vendor to verify that this is a valid Windows Installer patch package. +// +export const ERROR_PATCH_PACKAGE_OPEN_FAILED = 1635; + +// +// MessageId: ERROR_PATCH_PACKAGE_INVALID +// +// MessageText: +// +// This patch package could not be opened. Contact the application vendor to verify that this is a valid Windows Installer patch package. +// +export const ERROR_PATCH_PACKAGE_INVALID = 1636; + +// +// MessageId: ERROR_PATCH_PACKAGE_UNSUPPORTED +// +// MessageText: +// +// This patch package cannot be processed by the Windows Installer service. You must install a Windows service pack that contains a newer version of the Windows Installer service. +// +export const ERROR_PATCH_PACKAGE_UNSUPPORTED = 1637; + +// +// MessageId: ERROR_PRODUCT_VERSION +// +// MessageText: +// +// Another version of this product is already installed. Installation of this version cannot continue. To configure or remove the existing version of this product, use Add/Remove Programs on the Control Panel. +// +export const ERROR_PRODUCT_VERSION = 1638; + +// +// MessageId: ERROR_INVALID_COMMAND_LINE +// +// MessageText: +// +// Invalid command line argument. Consult the Windows Installer SDK for detailed command line help. +// +export const ERROR_INVALID_COMMAND_LINE = 1639; + +// +// MessageId: ERROR_INSTALL_REMOTE_DISALLOWED +// +// MessageText: +// +// Only administrators have permission to add, remove, or configure server software during a Terminal services remote session. If you want to install or configure software on the server, contact your network administrator. +// +export const ERROR_INSTALL_REMOTE_DISALLOWED = 1640; + +// +// MessageId: ERROR_SUCCESS_REBOOT_INITIATED +// +// MessageText: +// +// The requested operation completed successfully. The system will be restarted so the changes can take effect. +// +export const ERROR_SUCCESS_REBOOT_INITIATED = 1641; + +// +// MessageId: ERROR_PATCH_TARGET_NOT_FOUND +// +// MessageText: +// +// The upgrade patch cannot be installed by the Windows Installer service because the program to be upgraded may be missing, or the upgrade patch may update a different version of the program. Verify that the program to be upgraded exists on your computer an +// d that you have the correct upgrade patch. +// +export const ERROR_PATCH_TARGET_NOT_FOUND = 1642; + +// +// MessageId: ERROR_PATCH_PACKAGE_REJECTED +// +// MessageText: +// +// The patch package is not permitted by software restriction policy. +// +export const ERROR_PATCH_PACKAGE_REJECTED = 1643; + +// +// MessageId: ERROR_INSTALL_TRANSFORM_REJECTED +// +// MessageText: +// +// One or more customizations are not permitted by software restriction policy. +// +export const ERROR_INSTALL_TRANSFORM_REJECTED = 1644; + +// +// MessageId: ERROR_INSTALL_REMOTE_PROHIBITED +// +// MessageText: +// +// The Windows Installer does not permit installation from a Remote Desktop Connection. +// +export const ERROR_INSTALL_REMOTE_PROHIBITED = 1645; + +// End of MSI error codes + +/////////////////////////// +// // +// RPC Status Codes // +// // +/////////////////////////// + +// +// MessageId: RPC_S_INVALID_STRING_BINDING +// +// MessageText: +// +// The string binding is invalid. +// +export const RPC_S_INVALID_STRING_BINDING = 1700; + +// +// MessageId: RPC_S_WRONG_KIND_OF_BINDING +// +// MessageText: +// +// The binding handle is not the correct type. +// +export const RPC_S_WRONG_KIND_OF_BINDING = 1701; + +// +// MessageId: RPC_S_INVALID_BINDING +// +// MessageText: +// +// The binding handle is invalid. +// +export const RPC_S_INVALID_BINDING = 1702; + +// +// MessageId: RPC_S_PROTSEQ_NOT_SUPPORTED +// +// MessageText: +// +// The RPC protocol sequence is not supported. +// +export const RPC_S_PROTSEQ_NOT_SUPPORTED = 1703; + +// +// MessageId: RPC_S_INVALID_RPC_PROTSEQ +// +// MessageText: +// +// The RPC protocol sequence is invalid. +// +export const RPC_S_INVALID_RPC_PROTSEQ = 1704; + +// +// MessageId: RPC_S_INVALID_STRING_UUID +// +// MessageText: +// +// The string universal unique identifier (UUID) is invalid. +// +export const RPC_S_INVALID_STRING_UUID = 1705; + +// +// MessageId: RPC_S_INVALID_ENDPOINT_FORMAT +// +// MessageText: +// +// The endpoint format is invalid. +// +export const RPC_S_INVALID_ENDPOINT_FORMAT = 1706; + +// +// MessageId: RPC_S_INVALID_NET_ADDR +// +// MessageText: +// +// The network address is invalid. +// +export const RPC_S_INVALID_NET_ADDR = 1707; + +// +// MessageId: RPC_S_NO_ENDPOINT_FOUND +// +// MessageText: +// +// No endpoint was found. +// +export const RPC_S_NO_ENDPOINT_FOUND = 1708; + +// +// MessageId: RPC_S_INVALID_TIMEOUT +// +// MessageText: +// +// The timeout value is invalid. +// +export const RPC_S_INVALID_TIMEOUT = 1709; + +// +// MessageId: RPC_S_OBJECT_NOT_FOUND +// +// MessageText: +// +// The object universal unique identifier (UUID) was not found. +// +export const RPC_S_OBJECT_NOT_FOUND = 1710; + +// +// MessageId: RPC_S_ALREADY_REGISTERED +// +// MessageText: +// +// The object universal unique identifier (UUID) has already been registered. +// +export const RPC_S_ALREADY_REGISTERED = 1711; + +// +// MessageId: RPC_S_TYPE_ALREADY_REGISTERED +// +// MessageText: +// +// The type universal unique identifier (UUID) has already been registered. +// +export const RPC_S_TYPE_ALREADY_REGISTERED = 1712; + +// +// MessageId: RPC_S_ALREADY_LISTENING +// +// MessageText: +// +// The RPC server is already listening. +// +export const RPC_S_ALREADY_LISTENING = 1713; + +// +// MessageId: RPC_S_NO_PROTSEQS_REGISTERED +// +// MessageText: +// +// No protocol sequences have been registered. +// +export const RPC_S_NO_PROTSEQS_REGISTERED = 1714; + +// +// MessageId: RPC_S_NOT_LISTENING +// +// MessageText: +// +// The RPC server is not listening. +// +export const RPC_S_NOT_LISTENING = 1715; + +// +// MessageId: RPC_S_UNKNOWN_MGR_TYPE +// +// MessageText: +// +// The manager type is unknown. +// +export const RPC_S_UNKNOWN_MGR_TYPE = 1716; + +// +// MessageId: RPC_S_UNKNOWN_IF +// +// MessageText: +// +// The interface is unknown. +// +export const RPC_S_UNKNOWN_IF = 1717; + +// +// MessageId: RPC_S_NO_BINDINGS +// +// MessageText: +// +// There are no bindings. +// +export const RPC_S_NO_BINDINGS = 1718; + +// +// MessageId: RPC_S_NO_PROTSEQS +// +// MessageText: +// +// There are no protocol sequences. +// +export const RPC_S_NO_PROTSEQS = 1719; + +// +// MessageId: RPC_S_CANT_CREATE_ENDPOINT +// +// MessageText: +// +// The endpoint cannot be created. +// +export const RPC_S_CANT_CREATE_ENDPOINT = 1720; + +// +// MessageId: RPC_S_OUT_OF_RESOURCES +// +// MessageText: +// +// Not enough resources are available to complete this operation. +// +export const RPC_S_OUT_OF_RESOURCES = 1721; + +// +// MessageId: RPC_S_SERVER_UNAVAILABLE +// +// MessageText: +// +// The RPC server is unavailable. +// +export const RPC_S_SERVER_UNAVAILABLE = 1722; + +// +// MessageId: RPC_S_SERVER_TOO_BUSY +// +// MessageText: +// +// The RPC server is too busy to complete this operation. +// +export const RPC_S_SERVER_TOO_BUSY = 1723; + +// +// MessageId: RPC_S_INVALID_NETWORK_OPTIONS +// +// MessageText: +// +// The network options are invalid. +// +export const RPC_S_INVALID_NETWORK_OPTIONS = 1724; + +// +// MessageId: RPC_S_NO_CALL_ACTIVE +// +// MessageText: +// +// There are no remote procedure calls active on this thread. +// +export const RPC_S_NO_CALL_ACTIVE = 1725; + +// +// MessageId: RPC_S_CALL_FAILED +// +// MessageText: +// +// The remote procedure call failed. +// +export const RPC_S_CALL_FAILED = 1726; + +// +// MessageId: RPC_S_CALL_FAILED_DNE +// +// MessageText: +// +// The remote procedure call failed and did not execute. +// +export const RPC_S_CALL_FAILED_DNE = 1727; + +// +// MessageId: RPC_S_PROTOCOL_ERROR +// +// MessageText: +// +// A remote procedure call (RPC) protocol error occurred. +// +export const RPC_S_PROTOCOL_ERROR = 1728; + +// +// MessageId: RPC_S_UNSUPPORTED_TRANS_SYN +// +// MessageText: +// +// The transfer syntax is not supported by the RPC server. +// +export const RPC_S_UNSUPPORTED_TRANS_SYN = 1730; + +// +// MessageId: RPC_S_UNSUPPORTED_TYPE +// +// MessageText: +// +// The universal unique identifier (UUID) type is not supported. +// +export const RPC_S_UNSUPPORTED_TYPE = 1732; + +// +// MessageId: RPC_S_INVALID_TAG +// +// MessageText: +// +// The tag is invalid. +// +export const RPC_S_INVALID_TAG = 1733; + +// +// MessageId: RPC_S_INVALID_BOUND +// +// MessageText: +// +// The array bounds are invalid. +// +export const RPC_S_INVALID_BOUND = 1734; + +// +// MessageId: RPC_S_NO_ENTRY_NAME +// +// MessageText: +// +// The binding does not contain an entry name. +// +export const RPC_S_NO_ENTRY_NAME = 1735; + +// +// MessageId: RPC_S_INVALID_NAME_SYNTAX +// +// MessageText: +// +// The name syntax is invalid. +// +export const RPC_S_INVALID_NAME_SYNTAX = 1736; + +// +// MessageId: RPC_S_UNSUPPORTED_NAME_SYNTAX +// +// MessageText: +// +// The name syntax is not supported. +// +export const RPC_S_UNSUPPORTED_NAME_SYNTAX = 1737; + +// +// MessageId: RPC_S_UUID_NO_ADDRESS +// +// MessageText: +// +// No network address is available to use to export construct a universal unique identifier (UUID). +// +export const RPC_S_UUID_NO_ADDRESS = 1739; + +// +// MessageId: RPC_S_DUPLICATE_ENDPOINT +// +// MessageText: +// +// The endpoint is a duplicate. +// +export const RPC_S_DUPLICATE_ENDPOINT = 1740; + +// +// MessageId: RPC_S_UNKNOWN_AUTHN_TYPE +// +// MessageText: +// +// The authentication type is unknown. +// +export const RPC_S_UNKNOWN_AUTHN_TYPE = 1741; + +// +// MessageId: RPC_S_MAX_CALLS_TOO_SMALL +// +// MessageText: +// +// The maximum number of calls is too small. +// +export const RPC_S_MAX_CALLS_TOO_SMALL = 1742; + +// +// MessageId: RPC_S_STRING_TOO_LONG +// +// MessageText: +// +// The string is too long. +// +export const RPC_S_STRING_TOO_LONG = 1743; + +// +// MessageId: RPC_S_PROTSEQ_NOT_FOUND +// +// MessageText: +// +// The RPC protocol sequence was not found. +// +export const RPC_S_PROTSEQ_NOT_FOUND = 1744; + +// +// MessageId: RPC_S_PROCNUM_OUT_OF_RANGE +// +// MessageText: +// +// The procedure number is out of range. +// +export const RPC_S_PROCNUM_OUT_OF_RANGE = 1745; + +// +// MessageId: RPC_S_BINDING_HAS_NO_AUTH +// +// MessageText: +// +// The binding does not contain any authentication information. +// +export const RPC_S_BINDING_HAS_NO_AUTH = 1746; + +// +// MessageId: RPC_S_UNKNOWN_AUTHN_SERVICE +// +// MessageText: +// +// The authentication service is unknown. +// +export const RPC_S_UNKNOWN_AUTHN_SERVICE = 1747; + +// +// MessageId: RPC_S_UNKNOWN_AUTHN_LEVEL +// +// MessageText: +// +// The authentication level is unknown. +// +export const RPC_S_UNKNOWN_AUTHN_LEVEL = 1748; + +// +// MessageId: RPC_S_INVALID_AUTH_IDENTITY +// +// MessageText: +// +// The security context is invalid. +// +export const RPC_S_INVALID_AUTH_IDENTITY = 1749; + +// +// MessageId: RPC_S_UNKNOWN_AUTHZ_SERVICE +// +// MessageText: +// +// The authorization service is unknown. +// +export const RPC_S_UNKNOWN_AUTHZ_SERVICE = 1750; + +// +// MessageId: EPT_S_INVALID_ENTRY +// +// MessageText: +// +// The entry is invalid. +// +export const EPT_S_INVALID_ENTRY = 1751; + +// +// MessageId: EPT_S_CANT_PERFORM_OP +// +// MessageText: +// +// The server endpoint cannot perform the operation. +// +export const EPT_S_CANT_PERFORM_OP = 1752; + +// +// MessageId: EPT_S_NOT_REGISTERED +// +// MessageText: +// +// There are no more endpoints available from the endpoint mapper. +// +export const EPT_S_NOT_REGISTERED = 1753; + +// +// MessageId: RPC_S_NOTHING_TO_EXPORT +// +// MessageText: +// +// No interfaces have been exported. +// +export const RPC_S_NOTHING_TO_EXPORT = 1754; + +// +// MessageId: RPC_S_INCOMPLETE_NAME +// +// MessageText: +// +// The entry name is incomplete. +// +export const RPC_S_INCOMPLETE_NAME = 1755; + +// +// MessageId: RPC_S_INVALID_VERS_OPTION +// +// MessageText: +// +// The version option is invalid. +// +export const RPC_S_INVALID_VERS_OPTION = 1756; + +// +// MessageId: RPC_S_NO_MORE_MEMBERS +// +// MessageText: +// +// There are no more members. +// +export const RPC_S_NO_MORE_MEMBERS = 1757; + +// +// MessageId: RPC_S_NOT_ALL_OBJS_UNEXPORTED +// +// MessageText: +// +// There is nothing to unexport. +// +export const RPC_S_NOT_ALL_OBJS_UNEXPORTED = 1758; + +// +// MessageId: RPC_S_INTERFACE_NOT_FOUND +// +// MessageText: +// +// The interface was not found. +// +export const RPC_S_INTERFACE_NOT_FOUND = 1759; + +// +// MessageId: RPC_S_ENTRY_ALREADY_EXISTS +// +// MessageText: +// +// The entry already exists. +// +export const RPC_S_ENTRY_ALREADY_EXISTS = 1760; + +// +// MessageId: RPC_S_ENTRY_NOT_FOUND +// +// MessageText: +// +// The entry is not found. +// +export const RPC_S_ENTRY_NOT_FOUND = 1761; + +// +// MessageId: RPC_S_NAME_SERVICE_UNAVAILABLE +// +// MessageText: +// +// The name service is unavailable. +// +export const RPC_S_NAME_SERVICE_UNAVAILABLE = 1762; + +// +// MessageId: RPC_S_INVALID_NAF_ID +// +// MessageText: +// +// The network address family is invalid. +// +export const RPC_S_INVALID_NAF_ID = 1763; + +// +// MessageId: RPC_S_CANNOT_SUPPORT +// +// MessageText: +// +// The requested operation is not supported. +// +export const RPC_S_CANNOT_SUPPORT = 1764; + +// +// MessageId: RPC_S_NO_CONTEXT_AVAILABLE +// +// MessageText: +// +// No security context is available to allow impersonation. +// +export const RPC_S_NO_CONTEXT_AVAILABLE = 1765; + +// +// MessageId: RPC_S_INTERNAL_ERROR +// +// MessageText: +// +// An internal error occurred in a remote procedure call (RPC). +// +export const RPC_S_INTERNAL_ERROR = 1766; + +// +// MessageId: RPC_S_ZERO_DIVIDE +// +// MessageText: +// +// The RPC server attempted an integer division by zero. +// +export const RPC_S_ZERO_DIVIDE = 1767; + +// +// MessageId: RPC_S_ADDRESS_ERROR +// +// MessageText: +// +// An addressing error occurred in the RPC server. +// +export const RPC_S_ADDRESS_ERROR = 1768; + +// +// MessageId: RPC_S_FP_DIV_ZERO +// +// MessageText: +// +// A floating-point operation at the RPC server caused a division by zero. +// +export const RPC_S_FP_DIV_ZERO = 1769; + +// +// MessageId: RPC_S_FP_UNDERFLOW +// +// MessageText: +// +// A floating-point underflow occurred at the RPC server. +// +export const RPC_S_FP_UNDERFLOW = 1770; + +// +// MessageId: RPC_S_FP_OVERFLOW +// +// MessageText: +// +// A floating-point overflow occurred at the RPC server. +// +export const RPC_S_FP_OVERFLOW = 1771; + +// +// MessageId: RPC_X_NO_MORE_ENTRIES +// +// MessageText: +// +// The list of RPC servers available for the binding of auto handles has been exhausted. +// +export const RPC_X_NO_MORE_ENTRIES = 1772; + +// +// MessageId: RPC_X_SS_CHAR_TRANS_OPEN_FAIL +// +// MessageText: +// +// Unable to open the character translation table file. +// +export const RPC_X_SS_CHAR_TRANS_OPEN_FAIL = 1773; + +// +// MessageId: RPC_X_SS_CHAR_TRANS_SHORT_FILE +// +// MessageText: +// +// The file containing the character translation table has fewer than 512 bytes. +// +export const RPC_X_SS_CHAR_TRANS_SHORT_FILE = 1774; + +// +// MessageId: RPC_X_SS_IN_NULL_CONTEXT +// +// MessageText: +// +// A null context handle was passed from the client to the host during a remote procedure call. +// +export const RPC_X_SS_IN_NULL_CONTEXT = 1775; + +// +// MessageId: RPC_X_SS_CONTEXT_DAMAGED +// +// MessageText: +// +// The context handle changed during a remote procedure call. +// +export const RPC_X_SS_CONTEXT_DAMAGED = 1777; + +// +// MessageId: RPC_X_SS_HANDLES_MISMATCH +// +// MessageText: +// +// The binding handles passed to a remote procedure call do not match. +// +export const RPC_X_SS_HANDLES_MISMATCH = 1778; + +// +// MessageId: RPC_X_SS_CANNOT_GET_CALL_HANDLE +// +// MessageText: +// +// The stub is unable to get the remote procedure call handle. +// +export const RPC_X_SS_CANNOT_GET_CALL_HANDLE = 1779; + +// +// MessageId: RPC_X_NULL_REF_POINTER +// +// MessageText: +// +// A null reference pointer was passed to the stub. +// +export const RPC_X_NULL_REF_POINTER = 1780; + +// +// MessageId: RPC_X_ENUM_VALUE_OUT_OF_RANGE +// +// MessageText: +// +// The enumeration value is out of range. +// +export const RPC_X_ENUM_VALUE_OUT_OF_RANGE = 1781; + +// +// MessageId: RPC_X_BYTE_COUNT_TOO_SMALL +// +// MessageText: +// +// The byte count is too small. +// +export const RPC_X_BYTE_COUNT_TOO_SMALL = 1782; + +// +// MessageId: RPC_X_BAD_STUB_DATA +// +// MessageText: +// +// The stub received bad data. +// +export const RPC_X_BAD_STUB_DATA = 1783; + +// +// MessageId: ERROR_INVALID_USER_BUFFER +// +// MessageText: +// +// The supplied user buffer is not valid for the requested operation. +// +export const ERROR_INVALID_USER_BUFFER = 1784; + +// +// MessageId: ERROR_UNRECOGNIZED_MEDIA +// +// MessageText: +// +// The disk media is not recognized. It may not be formatted. +// +export const ERROR_UNRECOGNIZED_MEDIA = 1785; + +// +// MessageId: ERROR_NO_TRUST_LSA_SECRET +// +// MessageText: +// +// The workstation does not have a trust secret. +// +export const ERROR_NO_TRUST_LSA_SECRET = 1786; + +// +// MessageId: ERROR_NO_TRUST_SAM_ACCOUNT +// +// MessageText: +// +// The security database on the server does not have a computer account for this workstation trust relationship. +// +export const ERROR_NO_TRUST_SAM_ACCOUNT = 1787; + +// +// MessageId: ERROR_TRUSTED_DOMAIN_FAILURE +// +// MessageText: +// +// The trust relationship between the primary domain and the trusted domain failed. +// +export const ERROR_TRUSTED_DOMAIN_FAILURE = 1788; + +// +// MessageId: ERROR_TRUSTED_RELATIONSHIP_FAILURE +// +// MessageText: +// +// The trust relationship between this workstation and the primary domain failed. +// +export const ERROR_TRUSTED_RELATIONSHIP_FAILURE = 1789; + +// +// MessageId: ERROR_TRUST_FAILURE +// +// MessageText: +// +// The network logon failed. +// +export const ERROR_TRUST_FAILURE = 1790; + +// +// MessageId: RPC_S_CALL_IN_PROGRESS +// +// MessageText: +// +// A remote procedure call is already in progress for this thread. +// +export const RPC_S_CALL_IN_PROGRESS = 1791; + +// +// MessageId: ERROR_NETLOGON_NOT_STARTED +// +// MessageText: +// +// An attempt was made to logon, but the network logon service was not started. +// +export const ERROR_NETLOGON_NOT_STARTED = 1792; + +// +// MessageId: ERROR_ACCOUNT_EXPIRED +// +// MessageText: +// +// The user's account has expired. +// +export const ERROR_ACCOUNT_EXPIRED = 1793; + +// +// MessageId: ERROR_REDIRECTOR_HAS_OPEN_HANDLES +// +// MessageText: +// +// The redirector is in use and cannot be unloaded. +// +export const ERROR_REDIRECTOR_HAS_OPEN_HANDLES = 1794; + +// +// MessageId: ERROR_PRINTER_DRIVER_ALREADY_INSTALLED +// +// MessageText: +// +// The specified printer driver is already installed. +// +export const ERROR_PRINTER_DRIVER_ALREADY_INSTALLED = 1795; + +// +// MessageId: ERROR_UNKNOWN_PORT +// +// MessageText: +// +// The specified port is unknown. +// +export const ERROR_UNKNOWN_PORT = 1796; + +// +// MessageId: ERROR_UNKNOWN_PRINTER_DRIVER +// +// MessageText: +// +// The printer driver is unknown. +// +export const ERROR_UNKNOWN_PRINTER_DRIVER = 1797; + +// +// MessageId: ERROR_UNKNOWN_PRINTPROCESSOR +// +// MessageText: +// +// The print processor is unknown. +// +export const ERROR_UNKNOWN_PRINTPROCESSOR = 1798; + +// +// MessageId: ERROR_INVALID_SEPARATOR_FILE +// +// MessageText: +// +// The specified separator file is invalid. +// +export const ERROR_INVALID_SEPARATOR_FILE = 1799; + +// +// MessageId: ERROR_INVALID_PRIORITY +// +// MessageText: +// +// The specified priority is invalid. +// +export const ERROR_INVALID_PRIORITY = 1800; + +// +// MessageId: ERROR_INVALID_PRINTER_NAME +// +// MessageText: +// +// The printer name is invalid. +// +export const ERROR_INVALID_PRINTER_NAME = 1801; + +// +// MessageId: ERROR_PRINTER_ALREADY_EXISTS +// +// MessageText: +// +// The printer already exists. +// +export const ERROR_PRINTER_ALREADY_EXISTS = 1802; + +// +// MessageId: ERROR_INVALID_PRINTER_COMMAND +// +// MessageText: +// +// The printer command is invalid. +// +export const ERROR_INVALID_PRINTER_COMMAND = 1803; + +// +// MessageId: ERROR_INVALID_DATATYPE +// +// MessageText: +// +// The specified datatype is invalid. +// +export const ERROR_INVALID_DATATYPE = 1804; + +// +// MessageId: ERROR_INVALID_ENVIRONMENT +// +// MessageText: +// +// The environment specified is invalid. +// +export const ERROR_INVALID_ENVIRONMENT = 1805; + +// +// MessageId: RPC_S_NO_MORE_BINDINGS +// +// MessageText: +// +// There are no more bindings. +// +export const RPC_S_NO_MORE_BINDINGS = 1806; + +// +// MessageId: ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT +// +// MessageText: +// +// The account used is an interdomain trust account. Use your global user account or local user account to access this server. +// +export const ERROR_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 1807; + +// +// MessageId: ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT +// +// MessageText: +// +// The account used is a computer account. Use your global user account or local user account to access this server. +// +export const ERROR_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 1808; + +// +// MessageId: ERROR_NOLOGON_SERVER_TRUST_ACCOUNT +// +// MessageText: +// +// The account used is a server trust account. Use your global user account or local user account to access this server. +// +export const ERROR_NOLOGON_SERVER_TRUST_ACCOUNT = 1809; + +// +// MessageId: ERROR_DOMAIN_TRUST_INCONSISTENT +// +// MessageText: +// +// The name or security ID (SID) of the domain specified is inconsistent with the trust information for that domain. +// +export const ERROR_DOMAIN_TRUST_INCONSISTENT = 1810; + +// +// MessageId: ERROR_SERVER_HAS_OPEN_HANDLES +// +// MessageText: +// +// The server is in use and cannot be unloaded. +// +export const ERROR_SERVER_HAS_OPEN_HANDLES = 1811; + +// +// MessageId: ERROR_RESOURCE_DATA_NOT_FOUND +// +// MessageText: +// +// The specified image file did not contain a resource section. +// +export const ERROR_RESOURCE_DATA_NOT_FOUND = 1812; + +// +// MessageId: ERROR_RESOURCE_TYPE_NOT_FOUND +// +// MessageText: +// +// The specified resource type cannot be found in the image file. +// +export const ERROR_RESOURCE_TYPE_NOT_FOUND = 1813; + +// +// MessageId: ERROR_RESOURCE_NAME_NOT_FOUND +// +// MessageText: +// +// The specified resource name cannot be found in the image file. +// +export const ERROR_RESOURCE_NAME_NOT_FOUND = 1814; + +// +// MessageId: ERROR_RESOURCE_LANG_NOT_FOUND +// +// MessageText: +// +// The specified resource language ID cannot be found in the image file. +// +export const ERROR_RESOURCE_LANG_NOT_FOUND = 1815; + +// +// MessageId: ERROR_NOT_ENOUGH_QUOTA +// +// MessageText: +// +// Not enough quota is available to process this command. +// +export const ERROR_NOT_ENOUGH_QUOTA = 1816; + +// +// MessageId: RPC_S_NO_INTERFACES +// +// MessageText: +// +// No interfaces have been registered. +// +export const RPC_S_NO_INTERFACES = 1817; + +// +// MessageId: RPC_S_CALL_CANCELLED +// +// MessageText: +// +// The remote procedure call was cancelled. +// +export const RPC_S_CALL_CANCELLED = 1818; + +// +// MessageId: RPC_S_BINDING_INCOMPLETE +// +// MessageText: +// +// The binding handle does not contain all required information. +// +export const RPC_S_BINDING_INCOMPLETE = 1819; + +// +// MessageId: RPC_S_COMM_FAILURE +// +// MessageText: +// +// A communications failure occurred during a remote procedure call. +// +export const RPC_S_COMM_FAILURE = 1820; + +// +// MessageId: RPC_S_UNSUPPORTED_AUTHN_LEVEL +// +// MessageText: +// +// The requested authentication level is not supported. +// +export const RPC_S_UNSUPPORTED_AUTHN_LEVEL = 1821; + +// +// MessageId: RPC_S_NO_PRINC_NAME +// +// MessageText: +// +// No principal name registered. +// +export const RPC_S_NO_PRINC_NAME = 1822; + +// +// MessageId: RPC_S_NOT_RPC_ERROR +// +// MessageText: +// +// The error specified is not a valid Windows RPC error code. +// +export const RPC_S_NOT_RPC_ERROR = 1823; + +// +// MessageId: RPC_S_UUID_LOCAL_ONLY +// +// MessageText: +// +// A UUID that is valid only on this computer has been allocated. +// +export const RPC_S_UUID_LOCAL_ONLY = 1824; + +// +// MessageId: RPC_S_SEC_PKG_ERROR +// +// MessageText: +// +// A security package specific error occurred. +// +export const RPC_S_SEC_PKG_ERROR = 1825; + +// +// MessageId: RPC_S_NOT_CANCELLED +// +// MessageText: +// +// Thread is not canceled. +// +export const RPC_S_NOT_CANCELLED = 1826; + +// +// MessageId: RPC_X_INVALID_ES_ACTION +// +// MessageText: +// +// Invalid operation on the encoding/decoding handle. +// +export const RPC_X_INVALID_ES_ACTION = 1827; + +// +// MessageId: RPC_X_WRONG_ES_VERSION +// +// MessageText: +// +// Incompatible version of the serializing package. +// +export const RPC_X_WRONG_ES_VERSION = 1828; + +// +// MessageId: RPC_X_WRONG_STUB_VERSION +// +// MessageText: +// +// Incompatible version of the RPC stub. +// +export const RPC_X_WRONG_STUB_VERSION = 1829; + +// +// MessageId: RPC_X_INVALID_PIPE_OBJECT +// +// MessageText: +// +// The RPC pipe object is invalid or corrupted. +// +export const RPC_X_INVALID_PIPE_OBJECT = 1830; + +// +// MessageId: RPC_X_WRONG_PIPE_ORDER +// +// MessageText: +// +// An invalid operation was attempted on an RPC pipe object. +// +export const RPC_X_WRONG_PIPE_ORDER = 1831; + +// +// MessageId: RPC_X_WRONG_PIPE_VERSION +// +// MessageText: +// +// Unsupported RPC pipe version. +// +export const RPC_X_WRONG_PIPE_VERSION = 1832; + +// +// MessageId: RPC_S_GROUP_MEMBER_NOT_FOUND +// +// MessageText: +// +// The group member was not found. +// +export const RPC_S_GROUP_MEMBER_NOT_FOUND = 1898; + +// +// MessageId: EPT_S_CANT_CREATE +// +// MessageText: +// +// The endpoint mapper database entry could not be created. +// +export const EPT_S_CANT_CREATE = 1899; + +// +// MessageId: RPC_S_INVALID_OBJECT +// +// MessageText: +// +// The object universal unique identifier (UUID) is the nil UUID. +// +export const RPC_S_INVALID_OBJECT = 1900; + +// +// MessageId: ERROR_INVALID_TIME +// +// MessageText: +// +// The specified time is invalid. +// +export const ERROR_INVALID_TIME = 1901; + +// +// MessageId: ERROR_INVALID_FORM_NAME +// +// MessageText: +// +// The specified form name is invalid. +// +export const ERROR_INVALID_FORM_NAME = 1902; + +// +// MessageId: ERROR_INVALID_FORM_SIZE +// +// MessageText: +// +// The specified form size is invalid. +// +export const ERROR_INVALID_FORM_SIZE = 1903; + +// +// MessageId: ERROR_ALREADY_WAITING +// +// MessageText: +// +// The specified printer handle is already being waited on +// +export const ERROR_ALREADY_WAITING = 1904; + +// +// MessageId: ERROR_PRINTER_DELETED +// +// MessageText: +// +// The specified printer has been deleted. +// +export const ERROR_PRINTER_DELETED = 1905; + +// +// MessageId: ERROR_INVALID_PRINTER_STATE +// +// MessageText: +// +// The state of the printer is invalid. +// +export const ERROR_INVALID_PRINTER_STATE = 1906; + +// +// MessageId: ERROR_PASSWORD_MUST_CHANGE +// +// MessageText: +// +// The user's password must be changed before logging on the first time. +// +export const ERROR_PASSWORD_MUST_CHANGE = 1907; + +// +// MessageId: ERROR_DOMAIN_CONTROLLER_NOT_FOUND +// +// MessageText: +// +// Could not find the domain controller for this domain. +// +export const ERROR_DOMAIN_CONTROLLER_NOT_FOUND = 1908; + +// +// MessageId: ERROR_ACCOUNT_LOCKED_OUT +// +// MessageText: +// +// The referenced account is currently locked out and may not be logged on to. +// +export const ERROR_ACCOUNT_LOCKED_OUT = 1909; + +// +// MessageId: OR_INVALID_OXID +// +// MessageText: +// +// The object exporter specified was not found. +// +export const OR_INVALID_OXID = 1910; + +// +// MessageId: OR_INVALID_OID +// +// MessageText: +// +// The object specified was not found. +// +export const OR_INVALID_OID = 1911; + +// +// MessageId: OR_INVALID_SET +// +// MessageText: +// +// The object resolver set specified was not found. +// +export const OR_INVALID_SET = 1912; + +// +// MessageId: RPC_S_SEND_INCOMPLETE +// +// MessageText: +// +// Some data remains to be sent in the request buffer. +// +export const RPC_S_SEND_INCOMPLETE = 1913; + +// +// MessageId: RPC_S_INVALID_ASYNC_HANDLE +// +// MessageText: +// +// Invalid asynchronous remote procedure call handle. +// +export const RPC_S_INVALID_ASYNC_HANDLE = 1914; + +// +// MessageId: RPC_S_INVALID_ASYNC_CALL +// +// MessageText: +// +// Invalid asynchronous RPC call handle for this operation. +// +export const RPC_S_INVALID_ASYNC_CALL = 1915; + +// +// MessageId: RPC_X_PIPE_CLOSED +// +// MessageText: +// +// The RPC pipe object has already been closed. +// +export const RPC_X_PIPE_CLOSED = 1916; + +// +// MessageId: RPC_X_PIPE_DISCIPLINE_ERROR +// +// MessageText: +// +// The RPC call completed before all pipes were processed. +// +export const RPC_X_PIPE_DISCIPLINE_ERROR = 1917; + +// +// MessageId: RPC_X_PIPE_EMPTY +// +// MessageText: +// +// No more data is available from the RPC pipe. +// +export const RPC_X_PIPE_EMPTY = 1918; + +// +// MessageId: ERROR_NO_SITENAME +// +// MessageText: +// +// No site name is available for this machine. +// +export const ERROR_NO_SITENAME = 1919; + +// +// MessageId: ERROR_CANT_ACCESS_FILE +// +// MessageText: +// +// The file can not be accessed by the system. +// +export const ERROR_CANT_ACCESS_FILE = 1920; + +// +// MessageId: ERROR_CANT_RESOLVE_FILENAME +// +// MessageText: +// +// The name of the file cannot be resolved by the system. +// +export const ERROR_CANT_RESOLVE_FILENAME = 1921; + +// +// MessageId: RPC_S_ENTRY_TYPE_MISMATCH +// +// MessageText: +// +// The entry is not of the expected type. +// +export const RPC_S_ENTRY_TYPE_MISMATCH = 1922; + +// +// MessageId: RPC_S_NOT_ALL_OBJS_EXPORTED +// +// MessageText: +// +// Not all object UUIDs could be exported to the specified entry. +// +export const RPC_S_NOT_ALL_OBJS_EXPORTED = 1923; + +// +// MessageId: RPC_S_INTERFACE_NOT_EXPORTED +// +// MessageText: +// +// Interface could not be exported to the specified entry. +// +export const RPC_S_INTERFACE_NOT_EXPORTED = 1924; + +// +// MessageId: RPC_S_PROFILE_NOT_ADDED +// +// MessageText: +// +// The specified profile entry could not be added. +// +export const RPC_S_PROFILE_NOT_ADDED = 1925; + +// +// MessageId: RPC_S_PRF_ELT_NOT_ADDED +// +// MessageText: +// +// The specified profile element could not be added. +// +export const RPC_S_PRF_ELT_NOT_ADDED = 1926; + +// +// MessageId: RPC_S_PRF_ELT_NOT_REMOVED +// +// MessageText: +// +// The specified profile element could not be removed. +// +export const RPC_S_PRF_ELT_NOT_REMOVED = 1927; + +// +// MessageId: RPC_S_GRP_ELT_NOT_ADDED +// +// MessageText: +// +// The group element could not be added. +// +export const RPC_S_GRP_ELT_NOT_ADDED = 1928; + +// +// MessageId: RPC_S_GRP_ELT_NOT_REMOVED +// +// MessageText: +// +// The group element could not be removed. +// +export const RPC_S_GRP_ELT_NOT_REMOVED = 1929; + +// +// MessageId: ERROR_KM_DRIVER_BLOCKED +// +// MessageText: +// +// The printer driver is not compatible with a policy enabled on your computer that blocks NT 4.0 drivers. +// +export const ERROR_KM_DRIVER_BLOCKED = 1930; + +// +// MessageId: ERROR_CONTEXT_EXPIRED +// +// MessageText: +// +// The context has expired and can no longer be used. +// +export const ERROR_CONTEXT_EXPIRED = 1931; + +// +// MessageId: ERROR_PER_USER_TRUST_QUOTA_EXCEEDED +// +// MessageText: +// +// The current user's delegated trust creation quota has been exceeded. +// +export const ERROR_PER_USER_TRUST_QUOTA_EXCEEDED = 1932; + +// +// MessageId: ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED +// +// MessageText: +// +// The total delegated trust creation quota has been exceeded. +// +export const ERROR_ALL_USER_TRUST_QUOTA_EXCEEDED = 1933; + +// +// MessageId: ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED +// +// MessageText: +// +// The current user's delegated trust deletion quota has been exceeded. +// +export const ERROR_USER_DELETE_TRUST_QUOTA_EXCEEDED = 1934; + +// +// MessageId: ERROR_AUTHENTICATION_FIREWALL_FAILED +// +// MessageText: +// +// Logon Failure: The machine you are logging onto is protected by an authentication firewall. The specified account is not allowed to authenticate to the machine. +// +export const ERROR_AUTHENTICATION_FIREWALL_FAILED = 1935; + +// +// MessageId: ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED +// +// MessageText: +// +// Remote connections to the Print Spooler are blocked by a policy set on your machine. +// +export const ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED = 1936; + +/////////////////////////// +// // +// OpenGL Error Code // +// // +/////////////////////////// + +// +// MessageId: ERROR_INVALID_PIXEL_FORMAT +// +// MessageText: +// +// The pixel format is invalid. +// +export const ERROR_INVALID_PIXEL_FORMAT = 2000; + +// +// MessageId: ERROR_BAD_DRIVER +// +// MessageText: +// +// The specified driver is invalid. +// +export const ERROR_BAD_DRIVER = 2001; + +// +// MessageId: ERROR_INVALID_WINDOW_STYLE +// +// MessageText: +// +// The window style or class attribute is invalid for this operation. +// +export const ERROR_INVALID_WINDOW_STYLE = 2002; + +// +// MessageId: ERROR_METAFILE_NOT_SUPPORTED +// +// MessageText: +// +// The requested metafile operation is not supported. +// +export const ERROR_METAFILE_NOT_SUPPORTED = 2003; + +// +// MessageId: ERROR_TRANSFORM_NOT_SUPPORTED +// +// MessageText: +// +// The requested transformation operation is not supported. +// +export const ERROR_TRANSFORM_NOT_SUPPORTED = 2004; + +// +// MessageId: ERROR_CLIPPING_NOT_SUPPORTED +// +// MessageText: +// +// The requested clipping operation is not supported. +// +export const ERROR_CLIPPING_NOT_SUPPORTED = 2005; + +// End of OpenGL error codes + +/////////////////////////////////////////// +// // +// Image Color Management Error Code // +// // +/////////////////////////////////////////// + +// +// MessageId: ERROR_INVALID_CMM +// +// MessageText: +// +// The specified color management module is invalid. +// +export const ERROR_INVALID_CMM = 2010; + +// +// MessageId: ERROR_INVALID_PROFILE +// +// MessageText: +// +// The specified color profile is invalid. +// +export const ERROR_INVALID_PROFILE = 2011; + +// +// MessageId: ERROR_TAG_NOT_FOUND +// +// MessageText: +// +// The specified tag was not found. +// +export const ERROR_TAG_NOT_FOUND = 2012; + +// +// MessageId: ERROR_TAG_NOT_PRESENT +// +// MessageText: +// +// A required tag is not present. +// +export const ERROR_TAG_NOT_PRESENT = 2013; + +// +// MessageId: ERROR_DUPLICATE_TAG +// +// MessageText: +// +// The specified tag is already present. +// +export const ERROR_DUPLICATE_TAG = 2014; + +// +// MessageId: ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE +// +// MessageText: +// +// The specified color profile is not associated with any device. +// +export const ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE = 2015; + +// +// MessageId: ERROR_PROFILE_NOT_FOUND +// +// MessageText: +// +// The specified color profile was not found. +// +export const ERROR_PROFILE_NOT_FOUND = 2016; + +// +// MessageId: ERROR_INVALID_COLORSPACE +// +// MessageText: +// +// The specified color space is invalid. +// +export const ERROR_INVALID_COLORSPACE = 2017; + +// +// MessageId: ERROR_ICM_NOT_ENABLED +// +// MessageText: +// +// Image Color Management is not enabled. +// +export const ERROR_ICM_NOT_ENABLED = 2018; + +// +// MessageId: ERROR_DELETING_ICM_XFORM +// +// MessageText: +// +// There was an error while deleting the color transform. +// +export const ERROR_DELETING_ICM_XFORM = 2019; + +// +// MessageId: ERROR_INVALID_TRANSFORM +// +// MessageText: +// +// The specified color transform is invalid. +// +export const ERROR_INVALID_TRANSFORM = 2020; + +// +// MessageId: ERROR_COLORSPACE_MISMATCH +// +// MessageText: +// +// The specified transform does not match the bitmap's color space. +// +export const ERROR_COLORSPACE_MISMATCH = 2021; + +// +// MessageId: ERROR_INVALID_COLORINDEX +// +// MessageText: +// +// The specified named color index is not present in the profile. +// +export const ERROR_INVALID_COLORINDEX = 2022; + +/////////////////////////// +// // +// Winnet32 Status Codes // +// // +// The range 2100 through 2999 is reserved for network status codes. +// See lmerr.h for a complete listing +/////////////////////////// + +// +// MessageId: ERROR_CONNECTED_OTHER_PASSWORD +// +// MessageText: +// +// The network connection was made successfully, but the user had to be prompted for a password other than the one originally specified. +// +export const ERROR_CONNECTED_OTHER_PASSWORD = 2108; + +// +// MessageId: ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT +// +// MessageText: +// +// The network connection was made successfully using default credentials. +// +export const ERROR_CONNECTED_OTHER_PASSWORD_DEFAULT = 2109; + +// +// MessageId: ERROR_BAD_USERNAME +// +// MessageText: +// +// The specified username is invalid. +// +export const ERROR_BAD_USERNAME = 2202; + +// +// MessageId: ERROR_NOT_CONNECTED +// +// MessageText: +// +// This network connection does not exist. +// +export const ERROR_NOT_CONNECTED = 2250; + +// +// MessageId: ERROR_OPEN_FILES +// +// MessageText: +// +// This network connection has files open or requests pending. +// +export const ERROR_OPEN_FILES = 2401; + +// +// MessageId: ERROR_ACTIVE_CONNECTIONS +// +// MessageText: +// +// Active connections still exist. +// +export const ERROR_ACTIVE_CONNECTIONS = 2402; + +// +// MessageId: ERROR_DEVICE_IN_USE +// +// MessageText: +// +// The device is in use by an active process and cannot be disconnected. +// +export const ERROR_DEVICE_IN_USE = 2404; + +//////////////////////////////////// +// // +// Win32 Spooler Error Codes // +// // +//////////////////////////////////// +// +// MessageId: ERROR_UNKNOWN_PRINT_MONITOR +// +// MessageText: +// +// The specified print monitor is unknown. +// +export const ERROR_UNKNOWN_PRINT_MONITOR = 3000; + +// +// MessageId: ERROR_PRINTER_DRIVER_IN_USE +// +// MessageText: +// +// The specified printer driver is currently in use. +// +export const ERROR_PRINTER_DRIVER_IN_USE = 3001; + +// +// MessageId: ERROR_SPOOL_FILE_NOT_FOUND +// +// MessageText: +// +// The spool file was not found. +// +export const ERROR_SPOOL_FILE_NOT_FOUND = 3002; + +// +// MessageId: ERROR_SPL_NO_STARTDOC +// +// MessageText: +// +// A StartDocPrinter call was not issued. +// +export const ERROR_SPL_NO_STARTDOC = 3003; + +// +// MessageId: ERROR_SPL_NO_ADDJOB +// +// MessageText: +// +// An AddJob call was not issued. +// +export const ERROR_SPL_NO_ADDJOB = 3004; + +// +// MessageId: ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED +// +// MessageText: +// +// The specified print processor has already been installed. +// +export const ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED = 3005; + +// +// MessageId: ERROR_PRINT_MONITOR_ALREADY_INSTALLED +// +// MessageText: +// +// The specified print monitor has already been installed. +// +export const ERROR_PRINT_MONITOR_ALREADY_INSTALLED = 3006; + +// +// MessageId: ERROR_INVALID_PRINT_MONITOR +// +// MessageText: +// +// The specified print monitor does not have the required functions. +// +export const ERROR_INVALID_PRINT_MONITOR = 3007; + +// +// MessageId: ERROR_PRINT_MONITOR_IN_USE +// +// MessageText: +// +// The specified print monitor is currently in use. +// +export const ERROR_PRINT_MONITOR_IN_USE = 3008; + +// +// MessageId: ERROR_PRINTER_HAS_JOBS_QUEUED +// +// MessageText: +// +// The requested operation is not allowed when there are jobs queued to the printer. +// +export const ERROR_PRINTER_HAS_JOBS_QUEUED = 3009; + +// +// MessageId: ERROR_SUCCESS_REBOOT_REQUIRED +// +// MessageText: +// +// The requested operation is successful. Changes will not be effective until the system is rebooted. +// +export const ERROR_SUCCESS_REBOOT_REQUIRED = 3010; + +// +// MessageId: ERROR_SUCCESS_RESTART_REQUIRED +// +// MessageText: +// +// The requested operation is successful. Changes will not be effective until the service is restarted. +// +export const ERROR_SUCCESS_RESTART_REQUIRED = 3011; + +// +// MessageId: ERROR_PRINTER_NOT_FOUND +// +// MessageText: +// +// No printers were found. +// +export const ERROR_PRINTER_NOT_FOUND = 3012; + +// +// MessageId: ERROR_PRINTER_DRIVER_WARNED +// +// MessageText: +// +// The printer driver is known to be unreliable. +// +export const ERROR_PRINTER_DRIVER_WARNED = 3013; + +// +// MessageId: ERROR_PRINTER_DRIVER_BLOCKED +// +// MessageText: +// +// The printer driver is known to harm the system. +// +export const ERROR_PRINTER_DRIVER_BLOCKED = 3014; + +//////////////////////////////////// +// // +// Wins Error Codes // +// // +//////////////////////////////////// +// +// MessageId: ERROR_WINS_INTERNAL +// +// MessageText: +// +// WINS encountered an error while processing the command. +// +export const ERROR_WINS_INTERNAL = 4000; + +// +// MessageId: ERROR_CAN_NOT_DEL_LOCAL_WINS +// +// MessageText: +// +// The local WINS can not be deleted. +// +export const ERROR_CAN_NOT_DEL_LOCAL_WINS = 4001; + +// +// MessageId: ERROR_STATIC_INIT +// +// MessageText: +// +// The importation from the file failed. +// +export const ERROR_STATIC_INIT = 4002; + +// +// MessageId: ERROR_INC_BACKUP +// +// MessageText: +// +// The backup failed. Was a full backup done before? +// +export const ERROR_INC_BACKUP = 4003; + +// +// MessageId: ERROR_FULL_BACKUP +// +// MessageText: +// +// The backup failed. Check the directory to which you are backing the database. +// +export const ERROR_FULL_BACKUP = 4004; + +// +// MessageId: ERROR_REC_NON_EXISTENT +// +// MessageText: +// +// The name does not exist in the WINS database. +// +export const ERROR_REC_NON_EXISTENT = 4005; + +// +// MessageId: ERROR_RPL_NOT_ALLOWED +// +// MessageText: +// +// Replication with a nonconfigured partner is not allowed. +// +export const ERROR_RPL_NOT_ALLOWED = 4006; + +//////////////////////////////////// +// // +// DHCP Error Codes // +// // +//////////////////////////////////// +// +// MessageId: ERROR_DHCP_ADDRESS_CONFLICT +// +// MessageText: +// +// The DHCP client has obtained an IP address that is already in use on the network. The local interface will be disabled until the DHCP client can obtain a new address. +// +export const ERROR_DHCP_ADDRESS_CONFLICT = 4100; + +//////////////////////////////////// +// // +// WMI Error Codes // +// // +//////////////////////////////////// +// +// MessageId: ERROR_WMI_GUID_NOT_FOUND +// +// MessageText: +// +// The GUID passed was not recognized as valid by a WMI data provider. +// +export const ERROR_WMI_GUID_NOT_FOUND = 4200; + +// +// MessageId: ERROR_WMI_INSTANCE_NOT_FOUND +// +// MessageText: +// +// The instance name passed was not recognized as valid by a WMI data provider. +// +export const ERROR_WMI_INSTANCE_NOT_FOUND = 4201; + +// +// MessageId: ERROR_WMI_ITEMID_NOT_FOUND +// +// MessageText: +// +// The data item ID passed was not recognized as valid by a WMI data provider. +// +export const ERROR_WMI_ITEMID_NOT_FOUND = 4202; + +// +// MessageId: ERROR_WMI_TRY_AGAIN +// +// MessageText: +// +// The WMI request could not be completed and should be retried. +// +export const ERROR_WMI_TRY_AGAIN = 4203; + +// +// MessageId: ERROR_WMI_DP_NOT_FOUND +// +// MessageText: +// +// The WMI data provider could not be located. +// +export const ERROR_WMI_DP_NOT_FOUND = 4204; + +// +// MessageId: ERROR_WMI_UNRESOLVED_INSTANCE_REF +// +// MessageText: +// +// The WMI data provider references an instance set that has not been registered. +// +export const ERROR_WMI_UNRESOLVED_INSTANCE_REF = 4205; + +// +// MessageId: ERROR_WMI_ALREADY_ENABLED +// +// MessageText: +// +// The WMI data block or event notification has already been enabled. +// +export const ERROR_WMI_ALREADY_ENABLED = 4206; + +// +// MessageId: ERROR_WMI_GUID_DISCONNECTED +// +// MessageText: +// +// The WMI data block is no longer available. +// +export const ERROR_WMI_GUID_DISCONNECTED = 4207; + +// +// MessageId: ERROR_WMI_SERVER_UNAVAILABLE +// +// MessageText: +// +// The WMI data service is not available. +// +export const ERROR_WMI_SERVER_UNAVAILABLE = 4208; + +// +// MessageId: ERROR_WMI_DP_FAILED +// +// MessageText: +// +// The WMI data provider failed to carry out the request. +// +export const ERROR_WMI_DP_FAILED = 4209; + +// +// MessageId: ERROR_WMI_INVALID_MOF +// +// MessageText: +// +// The WMI MOF information is not valid. +// +export const ERROR_WMI_INVALID_MOF = 4210; + +// +// MessageId: ERROR_WMI_INVALID_REGINFO +// +// MessageText: +// +// The WMI registration information is not valid. +// +export const ERROR_WMI_INVALID_REGINFO = 4211; + +// +// MessageId: ERROR_WMI_ALREADY_DISABLED +// +// MessageText: +// +// The WMI data block or event notification has already been disabled. +// +export const ERROR_WMI_ALREADY_DISABLED = 4212; + +// +// MessageId: ERROR_WMI_READ_ONLY +// +// MessageText: +// +// The WMI data item or data block is read only. +// +export const ERROR_WMI_READ_ONLY = 4213; + +// +// MessageId: ERROR_WMI_SET_FAILURE +// +// MessageText: +// +// The WMI data item or data block could not be changed. +// +export const ERROR_WMI_SET_FAILURE = 4214; + +////////////////////////////////////////// +// // +// NT Media Services (RSM) Error Codes // +// // +////////////////////////////////////////// +// +// MessageId: ERROR_INVALID_MEDIA +// +// MessageText: +// +// The media identifier does not represent a valid medium. +// +export const ERROR_INVALID_MEDIA = 4300; + +// +// MessageId: ERROR_INVALID_LIBRARY +// +// MessageText: +// +// The library identifier does not represent a valid library. +// +export const ERROR_INVALID_LIBRARY = 4301; + +// +// MessageId: ERROR_INVALID_MEDIA_POOL +// +// MessageText: +// +// The media pool identifier does not represent a valid media pool. +// +export const ERROR_INVALID_MEDIA_POOL = 4302; + +// +// MessageId: ERROR_DRIVE_MEDIA_MISMATCH +// +// MessageText: +// +// The drive and medium are not compatible or exist in different libraries. +// +export const ERROR_DRIVE_MEDIA_MISMATCH = 4303; + +// +// MessageId: ERROR_MEDIA_OFFLINE +// +// MessageText: +// +// The medium currently exists in an offline library and must be online to perform this operation. +// +export const ERROR_MEDIA_OFFLINE = 4304; + +// +// MessageId: ERROR_LIBRARY_OFFLINE +// +// MessageText: +// +// The operation cannot be performed on an offline library. +// +export const ERROR_LIBRARY_OFFLINE = 4305; + +// +// MessageId: ERROR_EMPTY +// +// MessageText: +// +// The library, drive, or media pool is empty. +// +export const ERROR_EMPTY = 4306; + +// +// MessageId: ERROR_NOT_EMPTY +// +// MessageText: +// +// The library, drive, or media pool must be empty to perform this operation. +// +export const ERROR_NOT_EMPTY = 4307; + +// +// MessageId: ERROR_MEDIA_UNAVAILABLE +// +// MessageText: +// +// No media is currently available in this media pool or library. +// +export const ERROR_MEDIA_UNAVAILABLE = 4308; + +// +// MessageId: ERROR_RESOURCE_DISABLED +// +// MessageText: +// +// A resource required for this operation is disabled. +// +export const ERROR_RESOURCE_DISABLED = 4309; + +// +// MessageId: ERROR_INVALID_CLEANER +// +// MessageText: +// +// The media identifier does not represent a valid cleaner. +// +export const ERROR_INVALID_CLEANER = 4310; + +// +// MessageId: ERROR_UNABLE_TO_CLEAN +// +// MessageText: +// +// The drive cannot be cleaned or does not support cleaning. +// +export const ERROR_UNABLE_TO_CLEAN = 4311; + +// +// MessageId: ERROR_OBJECT_NOT_FOUND +// +// MessageText: +// +// The object identifier does not represent a valid object. +// +export const ERROR_OBJECT_NOT_FOUND = 4312; + +// +// MessageId: ERROR_DATABASE_FAILURE +// +// MessageText: +// +// Unable to read from or write to the database. +// +export const ERROR_DATABASE_FAILURE = 4313; + +// +// MessageId: ERROR_DATABASE_FULL +// +// MessageText: +// +// The database is full. +// +export const ERROR_DATABASE_FULL = 4314; + +// +// MessageId: ERROR_MEDIA_INCOMPATIBLE +// +// MessageText: +// +// The medium is not compatible with the device or media pool. +// +export const ERROR_MEDIA_INCOMPATIBLE = 4315; + +// +// MessageId: ERROR_RESOURCE_NOT_PRESENT +// +// MessageText: +// +// The resource required for this operation does not exist. +// +export const ERROR_RESOURCE_NOT_PRESENT = 4316; + +// +// MessageId: ERROR_INVALID_OPERATION +// +// MessageText: +// +// The operation identifier is not valid. +// +export const ERROR_INVALID_OPERATION = 4317; + +// +// MessageId: ERROR_MEDIA_NOT_AVAILABLE +// +// MessageText: +// +// The media is not mounted or ready for use. +// +export const ERROR_MEDIA_NOT_AVAILABLE = 4318; + +// +// MessageId: ERROR_DEVICE_NOT_AVAILABLE +// +// MessageText: +// +// The device is not ready for use. +// +export const ERROR_DEVICE_NOT_AVAILABLE = 4319; + +// +// MessageId: ERROR_REQUEST_REFUSED +// +// MessageText: +// +// The operator or administrator has refused the request. +// +export const ERROR_REQUEST_REFUSED = 4320; + +// +// MessageId: ERROR_INVALID_DRIVE_OBJECT +// +// MessageText: +// +// The drive identifier does not represent a valid drive. +// +export const ERROR_INVALID_DRIVE_OBJECT = 4321; + +// +// MessageId: ERROR_LIBRARY_FULL +// +// MessageText: +// +// Library is full. No slot is available for use. +// +export const ERROR_LIBRARY_FULL = 4322; + +// +// MessageId: ERROR_MEDIUM_NOT_ACCESSIBLE +// +// MessageText: +// +// The transport cannot access the medium. +// +export const ERROR_MEDIUM_NOT_ACCESSIBLE = 4323; + +// +// MessageId: ERROR_UNABLE_TO_LOAD_MEDIUM +// +// MessageText: +// +// Unable to load the medium into the drive. +// +export const ERROR_UNABLE_TO_LOAD_MEDIUM = 4324; + +// +// MessageId: ERROR_UNABLE_TO_INVENTORY_DRIVE +// +// MessageText: +// +// Unable to retrieve the drive status. +// +export const ERROR_UNABLE_TO_INVENTORY_DRIVE = 4325; + +// +// MessageId: ERROR_UNABLE_TO_INVENTORY_SLOT +// +// MessageText: +// +// Unable to retrieve the slot status. +// +export const ERROR_UNABLE_TO_INVENTORY_SLOT = 4326; + +// +// MessageId: ERROR_UNABLE_TO_INVENTORY_TRANSPORT +// +// MessageText: +// +// Unable to retrieve status about the transport. +// +export const ERROR_UNABLE_TO_INVENTORY_TRANSPORT = 4327; + +// +// MessageId: ERROR_TRANSPORT_FULL +// +// MessageText: +// +// Cannot use the transport because it is already in use. +// +export const ERROR_TRANSPORT_FULL = 4328; + +// +// MessageId: ERROR_CONTROLLING_IEPORT +// +// MessageText: +// +// Unable to open or close the inject/eject port. +// +export const ERROR_CONTROLLING_IEPORT = 4329; + +// +// MessageId: ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA +// +// MessageText: +// +// Unable to eject the medium because it is in a drive. +// +export const ERROR_UNABLE_TO_EJECT_MOUNTED_MEDIA = 4330; + +// +// MessageId: ERROR_CLEANER_SLOT_SET +// +// MessageText: +// +// A cleaner slot is already reserved. +// +export const ERROR_CLEANER_SLOT_SET = 4331; + +// +// MessageId: ERROR_CLEANER_SLOT_NOT_SET +// +// MessageText: +// +// A cleaner slot is not reserved. +// +export const ERROR_CLEANER_SLOT_NOT_SET = 4332; + +// +// MessageId: ERROR_CLEANER_CARTRIDGE_SPENT +// +// MessageText: +// +// The cleaner cartridge has performed the maximum number of drive cleanings. +// +export const ERROR_CLEANER_CARTRIDGE_SPENT = 4333; + +// +// MessageId: ERROR_UNEXPECTED_OMID +// +// MessageText: +// +// Unexpected on-medium identifier. +// +export const ERROR_UNEXPECTED_OMID = 4334; + +// +// MessageId: ERROR_CANT_DELETE_LAST_ITEM +// +// MessageText: +// +// The last remaining item in this group or resource cannot be deleted. +// +export const ERROR_CANT_DELETE_LAST_ITEM = 4335; + +// +// MessageId: ERROR_MESSAGE_EXCEEDS_MAX_SIZE +// +// MessageText: +// +// The message provided exceeds the maximum size allowed for this parameter. +// +export const ERROR_MESSAGE_EXCEEDS_MAX_SIZE = 4336; + +// +// MessageId: ERROR_VOLUME_CONTAINS_SYS_FILES +// +// MessageText: +// +// The volume contains system or paging files. +// +export const ERROR_VOLUME_CONTAINS_SYS_FILES = 4337; + +// +// MessageId: ERROR_INDIGENOUS_TYPE +// +// MessageText: +// +// The media type cannot be removed from this library since at least one drive in the library reports it can support this media type. +// +export const ERROR_INDIGENOUS_TYPE = 4338; + +// +// MessageId: ERROR_NO_SUPPORTING_DRIVES +// +// MessageText: +// +// This offline media cannot be mounted on this system since no enabled drives are present which can be used. +// +export const ERROR_NO_SUPPORTING_DRIVES = 4339; + +// +// MessageId: ERROR_CLEANER_CARTRIDGE_INSTALLED +// +// MessageText: +// +// A cleaner cartridge is present in the tape library. +// +export const ERROR_CLEANER_CARTRIDGE_INSTALLED = 4340; + +// +// MessageId: ERROR_IEPORT_FULL +// +// MessageText: +// +// Cannot use the ieport because it is not empty. +// +export const ERROR_IEPORT_FULL = 4341; + +//////////////////////////////////////////// +// // +// NT Remote Storage Service Error Codes // +// // +//////////////////////////////////////////// +// +// MessageId: ERROR_FILE_OFFLINE +// +// MessageText: +// +// The remote storage service was not able to recall the file. +// +export const ERROR_FILE_OFFLINE = 4350; + +// +// MessageId: ERROR_REMOTE_STORAGE_NOT_ACTIVE +// +// MessageText: +// +// The remote storage service is not operational at this time. +// +export const ERROR_REMOTE_STORAGE_NOT_ACTIVE = 4351; + +// +// MessageId: ERROR_REMOTE_STORAGE_MEDIA_ERROR +// +// MessageText: +// +// The remote storage service encountered a media error. +// +export const ERROR_REMOTE_STORAGE_MEDIA_ERROR = 4352; + +//////////////////////////////////////////// +// // +// NT Reparse Points Error Codes // +// // +//////////////////////////////////////////// +// +// MessageId: ERROR_NOT_A_REPARSE_POINT +// +// MessageText: +// +// The file or directory is not a reparse point. +// +export const ERROR_NOT_A_REPARSE_POINT = 4390; + +// +// MessageId: ERROR_REPARSE_ATTRIBUTE_CONFLICT +// +// MessageText: +// +// The reparse point attribute cannot be set because it conflicts with an existing attribute. +// +export const ERROR_REPARSE_ATTRIBUTE_CONFLICT = 4391; + +// +// MessageId: ERROR_INVALID_REPARSE_DATA +// +// MessageText: +// +// The data present in the reparse point buffer is invalid. +// +export const ERROR_INVALID_REPARSE_DATA = 4392; + +// +// MessageId: ERROR_REPARSE_TAG_INVALID +// +// MessageText: +// +// The tag present in the reparse point buffer is invalid. +// +export const ERROR_REPARSE_TAG_INVALID = 4393; + +// +// MessageId: ERROR_REPARSE_TAG_MISMATCH +// +// MessageText: +// +// There is a mismatch between the tag specified in the request and the tag present in the reparse point. +// +// +export const ERROR_REPARSE_TAG_MISMATCH = 4394; + +//////////////////////////////////////////// +// // +// NT Single Instance Store Error Codes // +// // +//////////////////////////////////////////// +// +// MessageId: ERROR_VOLUME_NOT_SIS_ENABLED +// +// MessageText: +// +// Single Instance Storage is not available on this volume. +// +export const ERROR_VOLUME_NOT_SIS_ENABLED = 4500; + +//////////////////////////////////// +// // +// Cluster Error Codes // +// // +//////////////////////////////////// +// +// MessageId: ERROR_DEPENDENT_RESOURCE_EXISTS +// +// MessageText: +// +// The cluster resource cannot be moved to another group because other resources are dependent on it. +// +export const ERROR_DEPENDENT_RESOURCE_EXISTS = 5001; + +// +// MessageId: ERROR_DEPENDENCY_NOT_FOUND +// +// MessageText: +// +// The cluster resource dependency cannot be found. +// +export const ERROR_DEPENDENCY_NOT_FOUND = 5002; + +// +// MessageId: ERROR_DEPENDENCY_ALREADY_EXISTS +// +// MessageText: +// +// The cluster resource cannot be made dependent on the specified resource because it is already dependent. +// +export const ERROR_DEPENDENCY_ALREADY_EXISTS = 5003; + +// +// MessageId: ERROR_RESOURCE_NOT_ONLINE +// +// MessageText: +// +// The cluster resource is not online. +// +export const ERROR_RESOURCE_NOT_ONLINE = 5004; + +// +// MessageId: ERROR_HOST_NODE_NOT_AVAILABLE +// +// MessageText: +// +// A cluster node is not available for this operation. +// +export const ERROR_HOST_NODE_NOT_AVAILABLE = 5005; + +// +// MessageId: ERROR_RESOURCE_NOT_AVAILABLE +// +// MessageText: +// +// The cluster resource is not available. +// +export const ERROR_RESOURCE_NOT_AVAILABLE = 5006; + +// +// MessageId: ERROR_RESOURCE_NOT_FOUND +// +// MessageText: +// +// The cluster resource could not be found. +// +export const ERROR_RESOURCE_NOT_FOUND = 5007; + +// +// MessageId: ERROR_SHUTDOWN_CLUSTER +// +// MessageText: +// +// The cluster is being shut down. +// +export const ERROR_SHUTDOWN_CLUSTER = 5008; + +// +// MessageId: ERROR_CANT_EVICT_ACTIVE_NODE +// +// MessageText: +// +// A cluster node cannot be evicted from the cluster unless the node is down or it is the last node. +// +export const ERROR_CANT_EVICT_ACTIVE_NODE = 5009; + +// +// MessageId: ERROR_OBJECT_ALREADY_EXISTS +// +// MessageText: +// +// The object already exists. +// +export const ERROR_OBJECT_ALREADY_EXISTS = 5010; + +// +// MessageId: ERROR_OBJECT_IN_LIST +// +// MessageText: +// +// The object is already in the list. +// +export const ERROR_OBJECT_IN_LIST = 5011; + +// +// MessageId: ERROR_GROUP_NOT_AVAILABLE +// +// MessageText: +// +// The cluster group is not available for any new requests. +// +export const ERROR_GROUP_NOT_AVAILABLE = 5012; + +// +// MessageId: ERROR_GROUP_NOT_FOUND +// +// MessageText: +// +// The cluster group could not be found. +// +export const ERROR_GROUP_NOT_FOUND = 5013; + +// +// MessageId: ERROR_GROUP_NOT_ONLINE +// +// MessageText: +// +// The operation could not be completed because the cluster group is not online. +// +export const ERROR_GROUP_NOT_ONLINE = 5014; + +// +// MessageId: ERROR_HOST_NODE_NOT_RESOURCE_OWNER +// +// MessageText: +// +// The cluster node is not the owner of the resource. +// +export const ERROR_HOST_NODE_NOT_RESOURCE_OWNER = 5015; + +// +// MessageId: ERROR_HOST_NODE_NOT_GROUP_OWNER +// +// MessageText: +// +// The cluster node is not the owner of the group. +// +export const ERROR_HOST_NODE_NOT_GROUP_OWNER = 5016; + +// +// MessageId: ERROR_RESMON_CREATE_FAILED +// +// MessageText: +// +// The cluster resource could not be created in the specified resource monitor. +// +export const ERROR_RESMON_CREATE_FAILED = 5017; + +// +// MessageId: ERROR_RESMON_ONLINE_FAILED +// +// MessageText: +// +// The cluster resource could not be brought online by the resource monitor. +// +export const ERROR_RESMON_ONLINE_FAILED = 5018; + +// +// MessageId: ERROR_RESOURCE_ONLINE +// +// MessageText: +// +// The operation could not be completed because the cluster resource is online. +// +export const ERROR_RESOURCE_ONLINE = 5019; + +// +// MessageId: ERROR_QUORUM_RESOURCE +// +// MessageText: +// +// The cluster resource could not be deleted or brought offline because it is the quorum resource. +// +export const ERROR_QUORUM_RESOURCE = 5020; + +// +// MessageId: ERROR_NOT_QUORUM_CAPABLE +// +// MessageText: +// +// The cluster could not make the specified resource a quorum resource because it is not capable of being a quorum resource. +// +export const ERROR_NOT_QUORUM_CAPABLE = 5021; + +// +// MessageId: ERROR_CLUSTER_SHUTTING_DOWN +// +// MessageText: +// +// The cluster software is shutting down. +// +export const ERROR_CLUSTER_SHUTTING_DOWN = 5022; + +// +// MessageId: ERROR_INVALID_STATE +// +// MessageText: +// +// The group or resource is not in the correct state to perform the requested operation. +// +export const ERROR_INVALID_STATE = 5023; + +// +// MessageId: ERROR_RESOURCE_PROPERTIES_STORED +// +// MessageText: +// +// The properties were stored but not all changes will take effect until the next time the resource is brought online. +// +export const ERROR_RESOURCE_PROPERTIES_STORED = 5024; + +// +// MessageId: ERROR_NOT_QUORUM_CLASS +// +// MessageText: +// +// The cluster could not make the specified resource a quorum resource because it does not belong to a shared storage class. +// +export const ERROR_NOT_QUORUM_CLASS = 5025; + +// +// MessageId: ERROR_CORE_RESOURCE +// +// MessageText: +// +// The cluster resource could not be deleted since it is a core resource. +// +export const ERROR_CORE_RESOURCE = 5026; + +// +// MessageId: ERROR_QUORUM_RESOURCE_ONLINE_FAILED +// +// MessageText: +// +// The quorum resource failed to come online. +// +export const ERROR_QUORUM_RESOURCE_ONLINE_FAILED = 5027; + +// +// MessageId: ERROR_QUORUMLOG_OPEN_FAILED +// +// MessageText: +// +// The quorum log could not be created or mounted successfully. +// +export const ERROR_QUORUMLOG_OPEN_FAILED = 5028; + +// +// MessageId: ERROR_CLUSTERLOG_CORRUPT +// +// MessageText: +// +// The cluster log is corrupt. +// +export const ERROR_CLUSTERLOG_CORRUPT = 5029; + +// +// MessageId: ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE +// +// MessageText: +// +// The record could not be written to the cluster log since it exceeds the maximum size. +// +export const ERROR_CLUSTERLOG_RECORD_EXCEEDS_MAXSIZE = 5030; + +// +// MessageId: ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE +// +// MessageText: +// +// The cluster log exceeds its maximum size. +// +export const ERROR_CLUSTERLOG_EXCEEDS_MAXSIZE = 5031; + +// +// MessageId: ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND +// +// MessageText: +// +// No checkpoint record was found in the cluster log. +// +export const ERROR_CLUSTERLOG_CHKPOINT_NOT_FOUND = 5032; + +// +// MessageId: ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE +// +// MessageText: +// +// The minimum required disk space needed for logging is not available. +// +export const ERROR_CLUSTERLOG_NOT_ENOUGH_SPACE = 5033; + +// +// MessageId: ERROR_QUORUM_OWNER_ALIVE +// +// MessageText: +// +// The cluster node failed to take control of the quorum resource because the resource is owned by another active node. +// +export const ERROR_QUORUM_OWNER_ALIVE = 5034; + +// +// MessageId: ERROR_NETWORK_NOT_AVAILABLE +// +// MessageText: +// +// A cluster network is not available for this operation. +// +export const ERROR_NETWORK_NOT_AVAILABLE = 5035; + +// +// MessageId: ERROR_NODE_NOT_AVAILABLE +// +// MessageText: +// +// A cluster node is not available for this operation. +// +export const ERROR_NODE_NOT_AVAILABLE = 5036; + +// +// MessageId: ERROR_ALL_NODES_NOT_AVAILABLE +// +// MessageText: +// +// All cluster nodes must be running to perform this operation. +// +export const ERROR_ALL_NODES_NOT_AVAILABLE = 5037; + +// +// MessageId: ERROR_RESOURCE_FAILED +// +// MessageText: +// +// A cluster resource failed. +// +export const ERROR_RESOURCE_FAILED = 5038; + +// +// MessageId: ERROR_CLUSTER_INVALID_NODE +// +// MessageText: +// +// The cluster node is not valid. +// +export const ERROR_CLUSTER_INVALID_NODE = 5039; + +// +// MessageId: ERROR_CLUSTER_NODE_EXISTS +// +// MessageText: +// +// The cluster node already exists. +// +export const ERROR_CLUSTER_NODE_EXISTS = 5040; + +// +// MessageId: ERROR_CLUSTER_JOIN_IN_PROGRESS +// +// MessageText: +// +// A node is in the process of joining the cluster. +// +export const ERROR_CLUSTER_JOIN_IN_PROGRESS = 5041; + +// +// MessageId: ERROR_CLUSTER_NODE_NOT_FOUND +// +// MessageText: +// +// The cluster node was not found. +// +export const ERROR_CLUSTER_NODE_NOT_FOUND = 5042; + +// +// MessageId: ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND +// +// MessageText: +// +// The cluster local node information was not found. +// +export const ERROR_CLUSTER_LOCAL_NODE_NOT_FOUND = 5043; + +// +// MessageId: ERROR_CLUSTER_NETWORK_EXISTS +// +// MessageText: +// +// The cluster network already exists. +// +export const ERROR_CLUSTER_NETWORK_EXISTS = 5044; + +// +// MessageId: ERROR_CLUSTER_NETWORK_NOT_FOUND +// +// MessageText: +// +// The cluster network was not found. +// +export const ERROR_CLUSTER_NETWORK_NOT_FOUND = 5045; + +// +// MessageId: ERROR_CLUSTER_NETINTERFACE_EXISTS +// +// MessageText: +// +// The cluster network interface already exists. +// +export const ERROR_CLUSTER_NETINTERFACE_EXISTS = 5046; + +// +// MessageId: ERROR_CLUSTER_NETINTERFACE_NOT_FOUND +// +// MessageText: +// +// The cluster network interface was not found. +// +export const ERROR_CLUSTER_NETINTERFACE_NOT_FOUND = 5047; + +// +// MessageId: ERROR_CLUSTER_INVALID_REQUEST +// +// MessageText: +// +// The cluster request is not valid for this object. +// +export const ERROR_CLUSTER_INVALID_REQUEST = 5048; + +// +// MessageId: ERROR_CLUSTER_INVALID_NETWORK_PROVIDER +// +// MessageText: +// +// The cluster network provider is not valid. +// +export const ERROR_CLUSTER_INVALID_NETWORK_PROVIDER = 5049; + +// +// MessageId: ERROR_CLUSTER_NODE_DOWN +// +// MessageText: +// +// The cluster node is down. +// +export const ERROR_CLUSTER_NODE_DOWN = 5050; + +// +// MessageId: ERROR_CLUSTER_NODE_UNREACHABLE +// +// MessageText: +// +// The cluster node is not reachable. +// +export const ERROR_CLUSTER_NODE_UNREACHABLE = 5051; + +// +// MessageId: ERROR_CLUSTER_NODE_NOT_MEMBER +// +// MessageText: +// +// The cluster node is not a member of the cluster. +// +export const ERROR_CLUSTER_NODE_NOT_MEMBER = 5052; + +// +// MessageId: ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS +// +// MessageText: +// +// A cluster join operation is not in progress. +// +export const ERROR_CLUSTER_JOIN_NOT_IN_PROGRESS = 5053; + +// +// MessageId: ERROR_CLUSTER_INVALID_NETWORK +// +// MessageText: +// +// The cluster network is not valid. +// +export const ERROR_CLUSTER_INVALID_NETWORK = 5054; + +// +// MessageId: ERROR_CLUSTER_NODE_UP +// +// MessageText: +// +// The cluster node is up. +// +export const ERROR_CLUSTER_NODE_UP = 5056; + +// +// MessageId: ERROR_CLUSTER_IPADDR_IN_USE +// +// MessageText: +// +// The cluster IP address is already in use. +// +export const ERROR_CLUSTER_IPADDR_IN_USE = 5057; + +// +// MessageId: ERROR_CLUSTER_NODE_NOT_PAUSED +// +// MessageText: +// +// The cluster node is not paused. +// +export const ERROR_CLUSTER_NODE_NOT_PAUSED = 5058; + +// +// MessageId: ERROR_CLUSTER_NO_SECURITY_CONTEXT +// +// MessageText: +// +// No cluster security context is available. +// +export const ERROR_CLUSTER_NO_SECURITY_CONTEXT = 5059; + +// +// MessageId: ERROR_CLUSTER_NETWORK_NOT_INTERNAL +// +// MessageText: +// +// The cluster network is not configured for internal cluster communication. +// +export const ERROR_CLUSTER_NETWORK_NOT_INTERNAL = 5060; + +// +// MessageId: ERROR_CLUSTER_NODE_ALREADY_UP +// +// MessageText: +// +// The cluster node is already up. +// +export const ERROR_CLUSTER_NODE_ALREADY_UP = 5061; + +// +// MessageId: ERROR_CLUSTER_NODE_ALREADY_DOWN +// +// MessageText: +// +// The cluster node is already down. +// +export const ERROR_CLUSTER_NODE_ALREADY_DOWN = 5062; + +// +// MessageId: ERROR_CLUSTER_NETWORK_ALREADY_ONLINE +// +// MessageText: +// +// The cluster network is already online. +// +export const ERROR_CLUSTER_NETWORK_ALREADY_ONLINE = 5063; + +// +// MessageId: ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE +// +// MessageText: +// +// The cluster network is already offline. +// +export const ERROR_CLUSTER_NETWORK_ALREADY_OFFLINE = 5064; + +// +// MessageId: ERROR_CLUSTER_NODE_ALREADY_MEMBER +// +// MessageText: +// +// The cluster node is already a member of the cluster. +// +export const ERROR_CLUSTER_NODE_ALREADY_MEMBER = 5065; + +// +// MessageId: ERROR_CLUSTER_LAST_INTERNAL_NETWORK +// +// MessageText: +// +// The cluster network is the only one configured for internal cluster communication between two or more active cluster nodes. The internal communication capability cannot be removed from the network. +// +export const ERROR_CLUSTER_LAST_INTERNAL_NETWORK = 5066; + +// +// MessageId: ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS +// +// MessageText: +// +// One or more cluster resources depend on the network to provide service to clients. The client access capability cannot be removed from the network. +// +export const ERROR_CLUSTER_NETWORK_HAS_DEPENDENTS = 5067; + +// +// MessageId: ERROR_INVALID_OPERATION_ON_QUORUM +// +// MessageText: +// +// This operation cannot be performed on the cluster resource as it the quorum resource. You may not bring the quorum resource offline or modify its possible owners list. +// +export const ERROR_INVALID_OPERATION_ON_QUORUM = 5068; + +// +// MessageId: ERROR_DEPENDENCY_NOT_ALLOWED +// +// MessageText: +// +// The cluster quorum resource is not allowed to have any dependencies. +// +export const ERROR_DEPENDENCY_NOT_ALLOWED = 5069; + +// +// MessageId: ERROR_CLUSTER_NODE_PAUSED +// +// MessageText: +// +// The cluster node is paused. +// +export const ERROR_CLUSTER_NODE_PAUSED = 5070; + +// +// MessageId: ERROR_NODE_CANT_HOST_RESOURCE +// +// MessageText: +// +// The cluster resource cannot be brought online. The owner node cannot run this resource. +// +export const ERROR_NODE_CANT_HOST_RESOURCE = 5071; + +// +// MessageId: ERROR_CLUSTER_NODE_NOT_READY +// +// MessageText: +// +// The cluster node is not ready to perform the requested operation. +// +export const ERROR_CLUSTER_NODE_NOT_READY = 5072; + +// +// MessageId: ERROR_CLUSTER_NODE_SHUTTING_DOWN +// +// MessageText: +// +// The cluster node is shutting down. +// +export const ERROR_CLUSTER_NODE_SHUTTING_DOWN = 5073; + +// +// MessageId: ERROR_CLUSTER_JOIN_ABORTED +// +// MessageText: +// +// The cluster join operation was aborted. +// +export const ERROR_CLUSTER_JOIN_ABORTED = 5074; + +// +// MessageId: ERROR_CLUSTER_INCOMPATIBLE_VERSIONS +// +// MessageText: +// +// The cluster join operation failed due to incompatible software versions between the joining node and its sponsor. +// +export const ERROR_CLUSTER_INCOMPATIBLE_VERSIONS = 5075; + +// +// MessageId: ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED +// +// MessageText: +// +// This resource cannot be created because the cluster has reached the limit on the number of resources it can monitor. +// +export const ERROR_CLUSTER_MAXNUM_OF_RESOURCES_EXCEEDED = 5076; + +// +// MessageId: ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED +// +// MessageText: +// +// The system configuration changed during the cluster join or form operation. The join or form operation was aborted. +// +export const ERROR_CLUSTER_SYSTEM_CONFIG_CHANGED = 5077; + +// +// MessageId: ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND +// +// MessageText: +// +// The specified resource type was not found. +// +export const ERROR_CLUSTER_RESOURCE_TYPE_NOT_FOUND = 5078; + +// +// MessageId: ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED +// +// MessageText: +// +// The specified node does not support a resource of this type. This may be due to version inconsistencies or due to the absence of the resource DLL on this node. +// +export const ERROR_CLUSTER_RESTYPE_NOT_SUPPORTED = 5079; + +// +// MessageId: ERROR_CLUSTER_RESNAME_NOT_FOUND +// +// MessageText: +// +// The specified resource name is not supported by this resource DLL. This may be due to a bad (or changed) name supplied to the resource DLL. +// +export const ERROR_CLUSTER_RESNAME_NOT_FOUND = 5080; + +// +// MessageId: ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED +// +// MessageText: +// +// No authentication package could be registered with the RPC server. +// +export const ERROR_CLUSTER_NO_RPC_PACKAGES_REGISTERED = 5081; + +// +// MessageId: ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST +// +// MessageText: +// +// You cannot bring the group online because the owner of the group is not in the preferred list for the group. To change the owner node for the group, move the group. +// +export const ERROR_CLUSTER_OWNER_NOT_IN_PREFLIST = 5082; + +// +// MessageId: ERROR_CLUSTER_DATABASE_SEQMISMATCH +// +// MessageText: +// +// The join operation failed because the cluster database sequence number has changed or is incompatible with the locker node. This may happen during a join operation if the cluster database was changing during the join. +// +export const ERROR_CLUSTER_DATABASE_SEQMISMATCH = 5083; + +// +// MessageId: ERROR_RESMON_INVALID_STATE +// +// MessageText: +// +// The resource monitor will not allow the fail operation to be performed while the resource is in its current state. This may happen if the resource is in a pending state. +// +export const ERROR_RESMON_INVALID_STATE = 5084; + +// +// MessageId: ERROR_CLUSTER_GUM_NOT_LOCKER +// +// MessageText: +// +// A non locker code got a request to reserve the lock for making global updates. +// +export const ERROR_CLUSTER_GUM_NOT_LOCKER = 5085; + +// +// MessageId: ERROR_QUORUM_DISK_NOT_FOUND +// +// MessageText: +// +// The quorum disk could not be located by the cluster service. +// +export const ERROR_QUORUM_DISK_NOT_FOUND = 5086; + +// +// MessageId: ERROR_DATABASE_BACKUP_CORRUPT +// +// MessageText: +// +// The backed up cluster database is possibly corrupt. +// +export const ERROR_DATABASE_BACKUP_CORRUPT = 5087; + +// +// MessageId: ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT +// +// MessageText: +// +// A DFS root already exists in this cluster node. +// +export const ERROR_CLUSTER_NODE_ALREADY_HAS_DFS_ROOT = 5088; + +// +// MessageId: ERROR_RESOURCE_PROPERTY_UNCHANGEABLE +// +// MessageText: +// +// An attempt to modify a resource property failed because it conflicts with another existing property. +// +export const ERROR_RESOURCE_PROPERTY_UNCHANGEABLE = 5089; + +/* + Codes from 4300 through 5889 overlap with codes in ds\published\inc\apperr2.w. + Do not add any more error codes in that range. +*/ +// +// MessageId: ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE +// +// MessageText: +// +// An operation was attempted that is incompatible with the current membership state of the node. +// +export const ERROR_CLUSTER_MEMBERSHIP_INVALID_STATE = 5890; + +// +// MessageId: ERROR_CLUSTER_QUORUMLOG_NOT_FOUND +// +// MessageText: +// +// The quorum resource does not contain the quorum log. +// +export const ERROR_CLUSTER_QUORUMLOG_NOT_FOUND = 5891; + +// +// MessageId: ERROR_CLUSTER_MEMBERSHIP_HALT +// +// MessageText: +// +// The membership engine requested shutdown of the cluster service on this node. +// +export const ERROR_CLUSTER_MEMBERSHIP_HALT = 5892; + +// +// MessageId: ERROR_CLUSTER_INSTANCE_ID_MISMATCH +// +// MessageText: +// +// The join operation failed because the cluster instance ID of the joining node does not match the cluster instance ID of the sponsor node. +// +export const ERROR_CLUSTER_INSTANCE_ID_MISMATCH = 5893; + +// +// MessageId: ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP +// +// MessageText: +// +// A matching network for the specified IP address could not be found. Please also specify a subnet mask and a cluster network. +// +export const ERROR_CLUSTER_NETWORK_NOT_FOUND_FOR_IP = 5894; + +// +// MessageId: ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH +// +// MessageText: +// +// The actual data type of the property did not match the expected data type of the property. +// +export const ERROR_CLUSTER_PROPERTY_DATA_TYPE_MISMATCH = 5895; + +// +// MessageId: ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP +// +// MessageText: +// +// The cluster node was evicted from the cluster successfully, but the node was not cleaned up. Extended status information explaining why the node was not cleaned up is available. +// +export const ERROR_CLUSTER_EVICT_WITHOUT_CLEANUP = 5896; + +// +// MessageId: ERROR_CLUSTER_PARAMETER_MISMATCH +// +// MessageText: +// +// Two or more parameter values specified for a resource's properties are in conflict. +// +export const ERROR_CLUSTER_PARAMETER_MISMATCH = 5897; + +// +// MessageId: ERROR_NODE_CANNOT_BE_CLUSTERED +// +// MessageText: +// +// This computer cannot be made a member of a cluster. +// +export const ERROR_NODE_CANNOT_BE_CLUSTERED = 5898; + +// +// MessageId: ERROR_CLUSTER_WRONG_OS_VERSION +// +// MessageText: +// +// This computer cannot be made a member of a cluster because it does not have the correct version of Windows installed. +// +export const ERROR_CLUSTER_WRONG_OS_VERSION = 5899; + +// +// MessageId: ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME +// +// MessageText: +// +// A cluster cannot be created with the specified cluster name because that cluster name is already in use. Specify a different name for the cluster. +// +export const ERROR_CLUSTER_CANT_CREATE_DUP_CLUSTER_NAME = 5900; + +// +// MessageId: ERROR_CLUSCFG_ALREADY_COMMITTED +// +// MessageText: +// +// The cluster configuration action has already been committed. +// +export const ERROR_CLUSCFG_ALREADY_COMMITTED = 5901; + +// +// MessageId: ERROR_CLUSCFG_ROLLBACK_FAILED +// +// MessageText: +// +// The cluster configuration action could not be rolled back. +// +export const ERROR_CLUSCFG_ROLLBACK_FAILED = 5902; + +// +// MessageId: ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT +// +// MessageText: +// +// The drive letter assigned to a system disk on one node conflicted with the drive letter assigned to a disk on another node. +// +export const ERROR_CLUSCFG_SYSTEM_DISK_DRIVE_LETTER_CONFLICT = 5903; + +// +// MessageId: ERROR_CLUSTER_OLD_VERSION +// +// MessageText: +// +// One or more nodes in the cluster are running a version of Windows that does not support this operation. +// +export const ERROR_CLUSTER_OLD_VERSION = 5904; + +// +// MessageId: ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME +// +// MessageText: +// +// The name of the corresponding computer account doesn't match the Network Name for this resource. +// +export const ERROR_CLUSTER_MISMATCHED_COMPUTER_ACCT_NAME = 5905; + +//////////////////////////////////// +// // +// EFS Error Codes // +// // +//////////////////////////////////// +// +// MessageId: ERROR_ENCRYPTION_FAILED +// +// MessageText: +// +// The specified file could not be encrypted. +// +export const ERROR_ENCRYPTION_FAILED = 6000; + +// +// MessageId: ERROR_DECRYPTION_FAILED +// +// MessageText: +// +// The specified file could not be decrypted. +// +export const ERROR_DECRYPTION_FAILED = 6001; + +// +// MessageId: ERROR_FILE_ENCRYPTED +// +// MessageText: +// +// The specified file is encrypted and the user does not have the ability to decrypt it. +// +export const ERROR_FILE_ENCRYPTED = 6002; + +// +// MessageId: ERROR_NO_RECOVERY_POLICY +// +// MessageText: +// +// There is no valid encryption recovery policy configured for this system. +// +export const ERROR_NO_RECOVERY_POLICY = 6003; + +// +// MessageId: ERROR_NO_EFS +// +// MessageText: +// +// The required encryption driver is not loaded for this system. +// +export const ERROR_NO_EFS = 6004; + +// +// MessageId: ERROR_WRONG_EFS +// +// MessageText: +// +// The file was encrypted with a different encryption driver than is currently loaded. +// +export const ERROR_WRONG_EFS = 6005; + +// +// MessageId: ERROR_NO_USER_KEYS +// +// MessageText: +// +// There are no EFS keys defined for the user. +// +export const ERROR_NO_USER_KEYS = 6006; + +// +// MessageId: ERROR_FILE_NOT_ENCRYPTED +// +// MessageText: +// +// The specified file is not encrypted. +// +export const ERROR_FILE_NOT_ENCRYPTED = 6007; + +// +// MessageId: ERROR_NOT_EXPORT_FORMAT +// +// MessageText: +// +// The specified file is not in the defined EFS export format. +// +export const ERROR_NOT_EXPORT_FORMAT = 6008; + +// +// MessageId: ERROR_FILE_READ_ONLY +// +// MessageText: +// +// The specified file is read only. +// +export const ERROR_FILE_READ_ONLY = 6009; + +// +// MessageId: ERROR_DIR_EFS_DISALLOWED +// +// MessageText: +// +// The directory has been disabled for encryption. +// +export const ERROR_DIR_EFS_DISALLOWED = 6010; + +// +// MessageId: ERROR_EFS_SERVER_NOT_TRUSTED +// +// MessageText: +// +// The server is not trusted for remote encryption operation. +// +export const ERROR_EFS_SERVER_NOT_TRUSTED = 6011; + +// +// MessageId: ERROR_BAD_RECOVERY_POLICY +// +// MessageText: +// +// Recovery policy configured for this system contains invalid recovery certificate. +// +export const ERROR_BAD_RECOVERY_POLICY = 6012; + +// +// MessageId: ERROR_EFS_ALG_BLOB_TOO_BIG +// +// MessageText: +// +// The encryption algorithm used on the source file needs a bigger key buffer than the one on the destination file. +// +export const ERROR_EFS_ALG_BLOB_TOO_BIG = 6013; + +// +// MessageId: ERROR_VOLUME_NOT_SUPPORT_EFS +// +// MessageText: +// +// The disk partition does not support file encryption. +// +export const ERROR_VOLUME_NOT_SUPPORT_EFS = 6014; + +// +// MessageId: ERROR_EFS_DISABLED +// +// MessageText: +// +// This machine is disabled for file encryption. +// +export const ERROR_EFS_DISABLED = 6015; + +// +// MessageId: ERROR_EFS_VERSION_NOT_SUPPORT +// +// MessageText: +// +// A newer system is required to decrypt this encrypted file. +// +export const ERROR_EFS_VERSION_NOT_SUPPORT = 6016; + +// This message number is for historical purposes and cannot be changed or re-used. +// +// MessageId: ERROR_NO_BROWSER_SERVERS_FOUND +// +// MessageText: +// +// The list of servers for this workgroup is not currently available +// +export const ERROR_NO_BROWSER_SERVERS_FOUND = 6118; + +////////////////////////////////////////////////////////////////// +// // +// Task Scheduler Error Codes that NET START must understand // +// // +////////////////////////////////////////////////////////////////// +// +// MessageId: SCHED_E_SERVICE_NOT_LOCALSYSTEM +// +// MessageText: +// +// The Task Scheduler service must be configured to run in the System account to function properly. Individual tasks may be configured to run in other accounts. +// +export const SCHED_E_SERVICE_NOT_LOCALSYSTEM = 6200; + +//////////////////////////////////// +// // +// Terminal Server Error Codes // +// // +//////////////////////////////////// +// +// MessageId: ERROR_CTX_WINSTATION_NAME_INVALID +// +// MessageText: +// +// The specified session name is invalid. +// +export const ERROR_CTX_WINSTATION_NAME_INVALID = 7001; + +// +// MessageId: ERROR_CTX_INVALID_PD +// +// MessageText: +// +// The specified protocol driver is invalid. +// +export const ERROR_CTX_INVALID_PD = 7002; + +// +// MessageId: ERROR_CTX_PD_NOT_FOUND +// +// MessageText: +// +// The specified protocol driver was not found in the system path. +// +export const ERROR_CTX_PD_NOT_FOUND = 7003; + +// +// MessageId: ERROR_CTX_WD_NOT_FOUND +// +// MessageText: +// +// The specified terminal connection driver was not found in the system path. +// +export const ERROR_CTX_WD_NOT_FOUND = 7004; + +// +// MessageId: ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY +// +// MessageText: +// +// A registry key for event logging could not be created for this session. +// +export const ERROR_CTX_CANNOT_MAKE_EVENTLOG_ENTRY = 7005; + +// +// MessageId: ERROR_CTX_SERVICE_NAME_COLLISION +// +// MessageText: +// +// A service with the same name already exists on the system. +// +export const ERROR_CTX_SERVICE_NAME_COLLISION = 7006; + +// +// MessageId: ERROR_CTX_CLOSE_PENDING +// +// MessageText: +// +// A close operation is pending on the session. +// +export const ERROR_CTX_CLOSE_PENDING = 7007; + +// +// MessageId: ERROR_CTX_NO_OUTBUF +// +// MessageText: +// +// There are no free output buffers available. +// +export const ERROR_CTX_NO_OUTBUF = 7008; + +// +// MessageId: ERROR_CTX_MODEM_INF_NOT_FOUND +// +// MessageText: +// +// The MODEM.INF file was not found. +// +export const ERROR_CTX_MODEM_INF_NOT_FOUND = 7009; + +// +// MessageId: ERROR_CTX_INVALID_MODEMNAME +// +// MessageText: +// +// The modem name was not found in MODEM.INF. +// +export const ERROR_CTX_INVALID_MODEMNAME = 7010; + +// +// MessageId: ERROR_CTX_MODEM_RESPONSE_ERROR +// +// MessageText: +// +// The modem did not accept the command sent to it. Verify that the configured modem name matches the attached modem. +// +export const ERROR_CTX_MODEM_RESPONSE_ERROR = 7011; + +// +// MessageId: ERROR_CTX_MODEM_RESPONSE_TIMEOUT +// +// MessageText: +// +// The modem did not respond to the command sent to it. Verify that the modem is properly cabled and powered on. +// +export const ERROR_CTX_MODEM_RESPONSE_TIMEOUT = 7012; + +// +// MessageId: ERROR_CTX_MODEM_RESPONSE_NO_CARRIER +// +// MessageText: +// +// Carrier detect has failed or carrier has been dropped due to disconnect. +// +export const ERROR_CTX_MODEM_RESPONSE_NO_CARRIER = 7013; + +// +// MessageId: ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE +// +// MessageText: +// +// Dial tone not detected within the required time. Verify that the phone cable is properly attached and functional. +// +export const ERROR_CTX_MODEM_RESPONSE_NO_DIALTONE = 7014; + +// +// MessageId: ERROR_CTX_MODEM_RESPONSE_BUSY +// +// MessageText: +// +// Busy signal detected at remote site on callback. +// +export const ERROR_CTX_MODEM_RESPONSE_BUSY = 7015; + +// +// MessageId: ERROR_CTX_MODEM_RESPONSE_VOICE +// +// MessageText: +// +// Voice detected at remote site on callback. +// +export const ERROR_CTX_MODEM_RESPONSE_VOICE = 7016; + +// +// MessageId: ERROR_CTX_TD_ERROR +// +// MessageText: +// +// Transport driver error +// +export const ERROR_CTX_TD_ERROR = 7017; + +// +// MessageId: ERROR_CTX_WINSTATION_NOT_FOUND +// +// MessageText: +// +// The specified session cannot be found. +// +export const ERROR_CTX_WINSTATION_NOT_FOUND = 7022; + +// +// MessageId: ERROR_CTX_WINSTATION_ALREADY_EXISTS +// +// MessageText: +// +// The specified session name is already in use. +// +export const ERROR_CTX_WINSTATION_ALREADY_EXISTS = 7023; + +// +// MessageId: ERROR_CTX_WINSTATION_BUSY +// +// MessageText: +// +// The requested operation cannot be completed because the terminal connection is currently busy processing a connect, disconnect, reset, or delete operation. +// +export const ERROR_CTX_WINSTATION_BUSY = 7024; + +// +// MessageId: ERROR_CTX_BAD_VIDEO_MODE +// +// MessageText: +// +// An attempt has been made to connect to a session whose video mode is not supported by the current client. +// +export const ERROR_CTX_BAD_VIDEO_MODE = 7025; + +// +// MessageId: ERROR_CTX_GRAPHICS_INVALID +// +// MessageText: +// +// The application attempted to enable DOS graphics mode. +// DOS graphics mode is not supported. +// +export const ERROR_CTX_GRAPHICS_INVALID = 7035; + +// +// MessageId: ERROR_CTX_LOGON_DISABLED +// +// MessageText: +// +// Your interactive logon privilege has been disabled. +// Please contact your administrator. +// +export const ERROR_CTX_LOGON_DISABLED = 7037; + +// +// MessageId: ERROR_CTX_NOT_CONSOLE +// +// MessageText: +// +// The requested operation can be performed only on the system console. +// This is most often the result of a driver or system DLL requiring direct console access. +// +export const ERROR_CTX_NOT_CONSOLE = 7038; + +// +// MessageId: ERROR_CTX_CLIENT_QUERY_TIMEOUT +// +// MessageText: +// +// The client failed to respond to the server connect message. +// +export const ERROR_CTX_CLIENT_QUERY_TIMEOUT = 7040; + +// +// MessageId: ERROR_CTX_CONSOLE_DISCONNECT +// +// MessageText: +// +// Disconnecting the console session is not supported. +// +export const ERROR_CTX_CONSOLE_DISCONNECT = 7041; + +// +// MessageId: ERROR_CTX_CONSOLE_CONNECT +// +// MessageText: +// +// Reconnecting a disconnected session to the console is not supported. +// +export const ERROR_CTX_CONSOLE_CONNECT = 7042; + +// +// MessageId: ERROR_CTX_SHADOW_DENIED +// +// MessageText: +// +// The request to control another session remotely was denied. +// +export const ERROR_CTX_SHADOW_DENIED = 7044; + +// +// MessageId: ERROR_CTX_WINSTATION_ACCESS_DENIED +// +// MessageText: +// +// The requested session access is denied. +// +export const ERROR_CTX_WINSTATION_ACCESS_DENIED = 7045; + +// +// MessageId: ERROR_CTX_INVALID_WD +// +// MessageText: +// +// The specified terminal connection driver is invalid. +// +export const ERROR_CTX_INVALID_WD = 7049; + +// +// MessageId: ERROR_CTX_SHADOW_INVALID +// +// MessageText: +// +// The requested session cannot be controlled remotely. +// This may be because the session is disconnected or does not currently have a user logged on. +// +export const ERROR_CTX_SHADOW_INVALID = 7050; + +// +// MessageId: ERROR_CTX_SHADOW_DISABLED +// +// MessageText: +// +// The requested session is not configured to allow remote control. +// +export const ERROR_CTX_SHADOW_DISABLED = 7051; + +// +// MessageId: ERROR_CTX_CLIENT_LICENSE_IN_USE +// +// MessageText: +// +// Your request to connect to this Terminal Server has been rejected. Your Terminal Server client license number is currently being used by another user. +// Please call your system administrator to obtain a unique license number. +// +export const ERROR_CTX_CLIENT_LICENSE_IN_USE = 7052; + +// +// MessageId: ERROR_CTX_CLIENT_LICENSE_NOT_SET +// +// MessageText: +// +// Your request to connect to this Terminal Server has been rejected. Your Terminal Server client license number has not been entered for this copy of the Terminal Server client. +// Please contact your system administrator. +// +export const ERROR_CTX_CLIENT_LICENSE_NOT_SET = 7053; + +// +// MessageId: ERROR_CTX_LICENSE_NOT_AVAILABLE +// +// MessageText: +// +// The system has reached its licensed logon limit. +// Please try again later. +// +export const ERROR_CTX_LICENSE_NOT_AVAILABLE = 7054; + +// +// MessageId: ERROR_CTX_LICENSE_CLIENT_INVALID +// +// MessageText: +// +// The client you are using is not licensed to use this system. Your logon request is denied. +// +export const ERROR_CTX_LICENSE_CLIENT_INVALID = 7055; + +// +// MessageId: ERROR_CTX_LICENSE_EXPIRED +// +// MessageText: +// +// The system license has expired. Your logon request is denied. +// +export const ERROR_CTX_LICENSE_EXPIRED = 7056; + +// +// MessageId: ERROR_CTX_SHADOW_NOT_RUNNING +// +// MessageText: +// +// Remote control could not be terminated because the specified session is not currently being remotely controlled. +// +export const ERROR_CTX_SHADOW_NOT_RUNNING = 7057; + +// +// MessageId: ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE +// +// MessageText: +// +// The remote control of the console was terminated because the display mode was changed. Changing the display mode in a remote control session is not supported. +// +export const ERROR_CTX_SHADOW_ENDED_BY_MODE_CHANGE = 7058; + +// +// MessageId: ERROR_ACTIVATION_COUNT_EXCEEDED +// +// MessageText: +// +// Activation has already been reset the maximum number of times for this installation. Your activation timer will not be cleared. +// +export const ERROR_ACTIVATION_COUNT_EXCEEDED = 7059; + +/////////////////////////////////////////////////// +// / +// Traffic Control Error Codes / +// / +// 7500 to 7999 / +// / +// defined in: tcerror.h / +/////////////////////////////////////////////////// +/////////////////////////////////////////////////// +// / +// Active Directory Error Codes / +// / +// 8000 to 8999 / +/////////////////////////////////////////////////// +// ***************** +// FACILITY_FILE_REPLICATION_SERVICE +// ***************** +// +// MessageId: FRS_ERR_INVALID_API_SEQUENCE +// +// MessageText: +// +// The file replication service API was called incorrectly. +// +export const FRS_ERR_INVALID_API_SEQUENCE = 8001; + +// +// MessageId: FRS_ERR_STARTING_SERVICE +// +// MessageText: +// +// The file replication service cannot be started. +// +export const FRS_ERR_STARTING_SERVICE = 8002; + +// +// MessageId: FRS_ERR_STOPPING_SERVICE +// +// MessageText: +// +// The file replication service cannot be stopped. +// +export const FRS_ERR_STOPPING_SERVICE = 8003; + +// +// MessageId: FRS_ERR_INTERNAL_API +// +// MessageText: +// +// The file replication service API terminated the request. +// The event log may have more information. +// +export const FRS_ERR_INTERNAL_API = 8004; + +// +// MessageId: FRS_ERR_INTERNAL +// +// MessageText: +// +// The file replication service terminated the request. +// The event log may have more information. +// +export const FRS_ERR_INTERNAL = 8005; + +// +// MessageId: FRS_ERR_SERVICE_COMM +// +// MessageText: +// +// The file replication service cannot be contacted. +// The event log may have more information. +// +export const FRS_ERR_SERVICE_COMM = 8006; + +// +// MessageId: FRS_ERR_INSUFFICIENT_PRIV +// +// MessageText: +// +// The file replication service cannot satisfy the request because the user has insufficient privileges. +// The event log may have more information. +// +export const FRS_ERR_INSUFFICIENT_PRIV = 8007; + +// +// MessageId: FRS_ERR_AUTHENTICATION +// +// MessageText: +// +// The file replication service cannot satisfy the request because authenticated RPC is not available. +// The event log may have more information. +// +export const FRS_ERR_AUTHENTICATION = 8008; + +// +// MessageId: FRS_ERR_PARENT_INSUFFICIENT_PRIV +// +// MessageText: +// +// The file replication service cannot satisfy the request because the user has insufficient privileges on the domain controller. +// The event log may have more information. +// +export const FRS_ERR_PARENT_INSUFFICIENT_PRIV = 8009; + +// +// MessageId: FRS_ERR_PARENT_AUTHENTICATION +// +// MessageText: +// +// The file replication service cannot satisfy the request because authenticated RPC is not available on the domain controller. +// The event log may have more information. +// +export const FRS_ERR_PARENT_AUTHENTICATION = 8010; + +// +// MessageId: FRS_ERR_CHILD_TO_PARENT_COMM +// +// MessageText: +// +// The file replication service cannot communicate with the file replication service on the domain controller. +// The event log may have more information. +// +export const FRS_ERR_CHILD_TO_PARENT_COMM = 8011; + +// +// MessageId: FRS_ERR_PARENT_TO_CHILD_COMM +// +// MessageText: +// +// The file replication service on the domain controller cannot communicate with the file replication service on this computer. +// The event log may have more information. +// +export const FRS_ERR_PARENT_TO_CHILD_COMM = 8012; + +// +// MessageId: FRS_ERR_SYSVOL_POPULATE +// +// MessageText: +// +// The file replication service cannot populate the system volume because of an internal error. +// The event log may have more information. +// +export const FRS_ERR_SYSVOL_POPULATE = 8013; + +// +// MessageId: FRS_ERR_SYSVOL_POPULATE_TIMEOUT +// +// MessageText: +// +// The file replication service cannot populate the system volume because of an internal timeout. +// The event log may have more information. +// +export const FRS_ERR_SYSVOL_POPULATE_TIMEOUT = 8014; + +// +// MessageId: FRS_ERR_SYSVOL_IS_BUSY +// +// MessageText: +// +// The file replication service cannot process the request. The system volume is busy with a previous request. +// +export const FRS_ERR_SYSVOL_IS_BUSY = 8015; + +// +// MessageId: FRS_ERR_SYSVOL_DEMOTE +// +// MessageText: +// +// The file replication service cannot stop replicating the system volume because of an internal error. +// The event log may have more information. +// +export const FRS_ERR_SYSVOL_DEMOTE = 8016; + +// +// MessageId: FRS_ERR_INVALID_SERVICE_PARAMETER +// +// MessageText: +// +// The file replication service detected an invalid parameter. +// +export const FRS_ERR_INVALID_SERVICE_PARAMETER = 8017; + +// ***************** +// FACILITY DIRECTORY SERVICE +// ***************** +// +// MessageId: ERROR_DS_NOT_INSTALLED +// +// MessageText: +// +// An error occurred while installing the directory service. For more information, see the event log. +// +export const ERROR_DS_NOT_INSTALLED = 8200; + +// +// MessageId: ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY +// +// MessageText: +// +// The directory service evaluated group memberships locally. +// +export const ERROR_DS_MEMBERSHIP_EVALUATED_LOCALLY = 8201; + +// +// MessageId: ERROR_DS_NO_ATTRIBUTE_OR_VALUE +// +// MessageText: +// +// The specified directory service attribute or value does not exist. +// +export const ERROR_DS_NO_ATTRIBUTE_OR_VALUE = 8202; + +// +// MessageId: ERROR_DS_INVALID_ATTRIBUTE_SYNTAX +// +// MessageText: +// +// The attribute syntax specified to the directory service is invalid. +// +export const ERROR_DS_INVALID_ATTRIBUTE_SYNTAX = 8203; + +// +// MessageId: ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED +// +// MessageText: +// +// The attribute type specified to the directory service is not defined. +// +export const ERROR_DS_ATTRIBUTE_TYPE_UNDEFINED = 8204; + +// +// MessageId: ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS +// +// MessageText: +// +// The specified directory service attribute or value already exists. +// +export const ERROR_DS_ATTRIBUTE_OR_VALUE_EXISTS = 8205; + +// +// MessageId: ERROR_DS_BUSY +// +// MessageText: +// +// The directory service is busy. +// +export const ERROR_DS_BUSY = 8206; + +// +// MessageId: ERROR_DS_UNAVAILABLE +// +// MessageText: +// +// The directory service is unavailable. +// +export const ERROR_DS_UNAVAILABLE = 8207; + +// +// MessageId: ERROR_DS_NO_RIDS_ALLOCATED +// +// MessageText: +// +// The directory service was unable to allocate a relative identifier. +// +export const ERROR_DS_NO_RIDS_ALLOCATED = 8208; + +// +// MessageId: ERROR_DS_NO_MORE_RIDS +// +// MessageText: +// +// The directory service has exhausted the pool of relative identifiers. +// +export const ERROR_DS_NO_MORE_RIDS = 8209; + +// +// MessageId: ERROR_DS_INCORRECT_ROLE_OWNER +// +// MessageText: +// +// The requested operation could not be performed because the directory service is not the master for that type of operation. +// +export const ERROR_DS_INCORRECT_ROLE_OWNER = 8210; + +// +// MessageId: ERROR_DS_RIDMGR_INIT_ERROR +// +// MessageText: +// +// The directory service was unable to initialize the subsystem that allocates relative identifiers. +// +export const ERROR_DS_RIDMGR_INIT_ERROR = 8211; + +// +// MessageId: ERROR_DS_OBJ_CLASS_VIOLATION +// +// MessageText: +// +// The requested operation did not satisfy one or more export constraints associated with the class of the object. +// +export const ERROR_DS_OBJ_CLASS_VIOLATION = 8212; + +// +// MessageId: ERROR_DS_CANT_ON_NON_LEAF +// +// MessageText: +// +// The directory service can perform the requested operation only on a leaf object. +// +export const ERROR_DS_CANT_ON_NON_LEAF = 8213; + +// +// MessageId: ERROR_DS_CANT_ON_RDN +// +// MessageText: +// +// The directory service cannot perform the requested operation on the RDN attribute of an object. +// +export const ERROR_DS_CANT_ON_RDN = 8214; + +// +// MessageId: ERROR_DS_CANT_MOD_OBJ_CLASS +// +// MessageText: +// +// The directory service detected an attempt to modify the object class of an object. +// +export const ERROR_DS_CANT_MOD_OBJ_CLASS = 8215; + +// +// MessageId: ERROR_DS_CROSS_DOM_MOVE_ERROR +// +// MessageText: +// +// The requested cross-domain move operation could not be performed. +// +export const ERROR_DS_CROSS_DOM_MOVE_ERROR = 8216; + +// +// MessageId: ERROR_DS_GC_NOT_AVAILABLE +// +// MessageText: +// +// Unable to contact the global catalog server. +// +export const ERROR_DS_GC_NOT_AVAILABLE = 8217; + +// +// MessageId: ERROR_SHARED_POLICY +// +// MessageText: +// +// The policy object is shared and can only be modified at the root. +// +export const ERROR_SHARED_POLICY = 8218; + +// +// MessageId: ERROR_POLICY_OBJECT_NOT_FOUND +// +// MessageText: +// +// The policy object does not exist. +// +export const ERROR_POLICY_OBJECT_NOT_FOUND = 8219; + +// +// MessageId: ERROR_POLICY_ONLY_IN_DS +// +// MessageText: +// +// The requested policy information is only in the directory service. +// +export const ERROR_POLICY_ONLY_IN_DS = 8220; + +// +// MessageId: ERROR_PROMOTION_ACTIVE +// +// MessageText: +// +// A domain controller promotion is currently active. +// +export const ERROR_PROMOTION_ACTIVE = 8221; + +// +// MessageId: ERROR_NO_PROMOTION_ACTIVE +// +// MessageText: +// +// A domain controller promotion is not currently active +// +export const ERROR_NO_PROMOTION_ACTIVE = 8222; + +// 8223 unused +// +// MessageId: ERROR_DS_OPERATIONS_ERROR +// +// MessageText: +// +// An operations error occurred. +// +export const ERROR_DS_OPERATIONS_ERROR = 8224; + +// +// MessageId: ERROR_DS_PROTOCOL_ERROR +// +// MessageText: +// +// A protocol error occurred. +// +export const ERROR_DS_PROTOCOL_ERROR = 8225; + +// +// MessageId: ERROR_DS_TIMELIMIT_EXCEEDED +// +// MessageText: +// +// The time limit for this request was exceeded. +// +export const ERROR_DS_TIMELIMIT_EXCEEDED = 8226; + +// +// MessageId: ERROR_DS_SIZELIMIT_EXCEEDED +// +// MessageText: +// +// The size limit for this request was exceeded. +// +export const ERROR_DS_SIZELIMIT_EXCEEDED = 8227; + +// +// MessageId: ERROR_DS_ADMIN_LIMIT_EXCEEDED +// +// MessageText: +// +// The administrative limit for this request was exceeded. +// +export const ERROR_DS_ADMIN_LIMIT_EXCEEDED = 8228; + +// +// MessageId: ERROR_DS_COMPARE_FALSE +// +// MessageText: +// +// The compare response was false. +// +export const ERROR_DS_COMPARE_FALSE = 8229; + +// +// MessageId: ERROR_DS_COMPARE_TRUE +// +// MessageText: +// +// The compare response was true. +// +export const ERROR_DS_COMPARE_TRUE = 8230; + +// +// MessageId: ERROR_DS_AUTH_METHOD_NOT_SUPPORTED +// +// MessageText: +// +// The requested authentication method is not supported by the server. +// +export const ERROR_DS_AUTH_METHOD_NOT_SUPPORTED = 8231; + +// +// MessageId: ERROR_DS_STRONG_AUTH_REQUIRED +// +// MessageText: +// +// A more secure authentication method is required for this server. +// +export const ERROR_DS_STRONG_AUTH_REQUIRED = 8232; + +// +// MessageId: ERROR_DS_INAPPROPRIATE_AUTH +// +// MessageText: +// +// Inappropriate authentication. +// +export const ERROR_DS_INAPPROPRIATE_AUTH = 8233; + +// +// MessageId: ERROR_DS_AUTH_UNKNOWN +// +// MessageText: +// +// The authentication mechanism is unknown. +// +export const ERROR_DS_AUTH_UNKNOWN = 8234; + +// +// MessageId: ERROR_DS_REFERRAL +// +// MessageText: +// +// A referral was returned from the server. +// +export const ERROR_DS_REFERRAL = 8235; + +// +// MessageId: ERROR_DS_UNAVAILABLE_CRIT_EXTENSION +// +// MessageText: +// +// The server does not support the requested critical extension. +// +export const ERROR_DS_UNAVAILABLE_CRIT_EXTENSION = 8236; + +// +// MessageId: ERROR_DS_CONFIDENTIALITY_REQUIRED +// +// MessageText: +// +// This request requires a secure connection. +// +export const ERROR_DS_CONFIDENTIALITY_REQUIRED = 8237; + +// +// MessageId: ERROR_DS_INAPPROPRIATE_MATCHING +// +// MessageText: +// +// Inappropriate matching. +// +export const ERROR_DS_INAPPROPRIATE_MATCHING = 8238; + +// +// MessageId: ERROR_DS_NO_SUCH_OBJECT +// +// MessageText: +// +// There is no such object on the server. +// +export const ERROR_DS_NO_SUCH_OBJECT = 8240; + +// +// MessageId: ERROR_DS_ALIAS_PROBLEM +// +// MessageText: +// +// There is an alias problem. +// +export const ERROR_DS_ALIAS_PROBLEM = 8241; + +// +// MessageId: ERROR_DS_INVALID_DN_SYNTAX +// +// MessageText: +// +// An invalid dn syntax has been specified. +// +export const ERROR_DS_INVALID_DN_SYNTAX = 8242; + +// +// MessageId: ERROR_DS_IS_LEAF +// +// MessageText: +// +// The object is a leaf object. +// +export const ERROR_DS_IS_LEAF = 8243; + +// +// MessageId: ERROR_DS_ALIAS_DEREF_PROBLEM +// +// MessageText: +// +// There is an alias dereferencing problem. +// +export const ERROR_DS_ALIAS_DEREF_PROBLEM = 8244; + +// +// MessageId: ERROR_DS_UNWILLING_TO_PERFORM +// +// MessageText: +// +// The server is unwilling to process the request. +// +export const ERROR_DS_UNWILLING_TO_PERFORM = 8245; + +// +// MessageId: ERROR_DS_LOOP_DETECT +// +// MessageText: +// +// A loop has been detected. +// +export const ERROR_DS_LOOP_DETECT = 8246; + +// +// MessageId: ERROR_DS_NAMING_VIOLATION +// +// MessageText: +// +// There is a naming violation. +// +export const ERROR_DS_NAMING_VIOLATION = 8247; + +// +// MessageId: ERROR_DS_OBJECT_RESULTS_TOO_LARGE +// +// MessageText: +// +// The result set is too large. +// +export const ERROR_DS_OBJECT_RESULTS_TOO_LARGE = 8248; + +// +// MessageId: ERROR_DS_AFFECTS_MULTIPLE_DSAS +// +// MessageText: +// +// The operation affects multiple DSAs +// +export const ERROR_DS_AFFECTS_MULTIPLE_DSAS = 8249; + +// +// MessageId: ERROR_DS_SERVER_DOWN +// +// MessageText: +// +// The server is not operational. +// +export const ERROR_DS_SERVER_DOWN = 8250; + +// +// MessageId: ERROR_DS_LOCAL_ERROR +// +// MessageText: +// +// A local error has occurred. +// +export const ERROR_DS_LOCAL_ERROR = 8251; + +// +// MessageId: ERROR_DS_ENCODING_ERROR +// +// MessageText: +// +// An encoding error has occurred. +// +export const ERROR_DS_ENCODING_ERROR = 8252; + +// +// MessageId: ERROR_DS_DECODING_ERROR +// +// MessageText: +// +// A decoding error has occurred. +// +export const ERROR_DS_DECODING_ERROR = 8253; + +// +// MessageId: ERROR_DS_FILTER_UNKNOWN +// +// MessageText: +// +// The search filter cannot be recognized. +// +export const ERROR_DS_FILTER_UNKNOWN = 8254; + +// +// MessageId: ERROR_DS_PARAM_ERROR +// +// MessageText: +// +// One or more parameters are illegal. +// +export const ERROR_DS_PARAM_ERROR = 8255; + +// +// MessageId: ERROR_DS_NOT_SUPPORTED +// +// MessageText: +// +// The specified method is not supported. +// +export const ERROR_DS_NOT_SUPPORTED = 8256; + +// +// MessageId: ERROR_DS_NO_RESULTS_RETURNED +// +// MessageText: +// +// No results were returned. +// +export const ERROR_DS_NO_RESULTS_RETURNED = 8257; + +// +// MessageId: ERROR_DS_CONTROL_NOT_FOUND +// +// MessageText: +// +// The specified control is not supported by the server. +// +export const ERROR_DS_CONTROL_NOT_FOUND = 8258; + +// +// MessageId: ERROR_DS_CLIENT_LOOP +// +// MessageText: +// +// A referral loop was detected by the client. +// +export const ERROR_DS_CLIENT_LOOP = 8259; + +// +// MessageId: ERROR_DS_REFERRAL_LIMIT_EXCEEDED +// +// MessageText: +// +// The preset referral limit was exceeded. +// +export const ERROR_DS_REFERRAL_LIMIT_EXCEEDED = 8260; + +// +// MessageId: ERROR_DS_SORT_CONTROL_MISSING +// +// MessageText: +// +// The search requires a SORT control. +// +export const ERROR_DS_SORT_CONTROL_MISSING = 8261; + +// +// MessageId: ERROR_DS_OFFSET_RANGE_ERROR +// +// MessageText: +// +// The search results exceed the offset range specified. +// +export const ERROR_DS_OFFSET_RANGE_ERROR = 8262; + +// +// MessageId: ERROR_DS_ROOT_MUST_BE_NC +// +// MessageText: +// +// The root object must be the head of a naming context. The root object cannot have an instantiated parent. +// +export const ERROR_DS_ROOT_MUST_BE_NC = 8301; + +// +// MessageId: ERROR_DS_ADD_REPLICA_INHIBITED +// +// MessageText: +// +// The add replica operation cannot be performed. The naming context must be writeable in order to create the replica. +// +export const ERROR_DS_ADD_REPLICA_INHIBITED = 8302; + +// +// MessageId: ERROR_DS_ATT_NOT_DEF_IN_SCHEMA +// +// MessageText: +// +// A reference to an attribute that is not defined in the schema occurred. +// +export const ERROR_DS_ATT_NOT_DEF_IN_SCHEMA = 8303; + +// +// MessageId: ERROR_DS_MAX_OBJ_SIZE_EXCEEDED +// +// MessageText: +// +// The maximum size of an object has been exceeded. +// +export const ERROR_DS_MAX_OBJ_SIZE_EXCEEDED = 8304; + +// +// MessageId: ERROR_DS_OBJ_STRING_NAME_EXISTS +// +// MessageText: +// +// An attempt was made to add an object to the directory with a name that is already in use. +// +export const ERROR_DS_OBJ_STRING_NAME_EXISTS = 8305; + +// +// MessageId: ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA +// +// MessageText: +// +// An attempt was made to add an object of a class that does not have an RDN defined in the schema. +// +export const ERROR_DS_NO_RDN_DEFINED_IN_SCHEMA = 8306; + +// +// MessageId: ERROR_DS_RDN_DOESNT_MATCH_SCHEMA +// +// MessageText: +// +// An attempt was made to add an object using an RDN that is not the RDN defined in the schema. +// +export const ERROR_DS_RDN_DOESNT_MATCH_SCHEMA = 8307; + +// +// MessageId: ERROR_DS_NO_REQUESTED_ATTS_FOUND +// +// MessageText: +// +// None of the requested attributes were found on the objects. +// +export const ERROR_DS_NO_REQUESTED_ATTS_FOUND = 8308; + +// +// MessageId: ERROR_DS_USER_BUFFER_TO_SMALL +// +// MessageText: +// +// The user buffer is too small. +// +export const ERROR_DS_USER_BUFFER_TO_SMALL = 8309; + +// +// MessageId: ERROR_DS_ATT_IS_NOT_ON_OBJ +// +// MessageText: +// +// The attribute specified in the operation is not present on the object. +// +export const ERROR_DS_ATT_IS_NOT_ON_OBJ = 8310; + +// +// MessageId: ERROR_DS_ILLEGAL_MOD_OPERATION +// +// MessageText: +// +// Illegal modify operation. Some aspect of the modification is not permitted. +// +export const ERROR_DS_ILLEGAL_MOD_OPERATION = 8311; + +// +// MessageId: ERROR_DS_OBJ_TOO_LARGE +// +// MessageText: +// +// The specified object is too large. +// +export const ERROR_DS_OBJ_TOO_LARGE = 8312; + +// +// MessageId: ERROR_DS_BAD_INSTANCE_TYPE +// +// MessageText: +// +// The specified instance type is not valid. +// +export const ERROR_DS_BAD_INSTANCE_TYPE = 8313; + +// +// MessageId: ERROR_DS_MASTERDSA_REQUIRED +// +// MessageText: +// +// The operation must be performed at a master DSA. +// +export const ERROR_DS_MASTERDSA_REQUIRED = 8314; + +// +// MessageId: ERROR_DS_OBJECT_CLASS_REQUIRED +// +// MessageText: +// +// The object class attribute must be specified. +// +export const ERROR_DS_OBJECT_CLASS_REQUIRED = 8315; + +// +// MessageId: ERROR_DS_MISSING_REQUIRED_ATT +// +// MessageText: +// +// A required attribute is missing. +// +export const ERROR_DS_MISSING_REQUIRED_ATT = 8316; + +// +// MessageId: ERROR_DS_ATT_NOT_DEF_FOR_CLASS +// +// MessageText: +// +// An attempt was made to modify an object to include an attribute that is not legal for its class. +// +export const ERROR_DS_ATT_NOT_DEF_FOR_CLASS = 8317; + +// +// MessageId: ERROR_DS_ATT_ALREADY_EXISTS +// +// MessageText: +// +// The specified attribute is already present on the object. +// +export const ERROR_DS_ATT_ALREADY_EXISTS = 8318; + +// 8319 unused +// +// MessageId: ERROR_DS_CANT_ADD_ATT_VALUES +// +// MessageText: +// +// The specified attribute is not present, or has no values. +// +export const ERROR_DS_CANT_ADD_ATT_VALUES = 8320; + +// +// MessageId: ERROR_DS_ATT_VAL_ALREADY_EXISTS +// +// MessageText: +// +// The specified value already exists. +// +export const ERROR_DS_ATT_VAL_ALREADY_EXISTS = 8323; + +// +// MessageId: ERROR_DS_CANT_REM_MISSING_ATT +// +// MessageText: +// +// The attribute cannot be removed because it is not present on the object. +// +export const ERROR_DS_CANT_REM_MISSING_ATT = 8324; + +// +// MessageId: ERROR_DS_CANT_REM_MISSING_ATT_VAL +// +// MessageText: +// +// The attribute value cannot be removed because it is not present on the object. +// +export const ERROR_DS_CANT_REM_MISSING_ATT_VAL = 8325; + +// +// MessageId: ERROR_DS_ROOT_CANT_BE_SUBREF +// +// MessageText: +// +// The specified root object cannot be a subref. +// +export const ERROR_DS_ROOT_CANT_BE_SUBREF = 8326; + +// +// MessageId: ERROR_DS_NO_CHAINING +// +// MessageText: +// +// Chaining is not permitted. +// +export const ERROR_DS_NO_CHAINING = 8327; + +// +// MessageId: ERROR_DS_NO_CHAINED_EVAL +// +// MessageText: +// +// Chained evaluation is not permitted. +// +export const ERROR_DS_NO_CHAINED_EVAL = 8328; + +// +// MessageId: ERROR_DS_NO_PARENT_OBJECT +// +// MessageText: +// +// The operation could not be performed because the object's parent is either uninstantiated or deleted. +// +export const ERROR_DS_NO_PARENT_OBJECT = 8329; + +// +// MessageId: ERROR_DS_PARENT_IS_AN_ALIAS +// +// MessageText: +// +// Having a parent that is an alias is not permitted. Aliases are leaf objects. +// +export const ERROR_DS_PARENT_IS_AN_ALIAS = 8330; + +// +// MessageId: ERROR_DS_CANT_MIX_MASTER_AND_REPS +// +// MessageText: +// +// The object and parent must be of the same type, either both masters or both replicas. +// +export const ERROR_DS_CANT_MIX_MASTER_AND_REPS = 8331; + +// +// MessageId: ERROR_DS_CHILDREN_EXIST +// +// MessageText: +// +// The operation cannot be performed because child objects exist. This operation can only be performed on a leaf object. +// +export const ERROR_DS_CHILDREN_EXIST = 8332; + +// +// MessageId: ERROR_DS_OBJ_NOT_FOUND +// +// MessageText: +// +// Directory object not found. +// +export const ERROR_DS_OBJ_NOT_FOUND = 8333; + +// +// MessageId: ERROR_DS_ALIASED_OBJ_MISSING +// +// MessageText: +// +// The aliased object is missing. +// +export const ERROR_DS_ALIASED_OBJ_MISSING = 8334; + +// +// MessageId: ERROR_DS_BAD_NAME_SYNTAX +// +// MessageText: +// +// The object name has bad syntax. +// +export const ERROR_DS_BAD_NAME_SYNTAX = 8335; + +// +// MessageId: ERROR_DS_ALIAS_POINTS_TO_ALIAS +// +// MessageText: +// +// It is not permitted for an alias to refer to another alias. +// +export const ERROR_DS_ALIAS_POINTS_TO_ALIAS = 8336; + +// +// MessageId: ERROR_DS_CANT_DEREF_ALIAS +// +// MessageText: +// +// The alias cannot be dereferenced. +// +export const ERROR_DS_CANT_DEREF_ALIAS = 8337; + +// +// MessageId: ERROR_DS_OUT_OF_SCOPE +// +// MessageText: +// +// The operation is out of scope. +// +export const ERROR_DS_OUT_OF_SCOPE = 8338; + +// +// MessageId: ERROR_DS_OBJECT_BEING_REMOVED +// +// MessageText: +// +// The operation cannot continue because the object is in the process of being removed. +// +export const ERROR_DS_OBJECT_BEING_REMOVED = 8339; + +// +// MessageId: ERROR_DS_CANT_DELETE_DSA_OBJ +// +// MessageText: +// +// The DSA object cannot be deleted. +// +export const ERROR_DS_CANT_DELETE_DSA_OBJ = 8340; + +// +// MessageId: ERROR_DS_GENERIC_ERROR +// +// MessageText: +// +// A directory service error has occurred. +// +export const ERROR_DS_GENERIC_ERROR = 8341; + +// +// MessageId: ERROR_DS_DSA_MUST_BE_INT_MASTER +// +// MessageText: +// +// The operation can only be performed on an internal master DSA object. +// +export const ERROR_DS_DSA_MUST_BE_INT_MASTER = 8342; + +// +// MessageId: ERROR_DS_CLASS_NOT_DSA +// +// MessageText: +// +// The object must be of class DSA. +// +export const ERROR_DS_CLASS_NOT_DSA = 8343; + +// +// MessageId: ERROR_DS_INSUFF_ACCESS_RIGHTS +// +// MessageText: +// +// Insufficient access rights to perform the operation. +// +export const ERROR_DS_INSUFF_ACCESS_RIGHTS = 8344; + +// +// MessageId: ERROR_DS_ILLEGAL_SUPERIOR +// +// MessageText: +// +// The object cannot be added because the parent is not on the list of possible superiors. +// +export const ERROR_DS_ILLEGAL_SUPERIOR = 8345; + +// +// MessageId: ERROR_DS_ATTRIBUTE_OWNED_BY_SAM +// +// MessageText: +// +// Access to the attribute is not permitted because the attribute is owned by the Security Accounts Manager (SAM). +// +export const ERROR_DS_ATTRIBUTE_OWNED_BY_SAM = 8346; + +// +// MessageId: ERROR_DS_NAME_TOO_MANY_PARTS +// +// MessageText: +// +// The name has too many parts. +// +export const ERROR_DS_NAME_TOO_MANY_PARTS = 8347; + +// +// MessageId: ERROR_DS_NAME_TOO_LONG +// +// MessageText: +// +// The name is too long. +// +export const ERROR_DS_NAME_TOO_LONG = 8348; + +// +// MessageId: ERROR_DS_NAME_VALUE_TOO_LONG +// +// MessageText: +// +// The name value is too long. +// +export const ERROR_DS_NAME_VALUE_TOO_LONG = 8349; + +// +// MessageId: ERROR_DS_NAME_UNPARSEABLE +// +// MessageText: +// +// The directory service encountered an error parsing a name. +// +export const ERROR_DS_NAME_UNPARSEABLE = 8350; + +// +// MessageId: ERROR_DS_NAME_TYPE_UNKNOWN +// +// MessageText: +// +// The directory service cannot get the attribute type for a name. +// +export const ERROR_DS_NAME_TYPE_UNKNOWN = 8351; + +// +// MessageId: ERROR_DS_NOT_AN_OBJECT +// +// MessageText: +// +// The name does not identify an object; the name identifies a phantom. +// +export const ERROR_DS_NOT_AN_OBJECT = 8352; + +// +// MessageId: ERROR_DS_SEC_DESC_TOO_SHORT +// +// MessageText: +// +// The security descriptor is too short. +// +export const ERROR_DS_SEC_DESC_TOO_SHORT = 8353; + +// +// MessageId: ERROR_DS_SEC_DESC_INVALID +// +// MessageText: +// +// The security descriptor is invalid. +// +export const ERROR_DS_SEC_DESC_INVALID = 8354; + +// +// MessageId: ERROR_DS_NO_DELETED_NAME +// +// MessageText: +// +// Failed to create name for deleted object. +// +export const ERROR_DS_NO_DELETED_NAME = 8355; + +// +// MessageId: ERROR_DS_SUBREF_MUST_HAVE_PARENT +// +// MessageText: +// +// The parent of a new subref must exist. +// +export const ERROR_DS_SUBREF_MUST_HAVE_PARENT = 8356; + +// +// MessageId: ERROR_DS_NCNAME_MUST_BE_NC +// +// MessageText: +// +// The object must be a naming context. +// +export const ERROR_DS_NCNAME_MUST_BE_NC = 8357; + +// +// MessageId: ERROR_DS_CANT_ADD_SYSTEM_ONLY +// +// MessageText: +// +// It is not permitted to add an attribute which is owned by the system. +// +export const ERROR_DS_CANT_ADD_SYSTEM_ONLY = 8358; + +// +// MessageId: ERROR_DS_CLASS_MUST_BE_CONCRETE +// +// MessageText: +// +// The class of the object must be structural; you cannot instantiate an abstract class. +// +export const ERROR_DS_CLASS_MUST_BE_CONCRETE = 8359; + +// +// MessageId: ERROR_DS_INVALID_DMD +// +// MessageText: +// +// The schema object could not be found. +// +export const ERROR_DS_INVALID_DMD = 8360; + +// +// MessageId: ERROR_DS_OBJ_GUID_EXISTS +// +// MessageText: +// +// A local object with this GUID (dead or alive) already exists. +// +export const ERROR_DS_OBJ_GUID_EXISTS = 8361; + +// +// MessageId: ERROR_DS_NOT_ON_BACKLINK +// +// MessageText: +// +// The operation cannot be performed on a back link. +// +export const ERROR_DS_NOT_ON_BACKLINK = 8362; + +// +// MessageId: ERROR_DS_NO_CROSSREF_FOR_NC +// +// MessageText: +// +// The cross reference for the specified naming context could not be found. +// +export const ERROR_DS_NO_CROSSREF_FOR_NC = 8363; + +// +// MessageId: ERROR_DS_SHUTTING_DOWN +// +// MessageText: +// +// The operation could not be performed because the directory service is shutting down. +// +export const ERROR_DS_SHUTTING_DOWN = 8364; + +// +// MessageId: ERROR_DS_UNKNOWN_OPERATION +// +// MessageText: +// +// The directory service request is invalid. +// +export const ERROR_DS_UNKNOWN_OPERATION = 8365; + +// +// MessageId: ERROR_DS_INVALID_ROLE_OWNER +// +// MessageText: +// +// The role owner attribute could not be read. +// +export const ERROR_DS_INVALID_ROLE_OWNER = 8366; + +// +// MessageId: ERROR_DS_COULDNT_CONTACT_FSMO +// +// MessageText: +// +// The requested FSMO operation failed. The current FSMO holder could not be contacted. +// +export const ERROR_DS_COULDNT_CONTACT_FSMO = 8367; + +// +// MessageId: ERROR_DS_CROSS_NC_DN_RENAME +// +// MessageText: +// +// Modification of a DN across a naming context is not permitted. +// +export const ERROR_DS_CROSS_NC_DN_RENAME = 8368; + +// +// MessageId: ERROR_DS_CANT_MOD_SYSTEM_ONLY +// +// MessageText: +// +// The attribute cannot be modified because it is owned by the system. +// +export const ERROR_DS_CANT_MOD_SYSTEM_ONLY = 8369; + +// +// MessageId: ERROR_DS_REPLICATOR_ONLY +// +// MessageText: +// +// Only the replicator can perform this function. +// +export const ERROR_DS_REPLICATOR_ONLY = 8370; + +// +// MessageId: ERROR_DS_OBJ_CLASS_NOT_DEFINED +// +// MessageText: +// +// The specified class is not defined. +// +export const ERROR_DS_OBJ_CLASS_NOT_DEFINED = 8371; + +// +// MessageId: ERROR_DS_OBJ_CLASS_NOT_SUBCLASS +// +// MessageText: +// +// The specified class is not a subclass. +// +export const ERROR_DS_OBJ_CLASS_NOT_SUBCLASS = 8372; + +// +// MessageId: ERROR_DS_NAME_REFERENCE_INVALID +// +// MessageText: +// +// The name reference is invalid. +// +export const ERROR_DS_NAME_REFERENCE_INVALID = 8373; + +// +// MessageId: ERROR_DS_CROSS_REF_EXISTS +// +// MessageText: +// +// A cross reference already exists. +// +export const ERROR_DS_CROSS_REF_EXISTS = 8374; + +// +// MessageId: ERROR_DS_CANT_DEL_MASTER_CROSSREF +// +// MessageText: +// +// It is not permitted to delete a master cross reference. +// +export const ERROR_DS_CANT_DEL_MASTER_CROSSREF = 8375; + +// +// MessageId: ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD +// +// MessageText: +// +// Subtree notifications are only supported on NC heads. +// +export const ERROR_DS_SUBTREE_NOTIFY_NOT_NC_HEAD = 8376; + +// +// MessageId: ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX +// +// MessageText: +// +// Notification filter is too complex. +// +export const ERROR_DS_NOTIFY_FILTER_TOO_COMPLEX = 8377; + +// +// MessageId: ERROR_DS_DUP_RDN +// +// MessageText: +// +// Schema update failed: duplicate RDN. +// +export const ERROR_DS_DUP_RDN = 8378; + +// +// MessageId: ERROR_DS_DUP_OID +// +// MessageText: +// +// Schema update failed: duplicate OID. +// +export const ERROR_DS_DUP_OID = 8379; + +// +// MessageId: ERROR_DS_DUP_MAPI_ID +// +// MessageText: +// +// Schema update failed: duplicate MAPI identifier. +// +export const ERROR_DS_DUP_MAPI_ID = 8380; + +// +// MessageId: ERROR_DS_DUP_SCHEMA_ID_GUID +// +// MessageText: +// +// Schema update failed: duplicate schema-id GUID. +// +export const ERROR_DS_DUP_SCHEMA_ID_GUID = 8381; + +// +// MessageId: ERROR_DS_DUP_LDAP_DISPLAY_NAME +// +// MessageText: +// +// Schema update failed: duplicate LDAP display name. +// +export const ERROR_DS_DUP_LDAP_DISPLAY_NAME = 8382; + +// +// MessageId: ERROR_DS_SEMANTIC_ATT_TEST +// +// MessageText: +// +// Schema update failed: range-lower less than range upper. +// +export const ERROR_DS_SEMANTIC_ATT_TEST = 8383; + +// +// MessageId: ERROR_DS_SYNTAX_MISMATCH +// +// MessageText: +// +// Schema update failed: syntax mismatch. +// +export const ERROR_DS_SYNTAX_MISMATCH = 8384; + +// +// MessageId: ERROR_DS_EXISTS_IN_MUST_HAVE +// +// MessageText: +// +// Schema deletion failed: attribute is used in must-contain. +// +export const ERROR_DS_EXISTS_IN_MUST_HAVE = 8385; + +// +// MessageId: ERROR_DS_EXISTS_IN_MAY_HAVE +// +// MessageText: +// +// Schema deletion failed: attribute is used in may-contain. +// +export const ERROR_DS_EXISTS_IN_MAY_HAVE = 8386; + +// +// MessageId: ERROR_DS_NONEXISTENT_MAY_HAVE +// +// MessageText: +// +// Schema update failed: attribute in may-contain does not exist. +// +export const ERROR_DS_NONEXISTENT_MAY_HAVE = 8387; + +// +// MessageId: ERROR_DS_NONEXISTENT_MUST_HAVE +// +// MessageText: +// +// Schema update failed: attribute in must-contain does not exist. +// +export const ERROR_DS_NONEXISTENT_MUST_HAVE = 8388; + +// +// MessageId: ERROR_DS_AUX_CLS_TEST_FAIL +// +// MessageText: +// +// Schema update failed: class in aux-class list does not exist or is not an auxiliary class. +// +export const ERROR_DS_AUX_CLS_TEST_FAIL = 8389; + +// +// MessageId: ERROR_DS_NONEXISTENT_POSS_SUP +// +// MessageText: +// +// Schema update failed: class in poss-superiors does not exist. +// +export const ERROR_DS_NONEXISTENT_POSS_SUP = 8390; + +// +// MessageId: ERROR_DS_SUB_CLS_TEST_FAIL +// +// MessageText: +// +// Schema update failed: class in subclassof list does not exist or does not satisfy hierarchy rules. +// +export const ERROR_DS_SUB_CLS_TEST_FAIL = 8391; + +// +// MessageId: ERROR_DS_BAD_RDN_ATT_ID_SYNTAX +// +// MessageText: +// +// Schema update failed: Rdn-Att-Id has wrong syntax. +// +export const ERROR_DS_BAD_RDN_ATT_ID_SYNTAX = 8392; + +// +// MessageId: ERROR_DS_EXISTS_IN_AUX_CLS +// +// MessageText: +// +// Schema deletion failed: class is used as auxiliary class. +// +export const ERROR_DS_EXISTS_IN_AUX_CLS = 8393; + +// +// MessageId: ERROR_DS_EXISTS_IN_SUB_CLS +// +// MessageText: +// +// Schema deletion failed: class is used as sub class. +// +export const ERROR_DS_EXISTS_IN_SUB_CLS = 8394; + +// +// MessageId: ERROR_DS_EXISTS_IN_POSS_SUP +// +// MessageText: +// +// Schema deletion failed: class is used as poss-superior. +// +export const ERROR_DS_EXISTS_IN_POSS_SUP = 8395; + +// +// MessageId: ERROR_DS_RECALCSCHEMA_FAILED +// +// MessageText: +// +// Schema update failed in recalculating validation cache. +// +export const ERROR_DS_RECALCSCHEMA_FAILED = 8396; + +// +// MessageId: ERROR_DS_TREE_DELETE_NOT_FINISHED +// +// MessageText: +// +// The tree deletion is not finished. The request must be made again to continue deleting the tree. +// +export const ERROR_DS_TREE_DELETE_NOT_FINISHED = 8397; + +// +// MessageId: ERROR_DS_CANT_DELETE +// +// MessageText: +// +// The requested delete operation could not be performed. +// +export const ERROR_DS_CANT_DELETE = 8398; + +// +// MessageId: ERROR_DS_ATT_SCHEMA_REQ_ID +// +// MessageText: +// +// Cannot read the governs class identifier for the schema record. +// +export const ERROR_DS_ATT_SCHEMA_REQ_ID = 8399; + +// +// MessageId: ERROR_DS_BAD_ATT_SCHEMA_SYNTAX +// +// MessageText: +// +// The attribute schema has bad syntax. +// +export const ERROR_DS_BAD_ATT_SCHEMA_SYNTAX = 8400; + +// +// MessageId: ERROR_DS_CANT_CACHE_ATT +// +// MessageText: +// +// The attribute could not be cached. +// +export const ERROR_DS_CANT_CACHE_ATT = 8401; + +// +// MessageId: ERROR_DS_CANT_CACHE_CLASS +// +// MessageText: +// +// The class could not be cached. +// +export const ERROR_DS_CANT_CACHE_CLASS = 8402; + +// +// MessageId: ERROR_DS_CANT_REMOVE_ATT_CACHE +// +// MessageText: +// +// The attribute could not be removed from the cache. +// +export const ERROR_DS_CANT_REMOVE_ATT_CACHE = 8403; + +// +// MessageId: ERROR_DS_CANT_REMOVE_CLASS_CACHE +// +// MessageText: +// +// The class could not be removed from the cache. +// +export const ERROR_DS_CANT_REMOVE_CLASS_CACHE = 8404; + +// +// MessageId: ERROR_DS_CANT_RETRIEVE_DN +// +// MessageText: +// +// The distinguished name attribute could not be read. +// +export const ERROR_DS_CANT_RETRIEVE_DN = 8405; + +// +// MessageId: ERROR_DS_MISSING_SUPREF +// +// MessageText: +// +// No superior reference has been configured for the directory service. The directory service is therefore unable to issue referrals to objects outside this forest. +// +export const ERROR_DS_MISSING_SUPREF = 8406; + +// +// MessageId: ERROR_DS_CANT_RETRIEVE_INSTANCE +// +// MessageText: +// +// The instance type attribute could not be retrieved. +// +export const ERROR_DS_CANT_RETRIEVE_INSTANCE = 8407; + +// +// MessageId: ERROR_DS_CODE_INCONSISTENCY +// +// MessageText: +// +// An internal error has occurred. +// +export const ERROR_DS_CODE_INCONSISTENCY = 8408; + +// +// MessageId: ERROR_DS_DATABASE_ERROR +// +// MessageText: +// +// A database error has occurred. +// +export const ERROR_DS_DATABASE_ERROR = 8409; + +// +// MessageId: ERROR_DS_GOVERNSID_MISSING +// +// MessageText: +// +// The attribute GOVERNSID is missing. +// +export const ERROR_DS_GOVERNSID_MISSING = 8410; + +// +// MessageId: ERROR_DS_MISSING_EXPECTED_ATT +// +// MessageText: +// +// An expected attribute is missing. +// +export const ERROR_DS_MISSING_EXPECTED_ATT = 8411; + +// +// MessageId: ERROR_DS_NCNAME_MISSING_CR_REF +// +// MessageText: +// +// The specified naming context is missing a cross reference. +// +export const ERROR_DS_NCNAME_MISSING_CR_REF = 8412; + +// +// MessageId: ERROR_DS_SECURITY_CHECKING_ERROR +// +// MessageText: +// +// A security checking error has occurred. +// +export const ERROR_DS_SECURITY_CHECKING_ERROR = 8413; + +// +// MessageId: ERROR_DS_SCHEMA_NOT_LOADED +// +// MessageText: +// +// The schema is not loaded. +// +export const ERROR_DS_SCHEMA_NOT_LOADED = 8414; + +// +// MessageId: ERROR_DS_SCHEMA_ALLOC_FAILED +// +// MessageText: +// +// Schema allocation failed. Please check if the machine is running low on memory. +// +export const ERROR_DS_SCHEMA_ALLOC_FAILED = 8415; + +// +// MessageId: ERROR_DS_ATT_SCHEMA_REQ_SYNTAX +// +// MessageText: +// +// Failed to obtain the required syntax for the attribute schema. +// +export const ERROR_DS_ATT_SCHEMA_REQ_SYNTAX = 8416; + +// +// MessageId: ERROR_DS_GCVERIFY_ERROR +// +// MessageText: +// +// The global catalog verification failed. The global catalog is not available or does not support the operation. Some part of the directory is currently not available. +// +export const ERROR_DS_GCVERIFY_ERROR = 8417; + +// +// MessageId: ERROR_DS_DRA_SCHEMA_MISMATCH +// +// MessageText: +// +// The replication operation failed because of a schema mismatch between the servers involved. +// +export const ERROR_DS_DRA_SCHEMA_MISMATCH = 8418; + +// +// MessageId: ERROR_DS_CANT_FIND_DSA_OBJ +// +// MessageText: +// +// The DSA object could not be found. +// +export const ERROR_DS_CANT_FIND_DSA_OBJ = 8419; + +// +// MessageId: ERROR_DS_CANT_FIND_EXPECTED_NC +// +// MessageText: +// +// The naming context could not be found. +// +export const ERROR_DS_CANT_FIND_EXPECTED_NC = 8420; + +// +// MessageId: ERROR_DS_CANT_FIND_NC_IN_CACHE +// +// MessageText: +// +// The naming context could not be found in the cache. +// +export const ERROR_DS_CANT_FIND_NC_IN_CACHE = 8421; + +// +// MessageId: ERROR_DS_CANT_RETRIEVE_CHILD +// +// MessageText: +// +// The child object could not be retrieved. +// +export const ERROR_DS_CANT_RETRIEVE_CHILD = 8422; + +// +// MessageId: ERROR_DS_SECURITY_ILLEGAL_MODIFY +// +// MessageText: +// +// The modification was not permitted for security reasons. +// +export const ERROR_DS_SECURITY_ILLEGAL_MODIFY = 8423; + +// +// MessageId: ERROR_DS_CANT_REPLACE_HIDDEN_REC +// +// MessageText: +// +// The operation cannot replace the hidden record. +// +export const ERROR_DS_CANT_REPLACE_HIDDEN_REC = 8424; + +// +// MessageId: ERROR_DS_BAD_HIERARCHY_FILE +// +// MessageText: +// +// The hierarchy file is invalid. +// +export const ERROR_DS_BAD_HIERARCHY_FILE = 8425; + +// +// MessageId: ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED +// +// MessageText: +// +// The attempt to build the hierarchy table failed. +// +export const ERROR_DS_BUILD_HIERARCHY_TABLE_FAILED = 8426; + +// +// MessageId: ERROR_DS_CONFIG_PARAM_MISSING +// +// MessageText: +// +// The directory configuration parameter is missing from the registry. +// +export const ERROR_DS_CONFIG_PARAM_MISSING = 8427; + +// +// MessageId: ERROR_DS_COUNTING_AB_INDICES_FAILED +// +// MessageText: +// +// The attempt to count the address book indices failed. +// +export const ERROR_DS_COUNTING_AB_INDICES_FAILED = 8428; + +// +// MessageId: ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED +// +// MessageText: +// +// The allocation of the hierarchy table failed. +// +export const ERROR_DS_HIERARCHY_TABLE_MALLOC_FAILED = 8429; + +// +// MessageId: ERROR_DS_INTERNAL_FAILURE +// +// MessageText: +// +// The directory service encountered an internal failure. +// +export const ERROR_DS_INTERNAL_FAILURE = 8430; + +// +// MessageId: ERROR_DS_UNKNOWN_ERROR +// +// MessageText: +// +// The directory service encountered an unknown failure. +// +export const ERROR_DS_UNKNOWN_ERROR = 8431; + +// +// MessageId: ERROR_DS_ROOT_REQUIRES_CLASS_TOP +// +// MessageText: +// +// A root object requires a class of 'top'. +// +export const ERROR_DS_ROOT_REQUIRES_CLASS_TOP = 8432; + +// +// MessageId: ERROR_DS_REFUSING_FSMO_ROLES +// +// MessageText: +// +// This directory server is shutting down, and cannot take ownership of new floating single-master operation roles. +// +export const ERROR_DS_REFUSING_FSMO_ROLES = 8433; + +// +// MessageId: ERROR_DS_MISSING_FSMO_SETTINGS +// +// MessageText: +// +// The directory service is missing mandatory configuration information, and is unable to determine the ownership of floating single-master operation roles. +// +export const ERROR_DS_MISSING_FSMO_SETTINGS = 8434; + +// +// MessageId: ERROR_DS_UNABLE_TO_SURRENDER_ROLES +// +// MessageText: +// +// The directory service was unable to transfer ownership of one or more floating single-master operation roles to other servers. +// +export const ERROR_DS_UNABLE_TO_SURRENDER_ROLES = 8435; + +// +// MessageId: ERROR_DS_DRA_GENERIC +// +// MessageText: +// +// The replication operation failed. +// +export const ERROR_DS_DRA_GENERIC = 8436; + +// +// MessageId: ERROR_DS_DRA_INVALID_PARAMETER +// +// MessageText: +// +// An invalid parameter was specified for this replication operation. +// +export const ERROR_DS_DRA_INVALID_PARAMETER = 8437; + +// +// MessageId: ERROR_DS_DRA_BUSY +// +// MessageText: +// +// The directory service is too busy to complete the replication operation at this time. +// +export const ERROR_DS_DRA_BUSY = 8438; + +// +// MessageId: ERROR_DS_DRA_BAD_DN +// +// MessageText: +// +// The distinguished name specified for this replication operation is invalid. +// +export const ERROR_DS_DRA_BAD_DN = 8439; + +// +// MessageId: ERROR_DS_DRA_BAD_NC +// +// MessageText: +// +// The naming context specified for this replication operation is invalid. +// +export const ERROR_DS_DRA_BAD_NC = 8440; + +// +// MessageId: ERROR_DS_DRA_DN_EXISTS +// +// MessageText: +// +// The distinguished name specified for this replication operation already exists. +// +export const ERROR_DS_DRA_DN_EXISTS = 8441; + +// +// MessageId: ERROR_DS_DRA_INTERNAL_ERROR +// +// MessageText: +// +// The replication system encountered an internal error. +// +export const ERROR_DS_DRA_INTERNAL_ERROR = 8442; + +// +// MessageId: ERROR_DS_DRA_INCONSISTENT_DIT +// +// MessageText: +// +// The replication operation encountered a database inconsistency. +// +export const ERROR_DS_DRA_INCONSISTENT_DIT = 8443; + +// +// MessageId: ERROR_DS_DRA_CONNECTION_FAILED +// +// MessageText: +// +// The server specified for this replication operation could not be contacted. +// +export const ERROR_DS_DRA_CONNECTION_FAILED = 8444; + +// +// MessageId: ERROR_DS_DRA_BAD_INSTANCE_TYPE +// +// MessageText: +// +// The replication operation encountered an object with an invalid instance type. +// +export const ERROR_DS_DRA_BAD_INSTANCE_TYPE = 8445; + +// +// MessageId: ERROR_DS_DRA_OUT_OF_MEM +// +// MessageText: +// +// The replication operation failed to allocate memory. +// +export const ERROR_DS_DRA_OUT_OF_MEM = 8446; + +// +// MessageId: ERROR_DS_DRA_MAIL_PROBLEM +// +// MessageText: +// +// The replication operation encountered an error with the mail system. +// +export const ERROR_DS_DRA_MAIL_PROBLEM = 8447; + +// +// MessageId: ERROR_DS_DRA_REF_ALREADY_EXISTS +// +// MessageText: +// +// The replication reference information for the target server already exists. +// +export const ERROR_DS_DRA_REF_ALREADY_EXISTS = 8448; + +// +// MessageId: ERROR_DS_DRA_REF_NOT_FOUND +// +// MessageText: +// +// The replication reference information for the target server does not exist. +// +export const ERROR_DS_DRA_REF_NOT_FOUND = 8449; + +// +// MessageId: ERROR_DS_DRA_OBJ_IS_REP_SOURCE +// +// MessageText: +// +// The naming context cannot be removed because it is replicated to another server. +// +export const ERROR_DS_DRA_OBJ_IS_REP_SOURCE = 8450; + +// +// MessageId: ERROR_DS_DRA_DB_ERROR +// +// MessageText: +// +// The replication operation encountered a database error. +// +export const ERROR_DS_DRA_DB_ERROR = 8451; + +// +// MessageId: ERROR_DS_DRA_NO_REPLICA +// +// MessageText: +// +// The naming context is in the process of being removed or is not replicated from the specified server. +// +export const ERROR_DS_DRA_NO_REPLICA = 8452; + +// +// MessageId: ERROR_DS_DRA_ACCESS_DENIED +// +// MessageText: +// +// Replication access was denied. +// +export const ERROR_DS_DRA_ACCESS_DENIED = 8453; + +// +// MessageId: ERROR_DS_DRA_NOT_SUPPORTED +// +// MessageText: +// +// The requested operation is not supported by this version of the directory service. +// +export const ERROR_DS_DRA_NOT_SUPPORTED = 8454; + +// +// MessageId: ERROR_DS_DRA_RPC_CANCELLED +// +// MessageText: +// +// The replication remote procedure call was cancelled. +// +export const ERROR_DS_DRA_RPC_CANCELLED = 8455; + +// +// MessageId: ERROR_DS_DRA_SOURCE_DISABLED +// +// MessageText: +// +// The source server is currently rejecting replication requests. +// +export const ERROR_DS_DRA_SOURCE_DISABLED = 8456; + +// +// MessageId: ERROR_DS_DRA_SINK_DISABLED +// +// MessageText: +// +// The destination server is currently rejecting replication requests. +// +export const ERROR_DS_DRA_SINK_DISABLED = 8457; + +// +// MessageId: ERROR_DS_DRA_NAME_COLLISION +// +// MessageText: +// +// The replication operation failed due to a collision of object names. +// +export const ERROR_DS_DRA_NAME_COLLISION = 8458; + +// +// MessageId: ERROR_DS_DRA_SOURCE_REINSTALLED +// +// MessageText: +// +// The replication source has been reinstalled. +// +export const ERROR_DS_DRA_SOURCE_REINSTALLED = 8459; + +// +// MessageId: ERROR_DS_DRA_MISSING_PARENT +// +// MessageText: +// +// The replication operation failed because a required parent object is missing. +// +export const ERROR_DS_DRA_MISSING_PARENT = 8460; + +// +// MessageId: ERROR_DS_DRA_PREEMPTED +// +// MessageText: +// +// The replication operation was preempted. +// +export const ERROR_DS_DRA_PREEMPTED = 8461; + +// +// MessageId: ERROR_DS_DRA_ABANDON_SYNC +// +// MessageText: +// +// The replication synchronization attempt was abandoned because of a lack of updates. +// +export const ERROR_DS_DRA_ABANDON_SYNC = 8462; + +// +// MessageId: ERROR_DS_DRA_SHUTDOWN +// +// MessageText: +// +// The replication operation was terminated because the system is shutting down. +// +export const ERROR_DS_DRA_SHUTDOWN = 8463; + +// +// MessageId: ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET +// +// MessageText: +// +// Synchronization attempt failed because the destination DC is currently waiting to synchronize new partial attributes from source. This condition is normal if a recent schema change modified the partial attribute set. The destination partial attribute set is not a subset of source partial attribute set. +// +export const ERROR_DS_DRA_INCOMPATIBLE_PARTIAL_SET = 8464; + +// +// MessageId: ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA +// +// MessageText: +// +// The replication synchronization attempt failed because a master replica attempted to sync from a partial replica. +// +export const ERROR_DS_DRA_SOURCE_IS_PARTIAL_REPLICA = 8465; + +// +// MessageId: ERROR_DS_DRA_EXTN_CONNECTION_FAILED +// +// MessageText: +// +// The server specified for this replication operation was contacted, but that server was unable to contact an additional server needed to complete the operation. +// +export const ERROR_DS_DRA_EXTN_CONNECTION_FAILED = 8466; + +// +// MessageId: ERROR_DS_INSTALL_SCHEMA_MISMATCH +// +// MessageText: +// +// The version of the Active Directory schema of the source forest is not compatible with the version of Active Directory on this computer. +// +export const ERROR_DS_INSTALL_SCHEMA_MISMATCH = 8467; + +// +// MessageId: ERROR_DS_DUP_LINK_ID +// +// MessageText: +// +// Schema update failed: An attribute with the same link identifier already exists. +// +export const ERROR_DS_DUP_LINK_ID = 8468; + +// +// MessageId: ERROR_DS_NAME_ERROR_RESOLVING +// +// MessageText: +// +// Name translation: Generic processing error. +// +export const ERROR_DS_NAME_ERROR_RESOLVING = 8469; + +// +// MessageId: ERROR_DS_NAME_ERROR_NOT_FOUND +// +// MessageText: +// +// Name translation: Could not find the name or insufficient right to see name. +// +export const ERROR_DS_NAME_ERROR_NOT_FOUND = 8470; + +// +// MessageId: ERROR_DS_NAME_ERROR_NOT_UNIQUE +// +// MessageText: +// +// Name translation: Input name mapped to more than one output name. +// +export const ERROR_DS_NAME_ERROR_NOT_UNIQUE = 8471; + +// +// MessageId: ERROR_DS_NAME_ERROR_NO_MAPPING +// +// MessageText: +// +// Name translation: Input name found, but not the associated output format. +// +export const ERROR_DS_NAME_ERROR_NO_MAPPING = 8472; + +// +// MessageId: ERROR_DS_NAME_ERROR_DOMAIN_ONLY +// +// MessageText: +// +// Name translation: Unable to resolve completely, only the domain was found. +// +export const ERROR_DS_NAME_ERROR_DOMAIN_ONLY = 8473; + +// +// MessageId: ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING +// +// MessageText: +// +// Name translation: Unable to perform purely syntactical mapping at the client without going out to the wire. +// +export const ERROR_DS_NAME_ERROR_NO_SYNTACTICAL_MAPPING = 8474; + +// +// MessageId: ERROR_DS_WRONG_OM_OBJ_CLASS +// +// MessageText: +// +// The OM-Object-Class specified is incorrect for an attribute with the specified syntax. +// +export const ERROR_DS_WRONG_OM_OBJ_CLASS = 8476; + +// +// MessageId: ERROR_DS_DRA_REPL_PENDING +// +// MessageText: +// +// The replication request has been posted; waiting for reply. +// +export const ERROR_DS_DRA_REPL_PENDING = 8477; + +// +// MessageId: ERROR_DS_DS_REQUIRED +// +// MessageText: +// +// The requested operation requires a directory service, and none was available. +// +export const ERROR_DS_DS_REQUIRED = 8478; + +// +// MessageId: ERROR_DS_INVALID_LDAP_DISPLAY_NAME +// +// MessageText: +// +// The LDAP display name of the class or attribute contains non-ASCII characters. +// +export const ERROR_DS_INVALID_LDAP_DISPLAY_NAME = 8479; + +// +// MessageId: ERROR_DS_NON_BASE_SEARCH +// +// MessageText: +// +// The requested search operation is only supported for base searches. +// +export const ERROR_DS_NON_BASE_SEARCH = 8480; + +// +// MessageId: ERROR_DS_CANT_RETRIEVE_ATTS +// +// MessageText: +// +// The search failed to retrieve attributes from the database. +// +export const ERROR_DS_CANT_RETRIEVE_ATTS = 8481; + +// +// MessageId: ERROR_DS_BACKLINK_WITHOUT_LINK +// +// MessageText: +// +// The schema update operation tried to add a backward link attribute that has no corresponding forward link. +// +export const ERROR_DS_BACKLINK_WITHOUT_LINK = 8482; + +// +// MessageId: ERROR_DS_EPOCH_MISMATCH +// +// MessageText: +// +// Source and destination of a cross-domain move do not agree on the object's epoch number. Either source or destination does not have the latest version of the object. +// +export const ERROR_DS_EPOCH_MISMATCH = 8483; + +// +// MessageId: ERROR_DS_SRC_NAME_MISMATCH +// +// MessageText: +// +// Source and destination of a cross-domain move do not agree on the object's current name. Either source or destination does not have the latest version of the object. +// +export const ERROR_DS_SRC_NAME_MISMATCH = 8484; + +// +// MessageId: ERROR_DS_SRC_AND_DST_NC_IDENTICAL +// +// MessageText: +// +// Source and destination for the cross-domain move operation are identical. Caller should use local move operation instead of cross-domain move operation. +// +export const ERROR_DS_SRC_AND_DST_NC_IDENTICAL = 8485; + +// +// MessageId: ERROR_DS_DST_NC_MISMATCH +// +// MessageText: +// +// Source and destination for a cross-domain move are not in agreement on the naming contexts in the forest. Either source or destination does not have the latest version of the Partitions container. +// +export const ERROR_DS_DST_NC_MISMATCH = 8486; + +// +// MessageId: ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC +// +// MessageText: +// +// Destination of a cross-domain move is not authoritative for the destination naming context. +// +export const ERROR_DS_NOT_AUTHORITIVE_FOR_DST_NC = 8487; + +// +// MessageId: ERROR_DS_SRC_GUID_MISMATCH +// +// MessageText: +// +// Source and destination of a cross-domain move do not agree on the identity of the source object. Either source or destination does not have the latest version of the source object. +// +export const ERROR_DS_SRC_GUID_MISMATCH = 8488; + +// +// MessageId: ERROR_DS_CANT_MOVE_DELETED_OBJECT +// +// MessageText: +// +// Object being moved across-domains is already known to be deleted by the destination server. The source server does not have the latest version of the source object. +// +export const ERROR_DS_CANT_MOVE_DELETED_OBJECT = 8489; + +// +// MessageId: ERROR_DS_PDC_OPERATION_IN_PROGRESS +// +// MessageText: +// +// Another operation which requires exclusive access to the PDC FSMO is already in progress. +// +export const ERROR_DS_PDC_OPERATION_IN_PROGRESS = 8490; + +// +// MessageId: ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD +// +// MessageText: +// +// A cross-domain move operation failed such that two versions of the moved object exist - one each in the source and destination domains. The destination object needs to be removed to restore the system to a consistent state. +// +export const ERROR_DS_CROSS_DOMAIN_CLEANUP_REQD = 8491; + +// +// MessageId: ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION +// +// MessageText: +// +// This object may not be moved across domain boundaries either because cross-domain moves for this class are disallowed, or the object has some special characteristics, e.g.: trust account or restricted RID, which prevent its move. +// +export const ERROR_DS_ILLEGAL_XDOM_MOVE_OPERATION = 8492; + +// +// MessageId: ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS +// +// MessageText: +// +// Can't move objects with memberships across domain boundaries as once moved, this would violate the membership conditions of the account group. Remove the object from any account group memberships and retry. +// +export const ERROR_DS_CANT_WITH_ACCT_GROUP_MEMBERSHPS = 8493; + +// +// MessageId: ERROR_DS_NC_MUST_HAVE_NC_PARENT +// +// MessageText: +// +// A naming context head must be the immediate child of another naming context head, not of an interior node. +// +export const ERROR_DS_NC_MUST_HAVE_NC_PARENT = 8494; + +// +// MessageId: ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE +// +// MessageText: +// +// The directory cannot validate the proposed naming context name because it does not hold a replica of the naming context above the proposed naming context. Please ensure that the domain naming master role is held by a server that is configured as a global catalog server, and that the server is up to date with its replication partners. (Applies only to Windows 2000 Domain Naming masters) +// +export const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE = 8495; + +// +// MessageId: ERROR_DS_DST_DOMAIN_NOT_NATIVE +// +// MessageText: +// +// Destination domain must be in native mode. +// +export const ERROR_DS_DST_DOMAIN_NOT_NATIVE = 8496; + +// +// MessageId: ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER +// +// MessageText: +// +// The operation can not be performed because the server does not have an infrastructure container in the domain of interest. +// +export const ERROR_DS_MISSING_INFRASTRUCTURE_CONTAINER = 8497; + +// +// MessageId: ERROR_DS_CANT_MOVE_ACCOUNT_GROUP +// +// MessageText: +// +// Cross-domain move of non-empty account groups is not allowed. +// +export const ERROR_DS_CANT_MOVE_ACCOUNT_GROUP = 8498; + +// +// MessageId: ERROR_DS_CANT_MOVE_RESOURCE_GROUP +// +// MessageText: +// +// Cross-domain move of non-empty resource groups is not allowed. +// +export const ERROR_DS_CANT_MOVE_RESOURCE_GROUP = 8499; + +// +// MessageId: ERROR_DS_INVALID_SEARCH_FLAG +// +// MessageText: +// +// The search flags for the attribute are invalid. The ANR bit is valid only on attributes of Unicode or Teletex strings. +// +export const ERROR_DS_INVALID_SEARCH_FLAG = 8500; + +// +// MessageId: ERROR_DS_NO_TREE_DELETE_ABOVE_NC +// +// MessageText: +// +// Tree deletions starting at an object which has an NC head as a descendant are not allowed. +// +export const ERROR_DS_NO_TREE_DELETE_ABOVE_NC = 8501; + +// +// MessageId: ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE +// +// MessageText: +// +// The directory service failed to lock a tree in preparation for a tree deletion because the tree was in use. +// +export const ERROR_DS_COULDNT_LOCK_TREE_FOR_DELETE = 8502; + +// +// MessageId: ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE +// +// MessageText: +// +// The directory service failed to identify the list of objects to delete while attempting a tree deletion. +// +export const ERROR_DS_COULDNT_IDENTIFY_OBJECTS_FOR_TREE_DELETE = 8503; + +// +// MessageId: ERROR_DS_SAM_INIT_FAILURE +// +// MessageText: +// +// Security Accounts Manager initialization failed because of the following error: %1. +// Error Status: 0x%2. Click OK to shut down the system and reboot into Directory Services Restore Mode. Check the event log for detailed information. +// +export const ERROR_DS_SAM_INIT_FAILURE = 8504; + +// +// MessageId: ERROR_DS_SENSITIVE_GROUP_VIOLATION +// +// MessageText: +// +// Only an administrator can modify the membership list of an administrative group. +// +export const ERROR_DS_SENSITIVE_GROUP_VIOLATION = 8505; + +// +// MessageId: ERROR_DS_CANT_MOD_PRIMARYGROUPID +// +// MessageText: +// +// Cannot change the primary group ID of a domain controller account. +// +export const ERROR_DS_CANT_MOD_PRIMARYGROUPID = 8506; + +// +// MessageId: ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD +// +// MessageText: +// +// An attempt is made to modify the base schema. +// +export const ERROR_DS_ILLEGAL_BASE_SCHEMA_MOD = 8507; + +// +// MessageId: ERROR_DS_NONSAFE_SCHEMA_CHANGE +// +// MessageText: +// +// Adding a new mandatory attribute to an existing class, deleting a mandatory attribute from an existing class, or adding an optional attribute to the special class Top that is not a backlink attribute (directly or through inheritance, for example, by adding or deleting an auxiliary class) is not allowed. +// +export const ERROR_DS_NONSAFE_SCHEMA_CHANGE = 8508; + +// +// MessageId: ERROR_DS_SCHEMA_UPDATE_DISALLOWED +// +// MessageText: +// +// Schema update is not allowed on this DC because the DC is not the schema FSMO Role Owner. +// +export const ERROR_DS_SCHEMA_UPDATE_DISALLOWED = 8509; + +// +// MessageId: ERROR_DS_CANT_CREATE_UNDER_SCHEMA +// +// MessageText: +// +// An object of this class cannot be created under the schema container. You can only create attribute-schema and class-schema objects under the schema container. +// +export const ERROR_DS_CANT_CREATE_UNDER_SCHEMA = 8510; + +// +// MessageId: ERROR_DS_INSTALL_NO_SRC_SCH_VERSION +// +// MessageText: +// +// The replica/child install failed to get the objectVersion attribute on the schema container on the source DC. Either the attribute is missing on the schema container or the credentials supplied do not have permission to read it. +// +export const ERROR_DS_INSTALL_NO_SRC_SCH_VERSION = 8511; + +// +// MessageId: ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE +// +// MessageText: +// +// The replica/child install failed to read the objectVersion attribute in the SCHEMA section of the file schema.ini in the system32 directory. +// +export const ERROR_DS_INSTALL_NO_SCH_VERSION_IN_INIFILE = 8512; + +// +// MessageId: ERROR_DS_INVALID_GROUP_TYPE +// +// MessageText: +// +// The specified group type is invalid. +// +export const ERROR_DS_INVALID_GROUP_TYPE = 8513; + +// +// MessageId: ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN +// +// MessageText: +// +// You cannot nest global groups in a mixed domain if the group is security-enabled. +// +export const ERROR_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN = 8514; + +// +// MessageId: ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN +// +// MessageText: +// +// You cannot nest local groups in a mixed domain if the group is security-enabled. +// +export const ERROR_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN = 8515; + +// +// MessageId: ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER +// +// MessageText: +// +// A global group cannot have a local group as a member. +// +export const ERROR_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER = 8516; + +// +// MessageId: ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER +// +// MessageText: +// +// A global group cannot have a universal group as a member. +// +export const ERROR_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER = 8517; + +// +// MessageId: ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER +// +// MessageText: +// +// A universal group cannot have a local group as a member. +// +export const ERROR_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER = 8518; + +// +// MessageId: ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER +// +// MessageText: +// +// A global group cannot have a cross-domain member. +// +export const ERROR_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER = 8519; + +// +// MessageId: ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER +// +// MessageText: +// +// A local group cannot have another cross domain local group as a member. +// +export const ERROR_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER = 8520; + +// +// MessageId: ERROR_DS_HAVE_PRIMARY_MEMBERS +// +// MessageText: +// +// A group with primary members cannot change to a security-disabled group. +// +export const ERROR_DS_HAVE_PRIMARY_MEMBERS = 8521; + +// +// MessageId: ERROR_DS_STRING_SD_CONVERSION_FAILED +// +// MessageText: +// +// The schema cache load failed to convert the string default SD on a class-schema object. +// +export const ERROR_DS_STRING_SD_CONVERSION_FAILED = 8522; + +// +// MessageId: ERROR_DS_NAMING_MASTER_GC +// +// MessageText: +// +// Only DSAs configured to be Global Catalog servers should be allowed to hold the Domain Naming Master FSMO role. (Applies only to Windows 2000 servers) +// +export const ERROR_DS_NAMING_MASTER_GC = 8523; + +// +// MessageId: ERROR_DS_DNS_LOOKUP_FAILURE +// +// MessageText: +// +// The DSA operation is unable to proceed because of a DNS lookup failure. +// +export const ERROR_DS_DNS_LOOKUP_FAILURE = 8524; + +// +// MessageId: ERROR_DS_COULDNT_UPDATE_SPNS +// +// MessageText: +// +// While processing a change to the DNS Host Name for an object, the Service Principal Name values could not be kept in sync. +// +export const ERROR_DS_COULDNT_UPDATE_SPNS = 8525; + +// +// MessageId: ERROR_DS_CANT_RETRIEVE_SD +// +// MessageText: +// +// The Security Descriptor attribute could not be read. +// +export const ERROR_DS_CANT_RETRIEVE_SD = 8526; + +// +// MessageId: ERROR_DS_KEY_NOT_UNIQUE +// +// MessageText: +// +// The object requested was not found, but an object with that key was found. +// +export const ERROR_DS_KEY_NOT_UNIQUE = 8527; + +// +// MessageId: ERROR_DS_WRONG_LINKED_ATT_SYNTAX +// +// MessageText: +// +// The syntax of the linked attribute being added is incorrect. Forward links can only have syntax 2.5.5.1, 2.5.5.7, and 2.5.5.14, and backlinks can only have syntax 2.5.5.1 +// +export const ERROR_DS_WRONG_LINKED_ATT_SYNTAX = 8528; + +// +// MessageId: ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD +// +// MessageText: +// +// Security Account Manager needs to get the boot password. +// +export const ERROR_DS_SAM_NEED_BOOTKEY_PASSWORD = 8529; + +// +// MessageId: ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY +// +// MessageText: +// +// Security Account Manager needs to get the boot key from floppy disk. +// +export const ERROR_DS_SAM_NEED_BOOTKEY_FLOPPY = 8530; + +// +// MessageId: ERROR_DS_CANT_START +// +// MessageText: +// +// Directory Service cannot start. +// +export const ERROR_DS_CANT_START = 8531; + +// +// MessageId: ERROR_DS_INIT_FAILURE +// +// MessageText: +// +// Directory Services could not start. +// +export const ERROR_DS_INIT_FAILURE = 8532; + +// +// MessageId: ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION +// +// MessageText: +// +// The connection between client and server requires packet privacy or better. +// +export const ERROR_DS_NO_PKT_PRIVACY_ON_CONNECTION = 8533; + +// +// MessageId: ERROR_DS_SOURCE_DOMAIN_IN_FOREST +// +// MessageText: +// +// The source domain may not be in the same forest as destination. +// +export const ERROR_DS_SOURCE_DOMAIN_IN_FOREST = 8534; + +// +// MessageId: ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST +// +// MessageText: +// +// The destination domain must be in the forest. +// +export const ERROR_DS_DESTINATION_DOMAIN_NOT_IN_FOREST = 8535; + +// +// MessageId: ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED +// +// MessageText: +// +// The operation requires that destination domain auditing be enabled. +// +export const ERROR_DS_DESTINATION_AUDITING_NOT_ENABLED = 8536; + +// +// MessageId: ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN +// +// MessageText: +// +// The operation couldn't locate a DC for the source domain. +// +export const ERROR_DS_CANT_FIND_DC_FOR_SRC_DOMAIN = 8537; + +// +// MessageId: ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER +// +// MessageText: +// +// The source object must be a group or user. +// +export const ERROR_DS_SRC_OBJ_NOT_GROUP_OR_USER = 8538; + +// +// MessageId: ERROR_DS_SRC_SID_EXISTS_IN_FOREST +// +// MessageText: +// +// The source object's SID already exists in destination forest. +// +export const ERROR_DS_SRC_SID_EXISTS_IN_FOREST = 8539; + +// +// MessageId: ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH +// +// MessageText: +// +// The source and destination object must be of the same type. +// +export const ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH = 8540; + +// +// MessageId: ERROR_SAM_INIT_FAILURE +// +// MessageText: +// +// Security Accounts Manager initialization failed because of the following error: %1. +// Error Status: 0x%2. Click OK to shut down the system and reboot into Safe Mode. Check the event log for detailed information. +// +export const ERROR_SAM_INIT_FAILURE = 8541; + +// +// MessageId: ERROR_DS_DRA_SCHEMA_INFO_SHIP +// +// MessageText: +// +// Schema information could not be included in the replication request. +// +export const ERROR_DS_DRA_SCHEMA_INFO_SHIP = 8542; + +// +// MessageId: ERROR_DS_DRA_SCHEMA_CONFLICT +// +// MessageText: +// +// The replication operation could not be completed due to a schema incompatibility. +// +export const ERROR_DS_DRA_SCHEMA_CONFLICT = 8543; + +// +// MessageId: ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT +// +// MessageText: +// +// The replication operation could not be completed due to a previous schema incompatibility. +// +export const ERROR_DS_DRA_EARLIER_SCHEMA_CONFLICT = 8544; + +// +// MessageId: ERROR_DS_DRA_OBJ_NC_MISMATCH +// +// MessageText: +// +// The replication update could not be applied because either the source or the destination has not yet received information regarding a recent cross-domain move operation. +// +export const ERROR_DS_DRA_OBJ_NC_MISMATCH = 8545; + +// +// MessageId: ERROR_DS_NC_STILL_HAS_DSAS +// +// MessageText: +// +// The requested domain could not be deleted because there exist domain controllers that still host this domain. +// +export const ERROR_DS_NC_STILL_HAS_DSAS = 8546; + +// +// MessageId: ERROR_DS_GC_REQUIRED +// +// MessageText: +// +// The requested operation can be performed only on a global catalog server. +// +export const ERROR_DS_GC_REQUIRED = 8547; + +// +// MessageId: ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY +// +// MessageText: +// +// A local group can only be a member of other local groups in the same domain. +// +export const ERROR_DS_LOCAL_MEMBER_OF_LOCAL_ONLY = 8548; + +// +// MessageId: ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS +// +// MessageText: +// +// Foreign security principals cannot be members of universal groups. +// +export const ERROR_DS_NO_FPO_IN_UNIVERSAL_GROUPS = 8549; + +// +// MessageId: ERROR_DS_CANT_ADD_TO_GC +// +// MessageText: +// +// The attribute is not allowed to be replicated to the GC because of security reasons. +// +export const ERROR_DS_CANT_ADD_TO_GC = 8550; + +// +// MessageId: ERROR_DS_NO_CHECKPOINT_WITH_PDC +// +// MessageText: +// +// The checkpoint with the PDC could not be taken because there too many modifications being processed currently. +// +export const ERROR_DS_NO_CHECKPOINT_WITH_PDC = 8551; + +// +// MessageId: ERROR_DS_SOURCE_AUDITING_NOT_ENABLED +// +// MessageText: +// +// The operation requires that source domain auditing be enabled. +// +export const ERROR_DS_SOURCE_AUDITING_NOT_ENABLED = 8552; + +// +// MessageId: ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC +// +// MessageText: +// +// Security principal objects can only be created inside domain naming contexts. +// +export const ERROR_DS_CANT_CREATE_IN_NONDOMAIN_NC = 8553; + +// +// MessageId: ERROR_DS_INVALID_NAME_FOR_SPN +// +// MessageText: +// +// A Service Principal Name (SPN) could not be export constructed because the provided hostname is not in the necessary format. +// +export const ERROR_DS_INVALID_NAME_FOR_SPN = 8554; + +// +// MessageId: ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS +// +// MessageText: +// +// A Filter was passed that uses export constructed attributes. +// +export const ERROR_DS_FILTER_USES_CONTRUCTED_ATTRS = 8555; + +// +// MessageId: ERROR_DS_UNICODEPWD_NOT_IN_QUOTES +// +// MessageText: +// +// The unicodePwd attribute value must be enclosed in double quotes. +// +export const ERROR_DS_UNICODEPWD_NOT_IN_QUOTES = 8556; + +// +// MessageId: ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED +// +// MessageText: +// +// Your computer could not be joined to the domain. You have exceeded the maximum number of computer accounts you are allowed to create in this domain. Contact your system administrator to have this limit reset or increased. +// +export const ERROR_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED = 8557; + +// +// MessageId: ERROR_DS_MUST_BE_RUN_ON_DST_DC +// +// MessageText: +// +// For security reasons, the operation must be run on the destination DC. +// +export const ERROR_DS_MUST_BE_RUN_ON_DST_DC = 8558; + +// +// MessageId: ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER +// +// MessageText: +// +// For security reasons, the source DC must be NT4SP4 or greater. +// +export const ERROR_DS_SRC_DC_MUST_BE_SP4_OR_GREATER = 8559; + +// +// MessageId: ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ +// +// MessageText: +// +// Critical Directory Service System objects cannot be deleted during tree delete operations. The tree delete may have been partially performed. +// +export const ERROR_DS_CANT_TREE_DELETE_CRITICAL_OBJ = 8560; + +// +// MessageId: ERROR_DS_INIT_FAILURE_CONSOLE +// +// MessageText: +// +// Directory Services could not start because of the following error: %1. +// Error Status: 0x%2. Please click OK to shutdown the system. You can use the recovery console to diagnose the system further. +// +export const ERROR_DS_INIT_FAILURE_CONSOLE = 8561; + +// +// MessageId: ERROR_DS_SAM_INIT_FAILURE_CONSOLE +// +// MessageText: +// +// Security Accounts Manager initialization failed because of the following error: %1. +// Error Status: 0x%2. Please click OK to shutdown the system. You can use the recovery console to diagnose the system further. +// +export const ERROR_DS_SAM_INIT_FAILURE_CONSOLE = 8562; + +// +// MessageId: ERROR_DS_FOREST_VERSION_TOO_HIGH +// +// MessageText: +// +// The version of the operating system installed is incompatible with the current forest functional level. You must upgrade to a new version of the operating system before this server can become a domain controller in this forest. +// +export const ERROR_DS_FOREST_VERSION_TOO_HIGH = 8563; + +// +// MessageId: ERROR_DS_DOMAIN_VERSION_TOO_HIGH +// +// MessageText: +// +// The version of the operating system installed is incompatible with the current domain functional level. You must upgrade to a new version of the operating system before this server can become a domain controller in this domain. +// +export const ERROR_DS_DOMAIN_VERSION_TOO_HIGH = 8564; + +// +// MessageId: ERROR_DS_FOREST_VERSION_TOO_LOW +// +// MessageText: +// +// The version of the operating system installed on this server no longer supports the current forest functional level. You must raise the forest functional level before this server can become a domain controller in this forest. +// +export const ERROR_DS_FOREST_VERSION_TOO_LOW = 8565; + +// +// MessageId: ERROR_DS_DOMAIN_VERSION_TOO_LOW +// +// MessageText: +// +// The version of the operating system installed on this server no longer supports the current domain functional level. You must raise the domain functional level before this server can become a domain controller in this domain. +// +export const ERROR_DS_DOMAIN_VERSION_TOO_LOW = 8566; + +// +// MessageId: ERROR_DS_INCOMPATIBLE_VERSION +// +// MessageText: +// +// The version of the operating system installed on this server is incompatible with the functional level of the domain or forest. +// +export const ERROR_DS_INCOMPATIBLE_VERSION = 8567; + +// +// MessageId: ERROR_DS_LOW_DSA_VERSION +// +// MessageText: +// +// The functional level of the domain (or forest) cannot be raised to the requested value, because there exist one or more domain controllers in the domain (or forest) that are at a lower incompatible functional level. +// +export const ERROR_DS_LOW_DSA_VERSION = 8568; + +// +// MessageId: ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN +// +// MessageText: +// +// The forest functional level cannot be raised to the requested value since one or more domains are still in mixed domain mode. All domains in the forest must be in native mode, for you to raise the forest functional level. +// +export const ERROR_DS_NO_BEHAVIOR_VERSION_IN_MIXEDDOMAIN = 8569; + +// +// MessageId: ERROR_DS_NOT_SUPPORTED_SORT_ORDER +// +// MessageText: +// +// The sort order requested is not supported. +// +export const ERROR_DS_NOT_SUPPORTED_SORT_ORDER = 8570; + +// +// MessageId: ERROR_DS_NAME_NOT_UNIQUE +// +// MessageText: +// +// The requested name already exists as a unique identifier. +// +export const ERROR_DS_NAME_NOT_UNIQUE = 8571; + +// +// MessageId: ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 +// +// MessageText: +// +// The machine account was created pre-NT4. The account needs to be recreated. +// +export const ERROR_DS_MACHINE_ACCOUNT_CREATED_PRENT4 = 8572; + +// +// MessageId: ERROR_DS_OUT_OF_VERSION_STORE +// +// MessageText: +// +// The database is out of version store. +// +export const ERROR_DS_OUT_OF_VERSION_STORE = 8573; + +// +// MessageId: ERROR_DS_INCOMPATIBLE_CONTROLS_USED +// +// MessageText: +// +// Unable to continue operation because multiple conflicting controls were used. +// +export const ERROR_DS_INCOMPATIBLE_CONTROLS_USED = 8574; + +// +// MessageId: ERROR_DS_NO_REF_DOMAIN +// +// MessageText: +// +// Unable to find a valid security descriptor reference domain for this partition. +// +export const ERROR_DS_NO_REF_DOMAIN = 8575; + +// +// MessageId: ERROR_DS_RESERVED_LINK_ID +// +// MessageText: +// +// Schema update failed: The link identifier is reserved. +// +export const ERROR_DS_RESERVED_LINK_ID = 8576; + +// +// MessageId: ERROR_DS_LINK_ID_NOT_AVAILABLE +// +// MessageText: +// +// Schema update failed: There are no link identifiers available. +// +export const ERROR_DS_LINK_ID_NOT_AVAILABLE = 8577; + +// +// MessageId: ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER +// +// MessageText: +// +// An account group can not have a universal group as a member. +// +export const ERROR_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER = 8578; + +// +// MessageId: ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE +// +// MessageText: +// +// Rename or move operations on naming context heads or read-only objects are not allowed. +// +export const ERROR_DS_MODIFYDN_DISALLOWED_BY_INSTANCE_TYPE = 8579; + +// +// MessageId: ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC +// +// MessageText: +// +// Move operations on objects in the schema naming context are not allowed. +// +export const ERROR_DS_NO_OBJECT_MOVE_IN_SCHEMA_NC = 8580; + +// +// MessageId: ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG +// +// MessageText: +// +// A system flag has been set on the object and does not allow the object to be moved or renamed. +// +export const ERROR_DS_MODIFYDN_DISALLOWED_BY_FLAG = 8581; + +// +// MessageId: ERROR_DS_MODIFYDN_WRONG_GRANDPARENT +// +// MessageText: +// +// This object is not allowed to change its grandparent container. Moves are not forbidden on this object, but are restricted to sibling containers. +// +export const ERROR_DS_MODIFYDN_WRONG_GRANDPARENT = 8582; + +// +// MessageId: ERROR_DS_NAME_ERROR_TRUST_REFERRAL +// +// MessageText: +// +// Unable to resolve completely, a referral to another forest is generated. +// +export const ERROR_DS_NAME_ERROR_TRUST_REFERRAL = 8583; + +// +// MessageId: ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER +// +// MessageText: +// +// The requested action is not supported on standard server. +// +export const ERROR_NOT_SUPPORTED_ON_STANDARD_SERVER = 8584; + +// +// MessageId: ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD +// +// MessageText: +// +// Could not access a partition of the Active Directory located on a remote server. Make sure at least one server is running for the partition in question. +// +export const ERROR_DS_CANT_ACCESS_REMOTE_PART_OF_AD = 8585; + +// +// MessageId: ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 +// +// MessageText: +// +// The directory cannot validate the proposed naming context (or partition) name because it does not hold a replica nor can it contact a replica of the naming context above the proposed naming context. Please ensure that the parent naming context is properly registered in DNS, and at least one replica of this naming context is reachable by the Domain Naming master. +// +export const ERROR_DS_CR_IMPOSSIBLE_TO_VALIDATE_V2 = 8586; + +// +// MessageId: ERROR_DS_THREAD_LIMIT_EXCEEDED +// +// MessageText: +// +// The thread limit for this request was exceeded. +// +export const ERROR_DS_THREAD_LIMIT_EXCEEDED = 8587; + +// +// MessageId: ERROR_DS_NOT_CLOSEST +// +// MessageText: +// +// The Global catalog server is not in the closest site. +// +export const ERROR_DS_NOT_CLOSEST = 8588; + +// +// MessageId: ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF +// +// MessageText: +// +// The DS cannot derive a service principal name (SPN) with which to mutually authenticate the target server because the corresponding server object in the local DS database has no serverReference attribute. +// +export const ERROR_DS_CANT_DERIVE_SPN_WITHOUT_SERVER_REF = 8589; + +// +// MessageId: ERROR_DS_SINGLE_USER_MODE_FAILED +// +// MessageText: +// +// The Directory Service failed to enter single user mode. +// +export const ERROR_DS_SINGLE_USER_MODE_FAILED = 8590; + +// +// MessageId: ERROR_DS_NTDSCRIPT_SYNTAX_ERROR +// +// MessageText: +// +// The Directory Service cannot parse the script because of a syntax error. +// +export const ERROR_DS_NTDSCRIPT_SYNTAX_ERROR = 8591; + +// +// MessageId: ERROR_DS_NTDSCRIPT_PROCESS_ERROR +// +// MessageText: +// +// The Directory Service cannot process the script because of an error. +// +export const ERROR_DS_NTDSCRIPT_PROCESS_ERROR = 8592; + +// +// MessageId: ERROR_DS_DIFFERENT_REPL_EPOCHS +// +// MessageText: +// +// The directory service cannot perform the requested operation because the servers +// involved are of different replication epochs (which is usually related to a +// domain rename that is in progress). +// +export const ERROR_DS_DIFFERENT_REPL_EPOCHS = 8593; + +// +// MessageId: ERROR_DS_DRS_EXTENSIONS_CHANGED +// +// MessageText: +// +// The directory service binding must be renegotiated due to a change in the server +// extensions information. +// +export const ERROR_DS_DRS_EXTENSIONS_CHANGED = 8594; + +// +// MessageId: ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR +// +// MessageText: +// +// Operation not allowed on a disabled cross ref. +// +export const ERROR_DS_REPLICA_SET_CHANGE_NOT_ALLOWED_ON_DISABLED_CR = 8595; + +// +// MessageId: ERROR_DS_NO_MSDS_INTID +// +// MessageText: +// +// Schema update failed: No values for msDS-IntId are available. +// +export const ERROR_DS_NO_MSDS_INTID = 8596; + +// +// MessageId: ERROR_DS_DUP_MSDS_INTID +// +// MessageText: +// +// Schema update failed: Duplicate msDS-INtId. Retry the operation. +// +export const ERROR_DS_DUP_MSDS_INTID = 8597; + +// +// MessageId: ERROR_DS_EXISTS_IN_RDNATTID +// +// MessageText: +// +// Schema deletion failed: attribute is used in rDNAttID. +// +export const ERROR_DS_EXISTS_IN_RDNATTID = 8598; + +// +// MessageId: ERROR_DS_AUTHORIZATION_FAILED +// +// MessageText: +// +// The directory service failed to authorize the request. +// +export const ERROR_DS_AUTHORIZATION_FAILED = 8599; + +// +// MessageId: ERROR_DS_INVALID_SCRIPT +// +// MessageText: +// +// The Directory Service cannot process the script because it is invalid. +// +export const ERROR_DS_INVALID_SCRIPT = 8600; + +// +// MessageId: ERROR_DS_REMOTE_CROSSREF_OP_FAILED +// +// MessageText: +// +// The remote create cross reference operation failed on the Domain Naming Master FSMO. The operation's error is in the extended data. +// +export const ERROR_DS_REMOTE_CROSSREF_OP_FAILED = 8601; + +// +// MessageId: ERROR_DS_CROSS_REF_BUSY +// +// MessageText: +// +// A cross reference is in use locally with the same name. +// +export const ERROR_DS_CROSS_REF_BUSY = 8602; + +// +// MessageId: ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN +// +// MessageText: +// +// The DS cannot derive a service principal name (SPN) with which to mutually authenticate the target server because the server's domain has been deleted from the forest. +// +export const ERROR_DS_CANT_DERIVE_SPN_FOR_DELETED_DOMAIN = 8603; + +// +// MessageId: ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC +// +// MessageText: +// +// Writeable NCs prevent this DC from demoting. +// +export const ERROR_DS_CANT_DEMOTE_WITH_WRITEABLE_NC = 8604; + +// +// MessageId: ERROR_DS_DUPLICATE_ID_FOUND +// +// MessageText: +// +// The requested object has a non-unique identifier and cannot be retrieved. +// +export const ERROR_DS_DUPLICATE_ID_FOUND = 8605; + +// +// MessageId: ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT +// +// MessageText: +// +// Insufficient attributes were given to create an object. This object may not exist because it may have been deleted and already garbage collected. +// +export const ERROR_DS_INSUFFICIENT_ATTR_TO_CREATE_OBJECT = 8606; + +// +// MessageId: ERROR_DS_GROUP_CONVERSION_ERROR +// +// MessageText: +// +// The group cannot be converted due to attribute restrictions on the requested group type. +// +export const ERROR_DS_GROUP_CONVERSION_ERROR = 8607; + +// +// MessageId: ERROR_DS_CANT_MOVE_APP_BASIC_GROUP +// +// MessageText: +// +// Cross-domain move of non-empty basic application groups is not allowed. +// +export const ERROR_DS_CANT_MOVE_APP_BASIC_GROUP = 8608; + +// +// MessageId: ERROR_DS_CANT_MOVE_APP_QUERY_GROUP +// +// MessageText: +// +// Cross-domain move of non-empty query based application groups is not allowed. +// +export const ERROR_DS_CANT_MOVE_APP_QUERY_GROUP = 8609; + +// +// MessageId: ERROR_DS_ROLE_NOT_VERIFIED +// +// MessageText: +// +// The FSMO role ownership could not be verified because its directory partition has not replicated successfully with atleast one replication partner. +// +export const ERROR_DS_ROLE_NOT_VERIFIED = 8610; + +// +// MessageId: ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL +// +// MessageText: +// +// The target container for a redirection of a well known object container cannot already be a special container. +// +export const ERROR_DS_WKO_CONTAINER_CANNOT_BE_SPECIAL = 8611; + +// +// MessageId: ERROR_DS_DOMAIN_RENAME_IN_PROGRESS +// +// MessageText: +// +// The Directory Service cannot perform the requested operation because a domain rename operation is in progress. +// +export const ERROR_DS_DOMAIN_RENAME_IN_PROGRESS = 8612; + +// +// MessageId: ERROR_DS_EXISTING_AD_CHILD_NC +// +// MessageText: +// +// The Active Directory detected an Active Directory child partition below the +// requested new partition name. The Active Directory's partition hierarchy must +// be created in a top down method. +// +export const ERROR_DS_EXISTING_AD_CHILD_NC = 8613; + +// +// MessageId: ERROR_DS_REPL_LIFETIME_EXCEEDED +// +// MessageText: +// +// The Active Directory cannot replicate with this server because the time since the last replication with this server has exceeded the tombstone lifetime. +// +export const ERROR_DS_REPL_LIFETIME_EXCEEDED = 8614; + +// +// MessageId: ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER +// +// MessageText: +// +// The requested operation is not allowed on an object under the system container. +// +export const ERROR_DS_DISALLOWED_IN_SYSTEM_CONTAINER = 8615; + +// +// MessageId: ERROR_DS_LDAP_SEND_QUEUE_FULL +// +// MessageText: +// +// The LDAP servers network send queue has filled up because the client is not +// processing the results of it's requests fast enough. No more requests will +// be processed until the client catches up. If the client does not catch up +// then it will be disconnected. +// +export const ERROR_DS_LDAP_SEND_QUEUE_FULL = 8616; + +// +// MessageId: ERROR_DS_DRA_OUT_SCHEDULE_WINDOW +// +// MessageText: +// +// The scheduled replication did not take place because the system was too busy to execute the request within the schedule window. The replication queue is overloaded. Consider reducing the number of partners or decreasing the scheduled replication frequency. +// +export const ERROR_DS_DRA_OUT_SCHEDULE_WINDOW = 8617; + +/////////////////////////////////////////////////// +// / +// End of Active Directory Error Codes / +// / +// 8000 to 8999 / +/////////////////////////////////////////////////// + +/////////////////////////////////////////////////// +// // +// DNS Error Codes // +// // +// 9000 to 9999 // +/////////////////////////////////////////////////// + +// ============================= +// Facility DNS Error Messages +// ============================= + +// +// DNS response codes. +// + +export const DNS_ERROR_RESPONSE_CODES_BASE = 9000; + +// DNS_ERROR_RCODE_FORMAT_ERROR 0x00002329 +// +// MessageId: DNS_ERROR_RCODE_FORMAT_ERROR +// +// MessageText: +// +// DNS server unable to interpret format. +// +export const DNS_ERROR_RCODE_FORMAT_ERROR = 9001; + +// DNS_ERROR_RCODE_SERVER_FAILURE 0x0000232a +// +// MessageId: DNS_ERROR_RCODE_SERVER_FAILURE +// +// MessageText: +// +// DNS server failure. +// +export const DNS_ERROR_RCODE_SERVER_FAILURE = 9002; + +// DNS_ERROR_RCODE_NAME_ERROR 0x0000232b +// +// MessageId: DNS_ERROR_RCODE_NAME_ERROR +// +// MessageText: +// +// DNS name does not exist. +// +export const DNS_ERROR_RCODE_NAME_ERROR = 9003; + +// DNS_ERROR_RCODE_NOT_IMPLEMENTED 0x0000232c +// +// MessageId: DNS_ERROR_RCODE_NOT_IMPLEMENTED +// +// MessageText: +// +// DNS request not supported by name server. +// +export const DNS_ERROR_RCODE_NOT_IMPLEMENTED = 9004; + +// DNS_ERROR_RCODE_REFUSED 0x0000232d +// +// MessageId: DNS_ERROR_RCODE_REFUSED +// +// MessageText: +// +// DNS operation refused. +// +export const DNS_ERROR_RCODE_REFUSED = 9005; + +// DNS_ERROR_RCODE_YXDOMAIN 0x0000232e +// +// MessageId: DNS_ERROR_RCODE_YXDOMAIN +// +// MessageText: +// +// DNS name that ought not exist, does exist. +// +export const DNS_ERROR_RCODE_YXDOMAIN = 9006; + +// DNS_ERROR_RCODE_YXRRSET 0x0000232f +// +// MessageId: DNS_ERROR_RCODE_YXRRSET +// +// MessageText: +// +// DNS RR set that ought not exist, does exist. +// +export const DNS_ERROR_RCODE_YXRRSET = 9007; + +// DNS_ERROR_RCODE_NXRRSET 0x00002330 +// +// MessageId: DNS_ERROR_RCODE_NXRRSET +// +// MessageText: +// +// DNS RR set that ought to exist, does not exist. +// +export const DNS_ERROR_RCODE_NXRRSET = 9008; + +// DNS_ERROR_RCODE_NOTAUTH 0x00002331 +// +// MessageId: DNS_ERROR_RCODE_NOTAUTH +// +// MessageText: +// +// DNS server not authoritative for zone. +// +export const DNS_ERROR_RCODE_NOTAUTH = 9009; + +// DNS_ERROR_RCODE_NOTZONE 0x00002332 +// +// MessageId: DNS_ERROR_RCODE_NOTZONE +// +// MessageText: +// +// DNS name in update or prereq is not in zone. +// +export const DNS_ERROR_RCODE_NOTZONE = 9010; + +// DNS_ERROR_RCODE_BADSIG 0x00002338 +// +// MessageId: DNS_ERROR_RCODE_BADSIG +// +// MessageText: +// +// DNS signature failed to verify. +// +export const DNS_ERROR_RCODE_BADSIG = 9016; + +// DNS_ERROR_RCODE_BADKEY 0x00002339 +// +// MessageId: DNS_ERROR_RCODE_BADKEY +// +// MessageText: +// +// DNS bad key. +// +export const DNS_ERROR_RCODE_BADKEY = 9017; + +// DNS_ERROR_RCODE_BADTIME 0x0000233a +// +// MessageId: DNS_ERROR_RCODE_BADTIME +// +// MessageText: +// +// DNS signature validity expired. +// +export const DNS_ERROR_RCODE_BADTIME = 9018; + +// +// Packet format +// + +export const DNS_ERROR_PACKET_FMT_BASE = 9500; + +// DNS_INFO_NO_RECORDS 0x0000251d +// +// MessageId: DNS_INFO_NO_RECORDS +// +// MessageText: +// +// No records found for given DNS query. +// +export const DNS_INFO_NO_RECORDS = 9501; + +// DNS_ERROR_BAD_PACKET 0x0000251e +// +// MessageId: DNS_ERROR_BAD_PACKET +// +// MessageText: +// +// Bad DNS packet. +// +export const DNS_ERROR_BAD_PACKET = 9502; + +// DNS_ERROR_NO_PACKET 0x0000251f +// +// MessageId: DNS_ERROR_NO_PACKET +// +// MessageText: +// +// No DNS packet. +// +export const DNS_ERROR_NO_PACKET = 9503; + +// DNS_ERROR_RCODE 0x00002520 +// +// MessageId: DNS_ERROR_RCODE +// +// MessageText: +// +// DNS error, check rcode. +// +export const DNS_ERROR_RCODE = 9504; + +// DNS_ERROR_UNSECURE_PACKET 0x00002521 +// +// MessageId: DNS_ERROR_UNSECURE_PACKET +// +// MessageText: +// +// Unsecured DNS packet. +// +export const DNS_ERROR_UNSECURE_PACKET = 9505; + +// +// General API errors +// + +// DNS_ERROR_INVALID_TYPE 0x0000254f +// +// MessageId: DNS_ERROR_INVALID_TYPE +// +// MessageText: +// +// Invalid DNS type. +// +export const DNS_ERROR_INVALID_TYPE = 9551; + +// DNS_ERROR_INVALID_IP_ADDRESS 0x00002550 +// +// MessageId: DNS_ERROR_INVALID_IP_ADDRESS +// +// MessageText: +// +// Invalid IP address. +// +export const DNS_ERROR_INVALID_IP_ADDRESS = 9552; + +// DNS_ERROR_INVALID_PROPERTY 0x00002551 +// +// MessageId: DNS_ERROR_INVALID_PROPERTY +// +// MessageText: +// +// Invalid property. +// +export const DNS_ERROR_INVALID_PROPERTY = 9553; + +// DNS_ERROR_TRY_AGAIN_LATER 0x00002552 +// +// MessageId: DNS_ERROR_TRY_AGAIN_LATER +// +// MessageText: +// +// Try DNS operation again later. +// +export const DNS_ERROR_TRY_AGAIN_LATER = 9554; + +// DNS_ERROR_NOT_UNIQUE 0x00002553 +// +// MessageId: DNS_ERROR_NOT_UNIQUE +// +// MessageText: +// +// Record for given name and type is not unique. +// +export const DNS_ERROR_NOT_UNIQUE = 9555; + +// DNS_ERROR_NON_RFC_NAME 0x00002554 +// +// MessageId: DNS_ERROR_NON_RFC_NAME +// +// MessageText: +// +// DNS name does not comply with RFC specifications. +// +export const DNS_ERROR_NON_RFC_NAME = 9556; + +// DNS_STATUS_FQDN 0x00002555 +// +// MessageId: DNS_STATUS_FQDN +// +// MessageText: +// +// DNS name is a fully-qualified DNS name. +// +export const DNS_STATUS_FQDN = 9557; + +// DNS_STATUS_DOTTED_NAME 0x00002556 +// +// MessageId: DNS_STATUS_DOTTED_NAME +// +// MessageText: +// +// DNS name is dotted (multi-label). +// +export const DNS_STATUS_DOTTED_NAME = 9558; + +// DNS_STATUS_SINGLE_PART_NAME 0x00002557 +// +// MessageId: DNS_STATUS_SINGLE_PART_NAME +// +// MessageText: +// +// DNS name is a single-part name. +// +export const DNS_STATUS_SINGLE_PART_NAME = 9559; + +// DNS_ERROR_INVALID_NAME_CHAR 0x00002558 +// +// MessageId: DNS_ERROR_INVALID_NAME_CHAR +// +// MessageText: +// +// DNS name contains an invalid character. +// +export const DNS_ERROR_INVALID_NAME_CHAR = 9560; + +// DNS_ERROR_NUMERIC_NAME 0x00002559 +// +// MessageId: DNS_ERROR_NUMERIC_NAME +// +// MessageText: +// +// DNS name is entirely numeric. +// +export const DNS_ERROR_NUMERIC_NAME = 9561; + +// DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER 0x0000255A +// +// MessageId: DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER +// +// MessageText: +// +// The operation requested is not permitted on a DNS root server. +// +export const DNS_ERROR_NOT_ALLOWED_ON_ROOT_SERVER = 9562; + +// DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION 0x0000255B +// +// MessageId: DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION +// +// MessageText: +// +// The record could not be created because this part of the DNS namespace has +// been delegated to another server. +// +export const DNS_ERROR_NOT_ALLOWED_UNDER_DELEGATION = 9563; + +// DNS_ERROR_CANNOT_FIND_ROOT_HINTS 0x0000255C +// +// MessageId: DNS_ERROR_CANNOT_FIND_ROOT_HINTS +// +// MessageText: +// +// The DNS server could not find a set of root hints. +// +export const DNS_ERROR_CANNOT_FIND_ROOT_HINTS = 9564; + +// DNS_ERROR_INCONSISTENT_ROOT_HINTS 0x0000255D +// +// MessageId: DNS_ERROR_INCONSISTENT_ROOT_HINTS +// +// MessageText: +// +// The DNS server found root hints but they were not consistent across +// all adapters. +// +export const DNS_ERROR_INCONSISTENT_ROOT_HINTS = 9565; + +// +// Zone errors +// + +export const DNS_ERROR_ZONE_BASE = 9600; + +// DNS_ERROR_ZONE_DOES_NOT_EXIST 0x00002581 +// +// MessageId: DNS_ERROR_ZONE_DOES_NOT_EXIST +// +// MessageText: +// +// DNS zone does not exist. +// +export const DNS_ERROR_ZONE_DOES_NOT_EXIST = 9601; + +// DNS_ERROR_NO_ZONE_INFO 0x00002582 +// +// MessageId: DNS_ERROR_NO_ZONE_INFO +// +// MessageText: +// +// DNS zone information not available. +// +export const DNS_ERROR_NO_ZONE_INFO = 9602; + +// DNS_ERROR_INVALID_ZONE_OPERATION 0x00002583 +// +// MessageId: DNS_ERROR_INVALID_ZONE_OPERATION +// +// MessageText: +// +// Invalid operation for DNS zone. +// +export const DNS_ERROR_INVALID_ZONE_OPERATION = 9603; + +// DNS_ERROR_ZONE_CONFIGURATION_ERROR 0x00002584 +// +// MessageId: DNS_ERROR_ZONE_CONFIGURATION_ERROR +// +// MessageText: +// +// Invalid DNS zone configuration. +// +export const DNS_ERROR_ZONE_CONFIGURATION_ERROR = 9604; + +// DNS_ERROR_ZONE_HAS_NO_SOA_RECORD 0x00002585 +// +// MessageId: DNS_ERROR_ZONE_HAS_NO_SOA_RECORD +// +// MessageText: +// +// DNS zone has no start of authority (SOA) record. +// +export const DNS_ERROR_ZONE_HAS_NO_SOA_RECORD = 9605; + +// DNS_ERROR_ZONE_HAS_NO_NS_RECORDS 0x00002586 +// +// MessageId: DNS_ERROR_ZONE_HAS_NO_NS_RECORDS +// +// MessageText: +// +// DNS zone has no Name Server (NS) record. +// +export const DNS_ERROR_ZONE_HAS_NO_NS_RECORDS = 9606; + +// DNS_ERROR_ZONE_LOCKED 0x00002587 +// +// MessageId: DNS_ERROR_ZONE_LOCKED +// +// MessageText: +// +// DNS zone is locked. +// +export const DNS_ERROR_ZONE_LOCKED = 9607; + +// DNS_ERROR_ZONE_CREATION_FAILED 0x00002588 +// +// MessageId: DNS_ERROR_ZONE_CREATION_FAILED +// +// MessageText: +// +// DNS zone creation failed. +// +export const DNS_ERROR_ZONE_CREATION_FAILED = 9608; + +// DNS_ERROR_ZONE_ALREADY_EXISTS 0x00002589 +// +// MessageId: DNS_ERROR_ZONE_ALREADY_EXISTS +// +// MessageText: +// +// DNS zone already exists. +// +export const DNS_ERROR_ZONE_ALREADY_EXISTS = 9609; + +// DNS_ERROR_AUTOZONE_ALREADY_EXISTS 0x0000258a +// +// MessageId: DNS_ERROR_AUTOZONE_ALREADY_EXISTS +// +// MessageText: +// +// DNS automatic zone already exists. +// +export const DNS_ERROR_AUTOZONE_ALREADY_EXISTS = 9610; + +// DNS_ERROR_INVALID_ZONE_TYPE 0x0000258b +// +// MessageId: DNS_ERROR_INVALID_ZONE_TYPE +// +// MessageText: +// +// Invalid DNS zone type. +// +export const DNS_ERROR_INVALID_ZONE_TYPE = 9611; + +// DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP 0x0000258c +// +// MessageId: DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP +// +// MessageText: +// +// Secondary DNS zone requires master IP address. +// +export const DNS_ERROR_SECONDARY_REQUIRES_MASTER_IP = 9612; + +// DNS_ERROR_ZONE_NOT_SECONDARY 0x0000258d +// +// MessageId: DNS_ERROR_ZONE_NOT_SECONDARY +// +// MessageText: +// +// DNS zone not secondary. +// +export const DNS_ERROR_ZONE_NOT_SECONDARY = 9613; + +// DNS_ERROR_NEED_SECONDARY_ADDRESSES 0x0000258e +// +// MessageId: DNS_ERROR_NEED_SECONDARY_ADDRESSES +// +// MessageText: +// +// Need secondary IP address. +// +export const DNS_ERROR_NEED_SECONDARY_ADDRESSES = 9614; + +// DNS_ERROR_WINS_INIT_FAILED 0x0000258f +// +// MessageId: DNS_ERROR_WINS_INIT_FAILED +// +// MessageText: +// +// WINS initialization failed. +// +export const DNS_ERROR_WINS_INIT_FAILED = 9615; + +// DNS_ERROR_NEED_WINS_SERVERS 0x00002590 +// +// MessageId: DNS_ERROR_NEED_WINS_SERVERS +// +// MessageText: +// +// Need WINS servers. +// +export const DNS_ERROR_NEED_WINS_SERVERS = 9616; + +// DNS_ERROR_NBSTAT_INIT_FAILED 0x00002591 +// +// MessageId: DNS_ERROR_NBSTAT_INIT_FAILED +// +// MessageText: +// +// NBTSTAT initialization call failed. +// +export const DNS_ERROR_NBSTAT_INIT_FAILED = 9617; + +// DNS_ERROR_SOA_DELETE_INVALID 0x00002592 +// +// MessageId: DNS_ERROR_SOA_DELETE_INVALID +// +// MessageText: +// +// Invalid delete of start of authority (SOA) +// +export const DNS_ERROR_SOA_DELETE_INVALID = 9618; + +// DNS_ERROR_FORWARDER_ALREADY_EXISTS 0x00002593 +// +// MessageId: DNS_ERROR_FORWARDER_ALREADY_EXISTS +// +// MessageText: +// +// A conditional forwarding zone already exists for that name. +// +export const DNS_ERROR_FORWARDER_ALREADY_EXISTS = 9619; + +// DNS_ERROR_ZONE_REQUIRES_MASTER_IP 0x00002594 +// +// MessageId: DNS_ERROR_ZONE_REQUIRES_MASTER_IP +// +// MessageText: +// +// This zone must be configured with one or more master DNS server IP addresses. +// +export const DNS_ERROR_ZONE_REQUIRES_MASTER_IP = 9620; + +// DNS_ERROR_ZONE_IS_SHUTDOWN 0x00002595 +// +// MessageId: DNS_ERROR_ZONE_IS_SHUTDOWN +// +// MessageText: +// +// The operation cannot be performed because this zone is shutdown. +// +export const DNS_ERROR_ZONE_IS_SHUTDOWN = 9621; + +// +// Datafile errors +// + +// DNS 0x000025b3 +// +// MessageId: DNS_ERROR_PRIMARY_REQUIRES_DATAFILE +// +// MessageText: +// +// Primary DNS zone requires datafile. +// +export const DNS_ERROR_PRIMARY_REQUIRES_DATAFILE = 9651; + +// DNS 0x000025b4 +// +// MessageId: DNS_ERROR_INVALID_DATAFILE_NAME +// +// MessageText: +// +// Invalid datafile name for DNS zone. +// +export const DNS_ERROR_INVALID_DATAFILE_NAME = 9652; + +// DNS 0x000025b5 +// +// MessageId: DNS_ERROR_DATAFILE_OPEN_FAILURE +// +// MessageText: +// +// Failed to open datafile for DNS zone. +// +export const DNS_ERROR_DATAFILE_OPEN_FAILURE = 9653; + +// DNS 0x000025b6 +// +// MessageId: DNS_ERROR_FILE_WRITEBACK_FAILED +// +// MessageText: +// +// Failed to write datafile for DNS zone. +// +export const DNS_ERROR_FILE_WRITEBACK_FAILED = 9654; + +// DNS 0x000025b7 +// +// MessageId: DNS_ERROR_DATAFILE_PARSING +// +// MessageText: +// +// Failure while reading datafile for DNS zone. +// +export const DNS_ERROR_DATAFILE_PARSING = 9655; + +// +// Database errors +// + +export const DNS_ERROR_DATABASE_BASE = 9700; + +// DNS_ERROR_RECORD_DOES_NOT_EXIST 0x000025e5 +// +// MessageId: DNS_ERROR_RECORD_DOES_NOT_EXIST +// +// MessageText: +// +// DNS record does not exist. +// +export const DNS_ERROR_RECORD_DOES_NOT_EXIST = 9701; + +// DNS_ERROR_RECORD_FORMAT 0x000025e6 +// +// MessageId: DNS_ERROR_RECORD_FORMAT +// +// MessageText: +// +// DNS record format error. +// +export const DNS_ERROR_RECORD_FORMAT = 9702; + +// DNS_ERROR_NODE_CREATION_FAILED 0x000025e7 +// +// MessageId: DNS_ERROR_NODE_CREATION_FAILED +// +// MessageText: +// +// Node creation failure in DNS. +// +export const DNS_ERROR_NODE_CREATION_FAILED = 9703; + +// DNS_ERROR_UNKNOWN_RECORD_TYPE 0x000025e8 +// +// MessageId: DNS_ERROR_UNKNOWN_RECORD_TYPE +// +// MessageText: +// +// Unknown DNS record type. +// +export const DNS_ERROR_UNKNOWN_RECORD_TYPE = 9704; + +// DNS_ERROR_RECORD_TIMED_OUT 0x000025e9 +// +// MessageId: DNS_ERROR_RECORD_TIMED_OUT +// +// MessageText: +// +// DNS record timed out. +// +export const DNS_ERROR_RECORD_TIMED_OUT = 9705; + +// DNS_ERROR_NAME_NOT_IN_ZONE 0x000025ea +// +// MessageId: DNS_ERROR_NAME_NOT_IN_ZONE +// +// MessageText: +// +// Name not in DNS zone. +// +export const DNS_ERROR_NAME_NOT_IN_ZONE = 9706; + +// DNS_ERROR_CNAME_LOOP 0x000025eb +// +// MessageId: DNS_ERROR_CNAME_LOOP +// +// MessageText: +// +// CNAME loop detected. +// +export const DNS_ERROR_CNAME_LOOP = 9707; + +// DNS_ERROR_NODE_IS_CNAME 0x000025ec +// +// MessageId: DNS_ERROR_NODE_IS_CNAME +// +// MessageText: +// +// Node is a CNAME DNS record. +// +export const DNS_ERROR_NODE_IS_CNAME = 9708; + +// DNS_ERROR_CNAME_COLLISION 0x000025ed +// +// MessageId: DNS_ERROR_CNAME_COLLISION +// +// MessageText: +// +// A CNAME record already exists for given name. +// +export const DNS_ERROR_CNAME_COLLISION = 9709; + +// DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT 0x000025ee +// +// MessageId: DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT +// +// MessageText: +// +// Record only at DNS zone root. +// +export const DNS_ERROR_RECORD_ONLY_AT_ZONE_ROOT = 9710; + +// DNS_ERROR_RECORD_ALREADY_EXISTS 0x000025ef +// +// MessageId: DNS_ERROR_RECORD_ALREADY_EXISTS +// +// MessageText: +// +// DNS record already exists. +// +export const DNS_ERROR_RECORD_ALREADY_EXISTS = 9711; + +// DNS_ERROR_SECONDARY_DATA 0x000025f0 +// +// MessageId: DNS_ERROR_SECONDARY_DATA +// +// MessageText: +// +// Secondary DNS zone data error. +// +export const DNS_ERROR_SECONDARY_DATA = 9712; + +// DNS_ERROR_NO_CREATE_CACHE_DATA 0x000025f1 +// +// MessageId: DNS_ERROR_NO_CREATE_CACHE_DATA +// +// MessageText: +// +// Could not create DNS cache data. +// +export const DNS_ERROR_NO_CREATE_CACHE_DATA = 9713; + +// DNS_ERROR_NAME_DOES_NOT_EXIST 0x000025f2 +// +// MessageId: DNS_ERROR_NAME_DOES_NOT_EXIST +// +// MessageText: +// +// DNS name does not exist. +// +export const DNS_ERROR_NAME_DOES_NOT_EXIST = 9714; + +// DNS_WARNING_PTR_CREATE_FAILED 0x000025f3 +// +// MessageId: DNS_WARNING_PTR_CREATE_FAILED +// +// MessageText: +// +// Could not create pointer (PTR) record. +// +export const DNS_WARNING_PTR_CREATE_FAILED = 9715; + +// DNS_WARNING_DOMAIN_UNDELETED 0x000025f4 +// +// MessageId: DNS_WARNING_DOMAIN_UNDELETED +// +// MessageText: +// +// DNS domain was undeleted. +// +export const DNS_WARNING_DOMAIN_UNDELETED = 9716; + +// DNS_ERROR_DS_UNAVAILABLE 0x000025f5 +// +// MessageId: DNS_ERROR_DS_UNAVAILABLE +// +// MessageText: +// +// The directory service is unavailable. +// +export const DNS_ERROR_DS_UNAVAILABLE = 9717; + +// DNS_ERROR_DS_ZONE_ALREADY_EXISTS 0x000025f6 +// +// MessageId: DNS_ERROR_DS_ZONE_ALREADY_EXISTS +// +// MessageText: +// +// DNS zone already exists in the directory service. +// +export const DNS_ERROR_DS_ZONE_ALREADY_EXISTS = 9718; + +// DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE 0x000025f7 +// +// MessageId: DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE +// +// MessageText: +// +// DNS server not creating or reading the boot file for the directory service integrated DNS zone. +// +export const DNS_ERROR_NO_BOOTFILE_IF_DS_ZONE = 9719; + +// +// Operation errors +// + +export const DNS_ERROR_OPERATION_BASE = 9750; + +// DNS_INFO_AXFR_COMPLETE 0x00002617 +// +// MessageId: DNS_INFO_AXFR_COMPLETE +// +// MessageText: +// +// DNS AXFR (zone transfer) complete. +// +export const DNS_INFO_AXFR_COMPLETE = 9751; + +// DNS_ERROR_AXFR 0x00002618 +// +// MessageId: DNS_ERROR_AXFR +// +// MessageText: +// +// DNS zone transfer failed. +// +export const DNS_ERROR_AXFR = 9752; + +// DNS_INFO_ADDED_LOCAL_WINS 0x00002619 +// +// MessageId: DNS_INFO_ADDED_LOCAL_WINS +// +// MessageText: +// +// Added local WINS server. +// +export const DNS_INFO_ADDED_LOCAL_WINS = 9753; + +// +// Secure update +// + +export const DNS_ERROR_SECURE_BASE = 9800; + +// DNS_STATUS_CONTINUE_NEEDED 0x00002649 +// +// MessageId: DNS_STATUS_CONTINUE_NEEDED +// +// MessageText: +// +// Secure update call needs to continue update request. +// +export const DNS_STATUS_CONTINUE_NEEDED = 9801; + +// +// Setup errors +// + +export const DNS_ERROR_SETUP_BASE = 9850; + +// DNS_ERROR_NO_TCPIP 0x0000267b +// +// MessageId: DNS_ERROR_NO_TCPIP +// +// MessageText: +// +// TCP/IP network protocol not installed. +// +export const DNS_ERROR_NO_TCPIP = 9851; + +// DNS_ERROR_NO_DNS_SERVERS 0x0000267c +// +// MessageId: DNS_ERROR_NO_DNS_SERVERS +// +// MessageText: +// +// No DNS servers configured for local system. +// +export const DNS_ERROR_NO_DNS_SERVERS = 9852; + +// +// Directory partition (DP) errors +// + +export const DNS_ERROR_DP_BASE = 9900; + +// DNS_ERROR_DP_DOES_NOT_EXIST 0x000026ad +// +// MessageId: DNS_ERROR_DP_DOES_NOT_EXIST +// +// MessageText: +// +// The specified directory partition does not exist. +// +export const DNS_ERROR_DP_DOES_NOT_EXIST = 9901; + +// DNS_ERROR_DP_ALREADY_EXISTS 0x000026ae +// +// MessageId: DNS_ERROR_DP_ALREADY_EXISTS +// +// MessageText: +// +// The specified directory partition already exists. +// +export const DNS_ERROR_DP_ALREADY_EXISTS = 9902; + +// DNS_ERROR_DP_NOT_ENLISTED 0x000026af +// +// MessageId: DNS_ERROR_DP_NOT_ENLISTED +// +// MessageText: +// +// This DNS server is not enlisted in the specified directory partition. +// +export const DNS_ERROR_DP_NOT_ENLISTED = 9903; + +// DNS_ERROR_DP_ALREADY_ENLISTED 0x000026b0 +// +// MessageId: DNS_ERROR_DP_ALREADY_ENLISTED +// +// MessageText: +// +// This DNS server is already enlisted in the specified directory partition. +// +export const DNS_ERROR_DP_ALREADY_ENLISTED = 9904; + +// DNS_ERROR_DP_NOT_AVAILABLE 0x000026b1 +// +// MessageId: DNS_ERROR_DP_NOT_AVAILABLE +// +// MessageText: +// +// The directory partition is not available at this time. Please wait +// a few minutes and try again. +// +export const DNS_ERROR_DP_NOT_AVAILABLE = 9905; + +// DNS_ERROR_DP_FSMO_ERROR 0x000026b2 +// +// MessageId: DNS_ERROR_DP_FSMO_ERROR +// +// MessageText: +// +// The application directory partition operation failed. The domain controller +// holding the domain naming master role is down or unable to service the +// request or is not running Windows Server 2003. +// +export const DNS_ERROR_DP_FSMO_ERROR = 9906; + +/////////////////////////////////////////////////// +// // +// End of DNS Error Codes // +// // +// 9000 to 9999 // +/////////////////////////////////////////////////// + +/////////////////////////////////////////////////// +// // +// WinSock Error Codes // +// // +// 10000 to 11999 // +/////////////////////////////////////////////////// + +// +// WinSock error codes are also defined in WinSock.h +// and WinSock2.h, hence the IFDEF +// +export const WSABASEERR = 10000; +// +// MessageId: WSAEINTR +// +// MessageText: +// +// A blocking operation was interrupted by a call to WSACancelBlockingCall. +// +export const WSAEINTR = 10004; + +// +// MessageId: WSAEBADF +// +// MessageText: +// +// The file handle supplied is not valid. +// +export const WSAEBADF = 10009; + +// +// MessageId: WSAEACCES +// +// MessageText: +// +// An attempt was made to access a socket in a way forbidden by its access permissions. +// +export const WSAEACCES = 10013; + +// +// MessageId: WSAEFAULT +// +// MessageText: +// +// The system detected an invalid pointer address in attempting to use a pointer argument in a call. +// +export const WSAEFAULT = 10014; + +// +// MessageId: WSAEINVAL +// +// MessageText: +// +// An invalid argument was supplied. +// +export const WSAEINVAL = 10022; + +// +// MessageId: WSAEMFILE +// +// MessageText: +// +// Too many open sockets. +// +export const WSAEMFILE = 10024; + +// +// MessageId: WSAEWOULDBLOCK +// +// MessageText: +// +// A non-blocking socket operation could not be completed immediately. +// +export const WSAEWOULDBLOCK = 10035; + +// +// MessageId: WSAEINPROGRESS +// +// MessageText: +// +// A blocking operation is currently executing. +// +export const WSAEINPROGRESS = 10036; + +// +// MessageId: WSAEALREADY +// +// MessageText: +// +// An operation was attempted on a non-blocking socket that already had an operation in progress. +// +export const WSAEALREADY = 10037; + +// +// MessageId: WSAENOTSOCK +// +// MessageText: +// +// An operation was attempted on something that is not a socket. +// +export const WSAENOTSOCK = 10038; + +// +// MessageId: WSAEDESTADDRREQ +// +// MessageText: +// +// A required address was omitted from an operation on a socket. +// +export const WSAEDESTADDRREQ = 10039; + +// +// MessageId: WSAEMSGSIZE +// +// MessageText: +// +// A message sent on a datagram socket was larger than the internal message buffer or some other network limit, or the buffer used to receive a datagram into was smaller than the datagram itself. +// +export const WSAEMSGSIZE = 10040; + +// +// MessageId: WSAEPROTOTYPE +// +// MessageText: +// +// A protocol was specified in the socket function call that does not support the semantics of the socket type requested. +// +export const WSAEPROTOTYPE = 10041; + +// +// MessageId: WSAENOPROTOOPT +// +// MessageText: +// +// An unknown, invalid, or unsupported option or level was specified in a getsockopt or setsockopt call. +// +export const WSAENOPROTOOPT = 10042; + +// +// MessageId: WSAEPROTONOSUPPORT +// +// MessageText: +// +// The requested protocol has not been configured into the system, or no implementation for it exists. +// +export const WSAEPROTONOSUPPORT = 10043; + +// +// MessageId: WSAESOCKTNOSUPPORT +// +// MessageText: +// +// The support for the specified socket type does not exist in this address family. +// +export const WSAESOCKTNOSUPPORT = 10044; + +// +// MessageId: WSAEOPNOTSUPP +// +// MessageText: +// +// The attempted operation is not supported for the type of object referenced. +// +export const WSAEOPNOTSUPP = 10045; + +// +// MessageId: WSAEPFNOSUPPORT +// +// MessageText: +// +// The protocol family has not been configured into the system or no implementation for it exists. +// +export const WSAEPFNOSUPPORT = 10046; + +// +// MessageId: WSAEAFNOSUPPORT +// +// MessageText: +// +// An address incompatible with the requested protocol was used. +// +export const WSAEAFNOSUPPORT = 10047; + +// +// MessageId: WSAEADDRINUSE +// +// MessageText: +// +// Only one usage of each socket address (protocol/network address/port) is normally permitted. +// +export const WSAEADDRINUSE = 10048; + +// +// MessageId: WSAEADDRNOTAVAIL +// +// MessageText: +// +// The requested address is not valid in its context. +// +export const WSAEADDRNOTAVAIL = 10049; + +// +// MessageId: WSAENETDOWN +// +// MessageText: +// +// A socket operation encountered a dead network. +// +export const WSAENETDOWN = 10050; + +// +// MessageId: WSAENETUNREACH +// +// MessageText: +// +// A socket operation was attempted to an unreachable network. +// +export const WSAENETUNREACH = 10051; + +// +// MessageId: WSAENETRESET +// +// MessageText: +// +// The connection has been broken due to keep-alive activity detecting a failure while the operation was in progress. +// +export const WSAENETRESET = 10052; + +// +// MessageId: WSAECONNABORTED +// +// MessageText: +// +// An established connection was aborted by the software in your host machine. +// +export const WSAECONNABORTED = 10053; + +// +// MessageId: WSAECONNRESET +// +// MessageText: +// +// An existing connection was forcibly closed by the remote host. +// +export const WSAECONNRESET = 10054; + +// +// MessageId: WSAENOBUFS +// +// MessageText: +// +// An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full. +// +export const WSAENOBUFS = 10055; + +// +// MessageId: WSAEISCONN +// +// MessageText: +// +// A connect request was made on an already connected socket. +// +export const WSAEISCONN = 10056; + +// +// MessageId: WSAENOTCONN +// +// MessageText: +// +// A request to send or receive data was disallowed because the socket is not connected and (when sending on a datagram socket using a sendto call) no address was supplied. +// +export const WSAENOTCONN = 10057; + +// +// MessageId: WSAESHUTDOWN +// +// MessageText: +// +// A request to send or receive data was disallowed because the socket had already been shut down in that direction with a previous shutdown call. +// +export const WSAESHUTDOWN = 10058; + +// +// MessageId: WSAETOOMANYREFS +// +// MessageText: +// +// Too many references to some kernel object. +// +export const WSAETOOMANYREFS = 10059; + +// +// MessageId: WSAETIMEDOUT +// +// MessageText: +// +// A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. +// +export const WSAETIMEDOUT = 10060; + +// +// MessageId: WSAECONNREFUSED +// +// MessageText: +// +// No connection could be made because the target machine actively refused it. +// +export const WSAECONNREFUSED = 10061; + +// +// MessageId: WSAELOOP +// +// MessageText: +// +// Cannot translate name. +// +export const WSAELOOP = 10062; + +// +// MessageId: WSAENAMETOOLONG +// +// MessageText: +// +// Name component or name was too long. +// +export const WSAENAMETOOLONG = 10063; + +// +// MessageId: WSAEHOSTDOWN +// +// MessageText: +// +// A socket operation failed because the destination host was down. +// +export const WSAEHOSTDOWN = 10064; + +// +// MessageId: WSAEHOSTUNREACH +// +// MessageText: +// +// A socket operation was attempted to an unreachable host. +// +export const WSAEHOSTUNREACH = 10065; + +// +// MessageId: WSAENOTEMPTY +// +// MessageText: +// +// Cannot remove a directory that is not empty. +// +export const WSAENOTEMPTY = 10066; + +// +// MessageId: WSAEPROCLIM +// +// MessageText: +// +// A Windows Sockets implementation may have a limit on the number of applications that may use it simultaneously. +// +export const WSAEPROCLIM = 10067; + +// +// MessageId: WSAEUSERS +// +// MessageText: +// +// Ran out of quota. +// +export const WSAEUSERS = 10068; + +// +// MessageId: WSAEDQUOT +// +// MessageText: +// +// Ran out of disk quota. +// +export const WSAEDQUOT = 10069; + +// +// MessageId: WSAESTALE +// +// MessageText: +// +// File handle reference is no longer available. +// +export const WSAESTALE = 10070; + +// +// MessageId: WSAEREMOTE +// +// MessageText: +// +// Item is not available locally. +// +export const WSAEREMOTE = 10071; + +// +// MessageId: WSASYSNOTREADY +// +// MessageText: +// +// WSAStartup cannot function at this time because the underlying system it uses to provide network services is currently unavailable. +// +export const WSASYSNOTREADY = 10091; + +// +// MessageId: WSAVERNOTSUPPORTED +// +// MessageText: +// +// The Windows Sockets version requested is not supported. +// +export const WSAVERNOTSUPPORTED = 10092; + +// +// MessageId: WSANOTINITIALISED +// +// MessageText: +// +// Either the application has not called WSAStartup, or WSAStartup failed. +// +export const WSANOTINITIALISED = 10093; + +// +// MessageId: WSAEDISCON +// +// MessageText: +// +// Returned by WSARecv or WSARecvFrom to indicate the remote party has initiated a graceful shutdown sequence. +// +export const WSAEDISCON = 10101; + +// +// MessageId: WSAENOMORE +// +// MessageText: +// +// No more results can be returned by WSALookupServiceNext. +// +export const WSAENOMORE = 10102; + +// +// MessageId: WSAECANCELLED +// +// MessageText: +// +// A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled. +// +export const WSAECANCELLED = 10103; + +// +// MessageId: WSAEINVALIDPROCTABLE +// +// MessageText: +// +// The procedure call table is invalid. +// +export const WSAEINVALIDPROCTABLE = 10104; + +// +// MessageId: WSAEINVALIDPROVIDER +// +// MessageText: +// +// The requested service provider is invalid. +// +export const WSAEINVALIDPROVIDER = 10105; + +// +// MessageId: WSAEPROVIDERFAILEDINIT +// +// MessageText: +// +// The requested service provider could not be loaded or initialized. +// +export const WSAEPROVIDERFAILEDINIT = 10106; + +// +// MessageId: WSASYSCALLFAILURE +// +// MessageText: +// +// A system call that should never fail has failed. +// +export const WSASYSCALLFAILURE = 10107; + +// +// MessageId: WSASERVICE_NOT_FOUND +// +// MessageText: +// +// No such service is known. The service cannot be found in the specified name space. +// +export const WSASERVICE_NOT_FOUND = 10108; + +// +// MessageId: WSATYPE_NOT_FOUND +// +// MessageText: +// +// The specified class was not found. +// +export const WSATYPE_NOT_FOUND = 10109; + +// +// MessageId: WSA_E_NO_MORE +// +// MessageText: +// +// No more results can be returned by WSALookupServiceNext. +// +export const WSA_E_NO_MORE = 10110; + +// +// MessageId: WSA_E_CANCELLED +// +// MessageText: +// +// A call to WSALookupServiceEnd was made while this call was still processing. The call has been canceled. +// +export const WSA_E_CANCELLED = 10111; + +// +// MessageId: WSAEREFUSED +// +// MessageText: +// +// A database query failed because it was actively refused. +// +export const WSAEREFUSED = 10112; + +// +// MessageId: WSAHOST_NOT_FOUND +// +// MessageText: +// +// No such host is known. +// +export const WSAHOST_NOT_FOUND = 11001; + +// +// MessageId: WSATRY_AGAIN +// +// MessageText: +// +// This is usually a temporary error during hostname resolution and means that the local server did not receive a response from an authoritative server. +// +export const WSATRY_AGAIN = 11002; + +// +// MessageId: WSANO_RECOVERY +// +// MessageText: +// +// A non-recoverable error occurred during a database lookup. +// +export const WSANO_RECOVERY = 11003; + +// +// MessageId: WSANO_DATA +// +// MessageText: +// +// The requested name is valid, but no data of the requested type was found. +// +export const WSANO_DATA = 11004; + +// +// MessageId: WSA_QOS_RECEIVERS +// +// MessageText: +// +// At least one reserve has arrived. +// +export const WSA_QOS_RECEIVERS = 11005; + +// +// MessageId: WSA_QOS_SENDERS +// +// MessageText: +// +// At least one path has arrived. +// +export const WSA_QOS_SENDERS = 11006; + +// +// MessageId: WSA_QOS_NO_SENDERS +// +// MessageText: +// +// There are no senders. +// +export const WSA_QOS_NO_SENDERS = 11007; + +// +// MessageId: WSA_QOS_NO_RECEIVERS +// +// MessageText: +// +// There are no receivers. +// +export const WSA_QOS_NO_RECEIVERS = 11008; + +// +// MessageId: WSA_QOS_REQUEST_CONFIRMED +// +// MessageText: +// +// Reserve has been confirmed. +// +export const WSA_QOS_REQUEST_CONFIRMED = 11009; + +// +// MessageId: WSA_QOS_ADMISSION_FAILURE +// +// MessageText: +// +// Error due to lack of resources. +// +export const WSA_QOS_ADMISSION_FAILURE = 11010; + +// +// MessageId: WSA_QOS_POLICY_FAILURE +// +// MessageText: +// +// Rejected for administrative reasons - bad credentials. +// +export const WSA_QOS_POLICY_FAILURE = 11011; + +// +// MessageId: WSA_QOS_BAD_STYLE +// +// MessageText: +// +// Unknown or conflicting style. +// +export const WSA_QOS_BAD_STYLE = 11012; + +// +// MessageId: WSA_QOS_BAD_OBJECT +// +// MessageText: +// +// Problem with some part of the filterspec or providerspecific buffer in general. +// +export const WSA_QOS_BAD_OBJECT = 11013; + +// +// MessageId: WSA_QOS_TRAFFIC_CTRL_ERROR +// +// MessageText: +// +// Problem with some part of the flowspec. +// +export const WSA_QOS_TRAFFIC_CTRL_ERROR = 11014; + +// +// MessageId: WSA_QOS_GENERIC_ERROR +// +// MessageText: +// +// General QOS error. +// +export const WSA_QOS_GENERIC_ERROR = 11015; + +// +// MessageId: WSA_QOS_ESERVICETYPE +// +// MessageText: +// +// An invalid or unrecognized service type was found in the flowspec. +// +export const WSA_QOS_ESERVICETYPE = 11016; + +// +// MessageId: WSA_QOS_EFLOWSPEC +// +// MessageText: +// +// An invalid or inconsistent flowspec was found in the QOS structure. +// +export const WSA_QOS_EFLOWSPEC = 11017; + +// +// MessageId: WSA_QOS_EPROVSPECBUF +// +// MessageText: +// +// Invalid QOS provider-specific buffer. +// +export const WSA_QOS_EPROVSPECBUF = 11018; + +// +// MessageId: WSA_QOS_EFILTERSTYLE +// +// MessageText: +// +// An invalid QOS filter style was used. +// +export const WSA_QOS_EFILTERSTYLE = 11019; + +// +// MessageId: WSA_QOS_EFILTERTYPE +// +// MessageText: +// +// An invalid QOS filter type was used. +// +export const WSA_QOS_EFILTERTYPE = 11020; + +// +// MessageId: WSA_QOS_EFILTERCOUNT +// +// MessageText: +// +// An incorrect number of QOS FILTERSPECs were specified in the FLOWDESCRIPTOR. +// +export const WSA_QOS_EFILTERCOUNT = 11021; + +// +// MessageId: WSA_QOS_EOBJLENGTH +// +// MessageText: +// +// An object with an invalid ObjectLength field was specified in the QOS provider-specific buffer. +// +export const WSA_QOS_EOBJLENGTH = 11022; + +// +// MessageId: WSA_QOS_EFLOWCOUNT +// +// MessageText: +// +// An incorrect number of flow descriptors was specified in the QOS structure. +// +export const WSA_QOS_EFLOWCOUNT = 11023; + +// +// MessageId: WSA_QOS_EUNKOWNPSOBJ +// +// MessageText: +// +// An unrecognized object was found in the QOS provider-specific buffer. +// +export const WSA_QOS_EUNKOWNPSOBJ = 11024; + +// +// MessageId: WSA_QOS_EPOLICYOBJ +// +// MessageText: +// +// An invalid policy object was found in the QOS provider-specific buffer. +// +export const WSA_QOS_EPOLICYOBJ = 11025; + +// +// MessageId: WSA_QOS_EFLOWDESC +// +// MessageText: +// +// An invalid QOS flow descriptor was found in the flow descriptor list. +// +export const WSA_QOS_EFLOWDESC = 11026; + +// +// MessageId: WSA_QOS_EPSFLOWSPEC +// +// MessageText: +// +// An invalid or inconsistent flowspec was found in the QOS provider specific buffer. +// +export const WSA_QOS_EPSFLOWSPEC = 11027; + +// +// MessageId: WSA_QOS_EPSFILTERSPEC +// +// MessageText: +// +// An invalid FILTERSPEC was found in the QOS provider-specific buffer. +// +export const WSA_QOS_EPSFILTERSPEC = 11028; + +// +// MessageId: WSA_QOS_ESDMODEOBJ +// +// MessageText: +// +// An invalid shape discard mode object was found in the QOS provider specific buffer. +// +export const WSA_QOS_ESDMODEOBJ = 11029; + +// +// MessageId: WSA_QOS_ESHAPERATEOBJ +// +// MessageText: +// +// An invalid shaping rate object was found in the QOS provider-specific buffer. +// +export const WSA_QOS_ESHAPERATEOBJ = 11030; + +// +// MessageId: WSA_QOS_RESERVED_PETYPE +// +// MessageText: +// +// A reserved policy element was found in the QOS provider-specific buffer. +// +export const WSA_QOS_RESERVED_PETYPE = 11031; + +/////////////////////////////////////////////////// +// // +// End of WinSock Error Codes // +// // +// 10000 to 11999 // +/////////////////////////////////////////////////// + +/////////////////////////////////////////////////// +// // +// Side By Side Error Codes // +// // +// 14000 to 14999 // +/////////////////////////////////////////////////// + +// +// MessageId: ERROR_SXS_SECTION_NOT_FOUND +// +// MessageText: +// +// The requested section was not present in the activation context. +// +export const ERROR_SXS_SECTION_NOT_FOUND = 14000; + +// +// MessageId: ERROR_SXS_CANT_GEN_ACTCTX +// +// MessageText: +// +// This application has failed to start because the application configuration is incorrect. Reinstalling the application may fix this problem. +// +export const ERROR_SXS_CANT_GEN_ACTCTX = 14001; + +// +// MessageId: ERROR_SXS_INVALID_ACTCTXDATA_FORMAT +// +// MessageText: +// +// The application binding data format is invalid. +// +export const ERROR_SXS_INVALID_ACTCTXDATA_FORMAT = 14002; + +// +// MessageId: ERROR_SXS_ASSEMBLY_NOT_FOUND +// +// MessageText: +// +// The referenced assembly is not installed on your system. +// +export const ERROR_SXS_ASSEMBLY_NOT_FOUND = 14003; + +// +// MessageId: ERROR_SXS_MANIFEST_FORMAT_ERROR +// +// MessageText: +// +// The manifest file does not begin with the required tag and format information. +// +export const ERROR_SXS_MANIFEST_FORMAT_ERROR = 14004; + +// +// MessageId: ERROR_SXS_MANIFEST_PARSE_ERROR +// +// MessageText: +// +// The manifest file contains one or more syntax errors. +// +export const ERROR_SXS_MANIFEST_PARSE_ERROR = 14005; + +// +// MessageId: ERROR_SXS_ACTIVATION_CONTEXT_DISABLED +// +// MessageText: +// +// The application attempted to activate a disabled activation context. +// +export const ERROR_SXS_ACTIVATION_CONTEXT_DISABLED = 14006; + +// +// MessageId: ERROR_SXS_KEY_NOT_FOUND +// +// MessageText: +// +// The requested lookup key was not found in any active activation context. +// +export const ERROR_SXS_KEY_NOT_FOUND = 14007; + +// +// MessageId: ERROR_SXS_VERSION_CONFLICT +// +// MessageText: +// +// A component version required by the application conflicts with another component version already active. +// +export const ERROR_SXS_VERSION_CONFLICT = 14008; + +// +// MessageId: ERROR_SXS_WRONG_SECTION_TYPE +// +// MessageText: +// +// The type requested activation context section does not match the query API used. +// +export const ERROR_SXS_WRONG_SECTION_TYPE = 14009; + +// +// MessageId: ERROR_SXS_THREAD_QUERIES_DISABLED +// +// MessageText: +// +// Lack of system resources has required isolated activation to be disabled for the current thread of execution. +// +export const ERROR_SXS_THREAD_QUERIES_DISABLED = 14010; + +// +// MessageId: ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET +// +// MessageText: +// +// An attempt to set the process default activation context failed because the process default activation context was already set. +// +export const ERROR_SXS_PROCESS_DEFAULT_ALREADY_SET = 14011; + +// +// MessageId: ERROR_SXS_UNKNOWN_ENCODING_GROUP +// +// MessageText: +// +// The encoding group identifier specified is not recognized. +// +export const ERROR_SXS_UNKNOWN_ENCODING_GROUP = 14012; + +// +// MessageId: ERROR_SXS_UNKNOWN_ENCODING +// +// MessageText: +// +// The encoding requested is not recognized. +// +export const ERROR_SXS_UNKNOWN_ENCODING = 14013; + +// +// MessageId: ERROR_SXS_INVALID_XML_NAMESPACE_URI +// +// MessageText: +// +// The manifest contains a reference to an invalid URI. +// +export const ERROR_SXS_INVALID_XML_NAMESPACE_URI = 14014; + +// +// MessageId: ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED +// +// MessageText: +// +// The application manifest contains a reference to a dependent assembly which is not installed +// +export const ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED = 14015; + +// +// MessageId: ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED +// +// MessageText: +// +// The manifest for an assembly used by the application has a reference to a dependent assembly which is not installed +// +export const ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED = 14016; + +// +// MessageId: ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE +// +// MessageText: +// +// The manifest contains an attribute for the assembly identity which is not valid. +// +export const ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE = 14017; + +// +// MessageId: ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE +// +// MessageText: +// +// The manifest is missing the required default namespace specification on the assembly element. +// +export const ERROR_SXS_MANIFEST_MISSING_REQUIRED_DEFAULT_NAMESPACE = 14018; + +// +// MessageId: ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE +// +// MessageText: +// +// The manifest has a default namespace specified on the assembly element but its value is not "urn:schemas-microsoft-com:asm.v1". +// +export const ERROR_SXS_MANIFEST_INVALID_REQUIRED_DEFAULT_NAMESPACE = 14019; + +// +// MessageId: ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT +// +// MessageText: +// +// The private manifest probed has crossed reparse-point-associated path +// +export const ERROR_SXS_PRIVATE_MANIFEST_CROSS_PATH_WITH_REPARSE_POINT = 14020; + +// +// MessageId: ERROR_SXS_DUPLICATE_DLL_NAME +// +// MessageText: +// +// Two or more components referenced directly or indirectly by the application manifest have files by the same name. +// +export const ERROR_SXS_DUPLICATE_DLL_NAME = 14021; + +// +// MessageId: ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME +// +// MessageText: +// +// Two or more components referenced directly or indirectly by the application manifest have window classes with the same name. +// +export const ERROR_SXS_DUPLICATE_WINDOWCLASS_NAME = 14022; + +// +// MessageId: ERROR_SXS_DUPLICATE_CLSID +// +// MessageText: +// +// Two or more components referenced directly or indirectly by the application manifest have the same COM server CLSIDs. +// +export const ERROR_SXS_DUPLICATE_CLSID = 14023; + +// +// MessageId: ERROR_SXS_DUPLICATE_IID +// +// MessageText: +// +// Two or more components referenced directly or indirectly by the application manifest have proxies for the same COM interface IIDs. +// +export const ERROR_SXS_DUPLICATE_IID = 14024; + +// +// MessageId: ERROR_SXS_DUPLICATE_TLBID +// +// MessageText: +// +// Two or more components referenced directly or indirectly by the application manifest have the same COM type library TLBIDs. +// +export const ERROR_SXS_DUPLICATE_TLBID = 14025; + +// +// MessageId: ERROR_SXS_DUPLICATE_PROGID +// +// MessageText: +// +// Two or more components referenced directly or indirectly by the application manifest have the same COM ProgIDs. +// +export const ERROR_SXS_DUPLICATE_PROGID = 14026; + +// +// MessageId: ERROR_SXS_DUPLICATE_ASSEMBLY_NAME +// +// MessageText: +// +// Two or more components referenced directly or indirectly by the application manifest are different versions of the same component which is not permitted. +// +export const ERROR_SXS_DUPLICATE_ASSEMBLY_NAME = 14027; + +// +// MessageId: ERROR_SXS_FILE_HASH_MISMATCH +// +// MessageText: +// +// A component's file does not match the verification information present in the +// component manifest. +// +export const ERROR_SXS_FILE_HASH_MISMATCH = 14028; + +// +// MessageId: ERROR_SXS_POLICY_PARSE_ERROR +// +// MessageText: +// +// The policy manifest contains one or more syntax errors. +// +export const ERROR_SXS_POLICY_PARSE_ERROR = 14029; + +// +// MessageId: ERROR_SXS_XML_E_MISSINGQUOTE +// +// MessageText: +// +// Manifest Parse Error : A string literal was expected, but no opening quote character was found. +// +export const ERROR_SXS_XML_E_MISSINGQUOTE = 14030; + +// +// MessageId: ERROR_SXS_XML_E_COMMENTSYNTAX +// +// MessageText: +// +// Manifest Parse Error : Incorrect syntax was used in a comment. +// +export const ERROR_SXS_XML_E_COMMENTSYNTAX = 14031; + +// +// MessageId: ERROR_SXS_XML_E_BADSTARTNAMECHAR +// +// MessageText: +// +// Manifest Parse Error : A name was started with an invalid character. +// +export const ERROR_SXS_XML_E_BADSTARTNAMECHAR = 14032; + +// +// MessageId: ERROR_SXS_XML_E_BADNAMECHAR +// +// MessageText: +// +// Manifest Parse Error : A name contained an invalid character. +// +export const ERROR_SXS_XML_E_BADNAMECHAR = 14033; + +// +// MessageId: ERROR_SXS_XML_E_BADCHARINSTRING +// +// MessageText: +// +// Manifest Parse Error : A string literal contained an invalid character. +// +export const ERROR_SXS_XML_E_BADCHARINSTRING = 14034; + +// +// MessageId: ERROR_SXS_XML_E_XMLDECLSYNTAX +// +// MessageText: +// +// Manifest Parse Error : Invalid syntax for an xml declaration. +// +export const ERROR_SXS_XML_E_XMLDECLSYNTAX = 14035; + +// +// MessageId: ERROR_SXS_XML_E_BADCHARDATA +// +// MessageText: +// +// Manifest Parse Error : An Invalid character was found in text content. +// +export const ERROR_SXS_XML_E_BADCHARDATA = 14036; + +// +// MessageId: ERROR_SXS_XML_E_MISSINGWHITESPACE +// +// MessageText: +// +// Manifest Parse Error : Required white space was missing. +// +export const ERROR_SXS_XML_E_MISSINGWHITESPACE = 14037; + +// +// MessageId: ERROR_SXS_XML_E_EXPECTINGTAGEND +// +// MessageText: +// +// Manifest Parse Error : The character '>' was expected. +// +export const ERROR_SXS_XML_E_EXPECTINGTAGEND = 14038; + +// +// MessageId: ERROR_SXS_XML_E_MISSINGSEMICOLON +// +// MessageText: +// +// Manifest Parse Error : A semi colon character was expected. +// +export const ERROR_SXS_XML_E_MISSINGSEMICOLON = 14039; + +// +// MessageId: ERROR_SXS_XML_E_UNBALANCEDPAREN +// +// MessageText: +// +// Manifest Parse Error : Unbalanced parentheses. +// +export const ERROR_SXS_XML_E_UNBALANCEDPAREN = 14040; + +// +// MessageId: ERROR_SXS_XML_E_INTERNALERROR +// +// MessageText: +// +// Manifest Parse Error : Internal error. +// +export const ERROR_SXS_XML_E_INTERNALERROR = 14041; + +// +// MessageId: ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE +// +// MessageText: +// +// Manifest Parse Error : Whitespace is not allowed at this location. +// +export const ERROR_SXS_XML_E_UNEXPECTED_WHITESPACE = 14042; + +// +// MessageId: ERROR_SXS_XML_E_INCOMPLETE_ENCODING +// +// MessageText: +// +// Manifest Parse Error : End of file reached in invalid state for current encoding. +// +export const ERROR_SXS_XML_E_INCOMPLETE_ENCODING = 14043; + +// +// MessageId: ERROR_SXS_XML_E_MISSING_PAREN +// +// MessageText: +// +// Manifest Parse Error : Missing parenthesis. +// +export const ERROR_SXS_XML_E_MISSING_PAREN = 14044; + +// +// MessageId: ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE +// +// MessageText: +// +// Manifest Parse Error : A single or double closing quote character (\' or \") is missing. +// +export const ERROR_SXS_XML_E_EXPECTINGCLOSEQUOTE = 14045; + +// +// MessageId: ERROR_SXS_XML_E_MULTIPLE_COLONS +// +// MessageText: +// +// Manifest Parse Error : Multiple colons are not allowed in a name. +// +export const ERROR_SXS_XML_E_MULTIPLE_COLONS = 14046; + +// +// MessageId: ERROR_SXS_XML_E_INVALID_DECIMAL +// +// MessageText: +// +// Manifest Parse Error : Invalid character for decimal digit. +// +export const ERROR_SXS_XML_E_INVALID_DECIMAL = 14047; + +// +// MessageId: ERROR_SXS_XML_E_INVALID_HEXIDECIMAL +// +// MessageText: +// +// Manifest Parse Error : Invalid character for hexadecimal digit. +// +export const ERROR_SXS_XML_E_INVALID_HEXIDECIMAL = 14048; + +// +// MessageId: ERROR_SXS_XML_E_INVALID_UNICODE +// +// MessageText: +// +// Manifest Parse Error : Invalid unicode character value for this platform. +// +export const ERROR_SXS_XML_E_INVALID_UNICODE = 14049; + +// +// MessageId: ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK +// +// MessageText: +// +// Manifest Parse Error : Expecting whitespace or '?'. +// +export const ERROR_SXS_XML_E_WHITESPACEORQUESTIONMARK = 14050; + +// +// MessageId: ERROR_SXS_XML_E_UNEXPECTEDENDTAG +// +// MessageText: +// +// Manifest Parse Error : End tag was not expected at this location. +// +export const ERROR_SXS_XML_E_UNEXPECTEDENDTAG = 14051; + +// +// MessageId: ERROR_SXS_XML_E_UNCLOSEDTAG +// +// MessageText: +// +// Manifest Parse Error : The following tags were not closed: %1. +// +export const ERROR_SXS_XML_E_UNCLOSEDTAG = 14052; + +// +// MessageId: ERROR_SXS_XML_E_DUPLICATEATTRIBUTE +// +// MessageText: +// +// Manifest Parse Error : Duplicate attribute. +// +export const ERROR_SXS_XML_E_DUPLICATEATTRIBUTE = 14053; + +// +// MessageId: ERROR_SXS_XML_E_MULTIPLEROOTS +// +// MessageText: +// +// Manifest Parse Error : Only one top level element is allowed in an XML document. +// +export const ERROR_SXS_XML_E_MULTIPLEROOTS = 14054; + +// +// MessageId: ERROR_SXS_XML_E_INVALIDATROOTLEVEL +// +// MessageText: +// +// Manifest Parse Error : Invalid at the top level of the document. +// +export const ERROR_SXS_XML_E_INVALIDATROOTLEVEL = 14055; + +// +// MessageId: ERROR_SXS_XML_E_BADXMLDECL +// +// MessageText: +// +// Manifest Parse Error : Invalid xml declaration. +// +export const ERROR_SXS_XML_E_BADXMLDECL = 14056; + +// +// MessageId: ERROR_SXS_XML_E_MISSINGROOT +// +// MessageText: +// +// Manifest Parse Error : XML document must have a top level element. +// +export const ERROR_SXS_XML_E_MISSINGROOT = 14057; + +// +// MessageId: ERROR_SXS_XML_E_UNEXPECTEDEOF +// +// MessageText: +// +// Manifest Parse Error : Unexpected end of file. +// +export const ERROR_SXS_XML_E_UNEXPECTEDEOF = 14058; + +// +// MessageId: ERROR_SXS_XML_E_BADPEREFINSUBSET +// +// MessageText: +// +// Manifest Parse Error : Parameter entities cannot be used inside markup declarations in an internal subset. +// +export const ERROR_SXS_XML_E_BADPEREFINSUBSET = 14059; + +// +// MessageId: ERROR_SXS_XML_E_UNCLOSEDSTARTTAG +// +// MessageText: +// +// Manifest Parse Error : Element was not closed. +// +export const ERROR_SXS_XML_E_UNCLOSEDSTARTTAG = 14060; + +// +// MessageId: ERROR_SXS_XML_E_UNCLOSEDENDTAG +// +// MessageText: +// +// Manifest Parse Error : End element was missing the character '>'. +// +export const ERROR_SXS_XML_E_UNCLOSEDENDTAG = 14061; + +// +// MessageId: ERROR_SXS_XML_E_UNCLOSEDSTRING +// +// MessageText: +// +// Manifest Parse Error : A string literal was not closed. +// +export const ERROR_SXS_XML_E_UNCLOSEDSTRING = 14062; + +// +// MessageId: ERROR_SXS_XML_E_UNCLOSEDCOMMENT +// +// MessageText: +// +// Manifest Parse Error : A comment was not closed. +// +export const ERROR_SXS_XML_E_UNCLOSEDCOMMENT = 14063; + +// +// MessageId: ERROR_SXS_XML_E_UNCLOSEDDECL +// +// MessageText: +// +// Manifest Parse Error : A declaration was not closed. +// +export const ERROR_SXS_XML_E_UNCLOSEDDECL = 14064; + +// +// MessageId: ERROR_SXS_XML_E_UNCLOSEDCDATA +// +// MessageText: +// +// Manifest Parse Error : A CDATA section was not closed. +// +export const ERROR_SXS_XML_E_UNCLOSEDCDATA = 14065; + +// +// MessageId: ERROR_SXS_XML_E_RESERVEDNAMESPACE +// +// MessageText: +// +// Manifest Parse Error : The namespace prefix is not allowed to start with the reserved string "xml". +// +export const ERROR_SXS_XML_E_RESERVEDNAMESPACE = 14066; + +// +// MessageId: ERROR_SXS_XML_E_INVALIDENCODING +// +// MessageText: +// +// Manifest Parse Error : System does not support the specified encoding. +// +export const ERROR_SXS_XML_E_INVALIDENCODING = 14067; + +// +// MessageId: ERROR_SXS_XML_E_INVALIDSWITCH +// +// MessageText: +// +// Manifest Parse Error : Switch from current encoding to specified encoding not supported. +// +export const ERROR_SXS_XML_E_INVALIDSWITCH = 14068; + +// +// MessageId: ERROR_SXS_XML_E_BADXMLCASE +// +// MessageText: +// +// Manifest Parse Error : The name 'xml' is reserved and must be lower case. +// +export const ERROR_SXS_XML_E_BADXMLCASE = 14069; + +// +// MessageId: ERROR_SXS_XML_E_INVALID_STANDALONE +// +// MessageText: +// +// Manifest Parse Error : The standalone attribute must have the value 'yes' or 'no'. +// +export const ERROR_SXS_XML_E_INVALID_STANDALONE = 14070; + +// +// MessageId: ERROR_SXS_XML_E_UNEXPECTED_STANDALONE +// +// MessageText: +// +// Manifest Parse Error : The standalone attribute cannot be used in external entities. +// +export const ERROR_SXS_XML_E_UNEXPECTED_STANDALONE = 14071; + +// +// MessageId: ERROR_SXS_XML_E_INVALID_VERSION +// +// MessageText: +// +// Manifest Parse Error : Invalid version number. +// +export const ERROR_SXS_XML_E_INVALID_VERSION = 14072; + +// +// MessageId: ERROR_SXS_XML_E_MISSINGEQUALS +// +// MessageText: +// +// Manifest Parse Error : Missing equals sign between attribute and attribute value. +// +export const ERROR_SXS_XML_E_MISSINGEQUALS = 14073; + +// +// MessageId: ERROR_SXS_PROTECTION_RECOVERY_FAILED +// +// MessageText: +// +// Assembly Protection Error : Unable to recover the specified assembly. +// +export const ERROR_SXS_PROTECTION_RECOVERY_FAILED = 14074; + +// +// MessageId: ERROR_SXS_PROTECTION_PUBLIC_KEY_TOO_SHORT +// +// MessageText: +// +// Assembly Protection Error : The public key for an assembly was too short to be allowed. +// +export const ERROR_SXS_PROTECTION_PUBLIC_KEY_TOO_SHORT = 14075; + +// +// MessageId: ERROR_SXS_PROTECTION_CATALOG_NOT_VALID +// +// MessageText: +// +// Assembly Protection Error : The catalog for an assembly is not valid, or does not match the assembly's manifest. +// +export const ERROR_SXS_PROTECTION_CATALOG_NOT_VALID = 14076; + +// +// MessageId: ERROR_SXS_UNTRANSLATABLE_HRESULT +// +// MessageText: +// +// An HRESULT could not be translated to a corresponding Win32 error code. +// +export const ERROR_SXS_UNTRANSLATABLE_HRESULT = 14077; + +// +// MessageId: ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING +// +// MessageText: +// +// Assembly Protection Error : The catalog for an assembly is missing. +// +export const ERROR_SXS_PROTECTION_CATALOG_FILE_MISSING = 14078; + +// +// MessageId: ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE +// +// MessageText: +// +// The supplied assembly identity is missing one or more attributes which must be present in this context. +// +export const ERROR_SXS_MISSING_ASSEMBLY_IDENTITY_ATTRIBUTE = 14079; + +// +// MessageId: ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME +// +// MessageText: +// +// The supplied assembly identity has one or more attribute names that contain characters not permitted in XML names. +// +export const ERROR_SXS_INVALID_ASSEMBLY_IDENTITY_ATTRIBUTE_NAME = 14080; + +/////////////////////////////////////////////////// +// // +// End of Side By Side Error Codes // +// // +// 14000 to 14999 // +/////////////////////////////////////////////////// + +/////////////////////////////////////////////////// +// // +// Start of IPSec Error codes // +// // +// 13000 to 13999 // +/////////////////////////////////////////////////// + +// +// MessageId: ERROR_IPSEC_QM_POLICY_EXISTS +// +// MessageText: +// +// The specified quick mode policy already exists. +// +export const ERROR_IPSEC_QM_POLICY_EXISTS = 13000; + +// +// MessageId: ERROR_IPSEC_QM_POLICY_NOT_FOUND +// +// MessageText: +// +// The specified quick mode policy was not found. +// +export const ERROR_IPSEC_QM_POLICY_NOT_FOUND = 13001; + +// +// MessageId: ERROR_IPSEC_QM_POLICY_IN_USE +// +// MessageText: +// +// The specified quick mode policy is being used. +// +export const ERROR_IPSEC_QM_POLICY_IN_USE = 13002; + +// +// MessageId: ERROR_IPSEC_MM_POLICY_EXISTS +// +// MessageText: +// +// The specified main mode policy already exists. +// +export const ERROR_IPSEC_MM_POLICY_EXISTS = 13003; + +// +// MessageId: ERROR_IPSEC_MM_POLICY_NOT_FOUND +// +// MessageText: +// +// The specified main mode policy was not found +// +export const ERROR_IPSEC_MM_POLICY_NOT_FOUND = 13004; + +// +// MessageId: ERROR_IPSEC_MM_POLICY_IN_USE +// +// MessageText: +// +// The specified main mode policy is being used. +// +export const ERROR_IPSEC_MM_POLICY_IN_USE = 13005; + +// +// MessageId: ERROR_IPSEC_MM_FILTER_EXISTS +// +// MessageText: +// +// The specified main mode filter already exists. +// +export const ERROR_IPSEC_MM_FILTER_EXISTS = 13006; + +// +// MessageId: ERROR_IPSEC_MM_FILTER_NOT_FOUND +// +// MessageText: +// +// The specified main mode filter was not found. +// +export const ERROR_IPSEC_MM_FILTER_NOT_FOUND = 13007; + +// +// MessageId: ERROR_IPSEC_TRANSPORT_FILTER_EXISTS +// +// MessageText: +// +// The specified transport mode filter already exists. +// +export const ERROR_IPSEC_TRANSPORT_FILTER_EXISTS = 13008; + +// +// MessageId: ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND +// +// MessageText: +// +// The specified transport mode filter does not exist. +// +export const ERROR_IPSEC_TRANSPORT_FILTER_NOT_FOUND = 13009; + +// +// MessageId: ERROR_IPSEC_MM_AUTH_EXISTS +// +// MessageText: +// +// The specified main mode authentication list exists. +// +export const ERROR_IPSEC_MM_AUTH_EXISTS = 13010; + +// +// MessageId: ERROR_IPSEC_MM_AUTH_NOT_FOUND +// +// MessageText: +// +// The specified main mode authentication list was not found. +// +export const ERROR_IPSEC_MM_AUTH_NOT_FOUND = 13011; + +// +// MessageId: ERROR_IPSEC_MM_AUTH_IN_USE +// +// MessageText: +// +// The specified quick mode policy is being used. +// +export const ERROR_IPSEC_MM_AUTH_IN_USE = 13012; + +// +// MessageId: ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND +// +// MessageText: +// +// The specified main mode policy was not found. +// +export const ERROR_IPSEC_DEFAULT_MM_POLICY_NOT_FOUND = 13013; + +// +// MessageId: ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND +// +// MessageText: +// +// The specified quick mode policy was not found +// +export const ERROR_IPSEC_DEFAULT_MM_AUTH_NOT_FOUND = 13014; + +// +// MessageId: ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND +// +// MessageText: +// +// The manifest file contains one or more syntax errors. +// +export const ERROR_IPSEC_DEFAULT_QM_POLICY_NOT_FOUND = 13015; + +// +// MessageId: ERROR_IPSEC_TUNNEL_FILTER_EXISTS +// +// MessageText: +// +// The application attempted to activate a disabled activation context. +// +export const ERROR_IPSEC_TUNNEL_FILTER_EXISTS = 13016; + +// +// MessageId: ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND +// +// MessageText: +// +// The requested lookup key was not found in any active activation context. +// +export const ERROR_IPSEC_TUNNEL_FILTER_NOT_FOUND = 13017; + +// +// MessageId: ERROR_IPSEC_MM_FILTER_PENDING_DELETION +// +// MessageText: +// +// The Main Mode filter is pending deletion. +// +export const ERROR_IPSEC_MM_FILTER_PENDING_DELETION = 13018; + +// +// MessageId: ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION +// +// MessageText: +// +// The transport filter is pending deletion. +// +export const ERROR_IPSEC_TRANSPORT_FILTER_PENDING_DELETION = 13019; + +// +// MessageId: ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION +// +// MessageText: +// +// The tunnel filter is pending deletion. +// +export const ERROR_IPSEC_TUNNEL_FILTER_PENDING_DELETION = 13020; + +// +// MessageId: ERROR_IPSEC_MM_POLICY_PENDING_DELETION +// +// MessageText: +// +// The Main Mode policy is pending deletion. +// +export const ERROR_IPSEC_MM_POLICY_PENDING_DELETION = 13021; + +// +// MessageId: ERROR_IPSEC_MM_AUTH_PENDING_DELETION +// +// MessageText: +// +// The Main Mode authentication bundle is pending deletion. +// +export const ERROR_IPSEC_MM_AUTH_PENDING_DELETION = 13022; + +// +// MessageId: ERROR_IPSEC_QM_POLICY_PENDING_DELETION +// +// MessageText: +// +// The Quick Mode policy is pending deletion. +// +export const ERROR_IPSEC_QM_POLICY_PENDING_DELETION = 13023; + +// +// MessageId: WARNING_IPSEC_MM_POLICY_PRUNED +// +// MessageText: +// +// The Main Mode policy was successfully added, but some of the requested offers are not supported. +// +export const WARNING_IPSEC_MM_POLICY_PRUNED = 13024; + +// +// MessageId: WARNING_IPSEC_QM_POLICY_PRUNED +// +// MessageText: +// +// The Quick Mode policy was successfully added, but some of the requested offers are not supported. +// +export const WARNING_IPSEC_QM_POLICY_PRUNED = 13025; + +// +// MessageId: ERROR_IPSEC_IKE_NEG_STATUS_BEGIN +// +// MessageText: +// +// ERROR_IPSEC_IKE_NEG_STATUS_BEGIN +// +export const ERROR_IPSEC_IKE_NEG_STATUS_BEGIN = 13800; + +// +// MessageId: ERROR_IPSEC_IKE_AUTH_FAIL +// +// MessageText: +// +// IKE authentication credentials are unacceptable +// +export const ERROR_IPSEC_IKE_AUTH_FAIL = 13801; + +// +// MessageId: ERROR_IPSEC_IKE_ATTRIB_FAIL +// +// MessageText: +// +// IKE security attributes are unacceptable +// +export const ERROR_IPSEC_IKE_ATTRIB_FAIL = 13802; + +// +// MessageId: ERROR_IPSEC_IKE_NEGOTIATION_PENDING +// +// MessageText: +// +// IKE Negotiation in progress +// +export const ERROR_IPSEC_IKE_NEGOTIATION_PENDING = 13803; + +// +// MessageId: ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR +// +// MessageText: +// +// General processing error +// +export const ERROR_IPSEC_IKE_GENERAL_PROCESSING_ERROR = 13804; + +// +// MessageId: ERROR_IPSEC_IKE_TIMED_OUT +// +// MessageText: +// +// Negotiation timed out +// +export const ERROR_IPSEC_IKE_TIMED_OUT = 13805; + +// +// MessageId: ERROR_IPSEC_IKE_NO_CERT +// +// MessageText: +// +// IKE failed to find valid machine certificate +// +export const ERROR_IPSEC_IKE_NO_CERT = 13806; + +// +// MessageId: ERROR_IPSEC_IKE_SA_DELETED +// +// MessageText: +// +// IKE SA deleted by peer before establishment completed +// +export const ERROR_IPSEC_IKE_SA_DELETED = 13807; + +// +// MessageId: ERROR_IPSEC_IKE_SA_REAPED +// +// MessageText: +// +// IKE SA deleted before establishment completed +// +export const ERROR_IPSEC_IKE_SA_REAPED = 13808; + +// +// MessageId: ERROR_IPSEC_IKE_MM_ACQUIRE_DROP +// +// MessageText: +// +// Negotiation request sat in Queue too long +// +export const ERROR_IPSEC_IKE_MM_ACQUIRE_DROP = 13809; + +// +// MessageId: ERROR_IPSEC_IKE_QM_ACQUIRE_DROP +// +// MessageText: +// +// Negotiation request sat in Queue too long +// +export const ERROR_IPSEC_IKE_QM_ACQUIRE_DROP = 13810; + +// +// MessageId: ERROR_IPSEC_IKE_QUEUE_DROP_MM +// +// MessageText: +// +// Negotiation request sat in Queue too long +// +export const ERROR_IPSEC_IKE_QUEUE_DROP_MM = 13811; + +// +// MessageId: ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM +// +// MessageText: +// +// Negotiation request sat in Queue too long +// +export const ERROR_IPSEC_IKE_QUEUE_DROP_NO_MM = 13812; + +// +// MessageId: ERROR_IPSEC_IKE_DROP_NO_RESPONSE +// +// MessageText: +// +// No response from peer +// +export const ERROR_IPSEC_IKE_DROP_NO_RESPONSE = 13813; + +// +// MessageId: ERROR_IPSEC_IKE_MM_DELAY_DROP +// +// MessageText: +// +// Negotiation took too long +// +export const ERROR_IPSEC_IKE_MM_DELAY_DROP = 13814; + +// +// MessageId: ERROR_IPSEC_IKE_QM_DELAY_DROP +// +// MessageText: +// +// Negotiation took too long +// +export const ERROR_IPSEC_IKE_QM_DELAY_DROP = 13815; + +// +// MessageId: ERROR_IPSEC_IKE_ERROR +// +// MessageText: +// +// Unknown error occurred +// +export const ERROR_IPSEC_IKE_ERROR = 13816; + +// +// MessageId: ERROR_IPSEC_IKE_CRL_FAILED +// +// MessageText: +// +// Certificate Revocation Check failed +// +export const ERROR_IPSEC_IKE_CRL_FAILED = 13817; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_KEY_USAGE +// +// MessageText: +// +// Invalid certificate key usage +// +export const ERROR_IPSEC_IKE_INVALID_KEY_USAGE = 13818; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_CERT_TYPE +// +// MessageText: +// +// Invalid certificate type +// +export const ERROR_IPSEC_IKE_INVALID_CERT_TYPE = 13819; + +// +// MessageId: ERROR_IPSEC_IKE_NO_PRIVATE_KEY +// +// MessageText: +// +// No private key associated with machine certificate +// +export const ERROR_IPSEC_IKE_NO_PRIVATE_KEY = 13820; + +// +// MessageId: ERROR_IPSEC_IKE_DH_FAIL +// +// MessageText: +// +// Failure in Diffie-Hellman computation +// +export const ERROR_IPSEC_IKE_DH_FAIL = 13822; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_HEADER +// +// MessageText: +// +// Invalid header +// +export const ERROR_IPSEC_IKE_INVALID_HEADER = 13824; + +// +// MessageId: ERROR_IPSEC_IKE_NO_POLICY +// +// MessageText: +// +// No policy configured +// +export const ERROR_IPSEC_IKE_NO_POLICY = 13825; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_SIGNATURE +// +// MessageText: +// +// Failed to verify signature +// +export const ERROR_IPSEC_IKE_INVALID_SIGNATURE = 13826; + +// +// MessageId: ERROR_IPSEC_IKE_KERBEROS_ERROR +// +// MessageText: +// +// Failed to authenticate using kerberos +// +export const ERROR_IPSEC_IKE_KERBEROS_ERROR = 13827; + +// +// MessageId: ERROR_IPSEC_IKE_NO_PUBLIC_KEY +// +// MessageText: +// +// Peer's certificate did not have a public key +// +export const ERROR_IPSEC_IKE_NO_PUBLIC_KEY = 13828; + +// These must stay as a unit. +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR +// +// MessageText: +// +// Error processing error payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR = 13829; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_SA +// +// MessageText: +// +// Error processing SA payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_SA = 13830; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_PROP +// +// MessageText: +// +// Error processing Proposal payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_PROP = 13831; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_TRANS +// +// MessageText: +// +// Error processing Transform payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_TRANS = 13832; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_KE +// +// MessageText: +// +// Error processing KE payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_KE = 13833; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_ID +// +// MessageText: +// +// Error processing ID payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_ID = 13834; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_CERT +// +// MessageText: +// +// Error processing Cert payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_CERT = 13835; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ +// +// MessageText: +// +// Error processing Certificate Request payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_CERT_REQ = 13836; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_HASH +// +// MessageText: +// +// Error processing Hash payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_HASH = 13837; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_SIG +// +// MessageText: +// +// Error processing Signature payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_SIG = 13838; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_NONCE +// +// MessageText: +// +// Error processing Nonce payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_NONCE = 13839; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY +// +// MessageText: +// +// Error processing Notify payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_NOTIFY = 13840; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_DELETE +// +// MessageText: +// +// Error processing Delete Payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_DELETE = 13841; + +// +// MessageId: ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR +// +// MessageText: +// +// Error processing VendorId payload +// +export const ERROR_IPSEC_IKE_PROCESS_ERR_VENDOR = 13842; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_PAYLOAD +// +// MessageText: +// +// Invalid payload received +// +export const ERROR_IPSEC_IKE_INVALID_PAYLOAD = 13843; + +// +// MessageId: ERROR_IPSEC_IKE_LOAD_SOFT_SA +// +// MessageText: +// +// Soft SA loaded +// +export const ERROR_IPSEC_IKE_LOAD_SOFT_SA = 13844; + +// +// MessageId: ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN +// +// MessageText: +// +// Soft SA torn down +// +export const ERROR_IPSEC_IKE_SOFT_SA_TORN_DOWN = 13845; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_COOKIE +// +// MessageText: +// +// Invalid cookie received. +// +export const ERROR_IPSEC_IKE_INVALID_COOKIE = 13846; + +// +// MessageId: ERROR_IPSEC_IKE_NO_PEER_CERT +// +// MessageText: +// +// Peer failed to send valid machine certificate +// +export const ERROR_IPSEC_IKE_NO_PEER_CERT = 13847; + +// +// MessageId: ERROR_IPSEC_IKE_PEER_CRL_FAILED +// +// MessageText: +// +// Certification Revocation check of peer's certificate failed +// +export const ERROR_IPSEC_IKE_PEER_CRL_FAILED = 13848; + +// +// MessageId: ERROR_IPSEC_IKE_POLICY_CHANGE +// +// MessageText: +// +// New policy invalidated SAs formed with old policy +// +export const ERROR_IPSEC_IKE_POLICY_CHANGE = 13849; + +// +// MessageId: ERROR_IPSEC_IKE_NO_MM_POLICY +// +// MessageText: +// +// There is no available Main Mode IKE policy. +// +export const ERROR_IPSEC_IKE_NO_MM_POLICY = 13850; + +// +// MessageId: ERROR_IPSEC_IKE_NOTCBPRIV +// +// MessageText: +// +// Failed to enabled TCB privilege. +// +export const ERROR_IPSEC_IKE_NOTCBPRIV = 13851; + +// +// MessageId: ERROR_IPSEC_IKE_SECLOADFAIL +// +// MessageText: +// +// Failed to load SECURITY.DLL. +// +export const ERROR_IPSEC_IKE_SECLOADFAIL = 13852; + +// +// MessageId: ERROR_IPSEC_IKE_FAILSSPINIT +// +// MessageText: +// +// Failed to obtain security function table dispatch address from SSPI. +// +export const ERROR_IPSEC_IKE_FAILSSPINIT = 13853; + +// +// MessageId: ERROR_IPSEC_IKE_FAILQUERYSSP +// +// MessageText: +// +// Failed to query Kerberos package to obtain max token size. +// +export const ERROR_IPSEC_IKE_FAILQUERYSSP = 13854; + +// +// MessageId: ERROR_IPSEC_IKE_SRVACQFAIL +// +// MessageText: +// +// Failed to obtain Kerberos server credentials for ISAKMP/ERROR_IPSEC_IKE service. Kerberos authentication will not function. The most likely reason for this is lack of domain membership. This is normal if your computer is a member of a workgroup. +// +export const ERROR_IPSEC_IKE_SRVACQFAIL = 13855; + +// +// MessageId: ERROR_IPSEC_IKE_SRVQUERYCRED +// +// MessageText: +// +// Failed to determine SSPI principal name for ISAKMP/ERROR_IPSEC_IKE service (QueryCredentialsAttributes). +// +export const ERROR_IPSEC_IKE_SRVQUERYCRED = 13856; + +// +// MessageId: ERROR_IPSEC_IKE_GETSPIFAIL +// +// MessageText: +// +// Failed to obtain new SPI for the inbound SA from Ipsec driver. The most common cause for this is that the driver does not have the correct filter. Check your policy to verify the filters. +// +export const ERROR_IPSEC_IKE_GETSPIFAIL = 13857; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_FILTER +// +// MessageText: +// +// Given filter is invalid +// +export const ERROR_IPSEC_IKE_INVALID_FILTER = 13858; + +// +// MessageId: ERROR_IPSEC_IKE_OUT_OF_MEMORY +// +// MessageText: +// +// Memory allocation failed. +// +export const ERROR_IPSEC_IKE_OUT_OF_MEMORY = 13859; + +// +// MessageId: ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED +// +// MessageText: +// +// Failed to add Security Association to IPSec Driver. The most common cause for this is if the IKE negotiation took too long to complete. If the problem persists, reduce the load on the faulting machine. +// +export const ERROR_IPSEC_IKE_ADD_UPDATE_KEY_FAILED = 13860; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_POLICY +// +// MessageText: +// +// Invalid policy +// +export const ERROR_IPSEC_IKE_INVALID_POLICY = 13861; + +// +// MessageId: ERROR_IPSEC_IKE_UNKNOWN_DOI +// +// MessageText: +// +// Invalid DOI +// +export const ERROR_IPSEC_IKE_UNKNOWN_DOI = 13862; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_SITUATION +// +// MessageText: +// +// Invalid situation +// +export const ERROR_IPSEC_IKE_INVALID_SITUATION = 13863; + +// +// MessageId: ERROR_IPSEC_IKE_DH_FAILURE +// +// MessageText: +// +// Diffie-Hellman failure +// +export const ERROR_IPSEC_IKE_DH_FAILURE = 13864; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_GROUP +// +// MessageText: +// +// Invalid Diffie-Hellman group +// +export const ERROR_IPSEC_IKE_INVALID_GROUP = 13865; + +// +// MessageId: ERROR_IPSEC_IKE_ENCRYPT +// +// MessageText: +// +// Error encrypting payload +// +export const ERROR_IPSEC_IKE_ENCRYPT = 13866; + +// +// MessageId: ERROR_IPSEC_IKE_DECRYPT +// +// MessageText: +// +// Error decrypting payload +// +export const ERROR_IPSEC_IKE_DECRYPT = 13867; + +// +// MessageId: ERROR_IPSEC_IKE_POLICY_MATCH +// +// MessageText: +// +// Policy match error +// +export const ERROR_IPSEC_IKE_POLICY_MATCH = 13868; + +// +// MessageId: ERROR_IPSEC_IKE_UNSUPPORTED_ID +// +// MessageText: +// +// Unsupported ID +// +export const ERROR_IPSEC_IKE_UNSUPPORTED_ID = 13869; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_HASH +// +// MessageText: +// +// Hash verification failed +// +export const ERROR_IPSEC_IKE_INVALID_HASH = 13870; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_HASH_ALG +// +// MessageText: +// +// Invalid hash algorithm +// +export const ERROR_IPSEC_IKE_INVALID_HASH_ALG = 13871; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_HASH_SIZE +// +// MessageText: +// +// Invalid hash size +// +export const ERROR_IPSEC_IKE_INVALID_HASH_SIZE = 13872; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG +// +// MessageText: +// +// Invalid encryption algorithm +// +export const ERROR_IPSEC_IKE_INVALID_ENCRYPT_ALG = 13873; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_AUTH_ALG +// +// MessageText: +// +// Invalid authentication algorithm +// +export const ERROR_IPSEC_IKE_INVALID_AUTH_ALG = 13874; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_SIG +// +// MessageText: +// +// Invalid certificate signature +// +export const ERROR_IPSEC_IKE_INVALID_SIG = 13875; + +// +// MessageId: ERROR_IPSEC_IKE_LOAD_FAILED +// +// MessageText: +// +// Load failed +// +export const ERROR_IPSEC_IKE_LOAD_FAILED = 13876; + +// +// MessageId: ERROR_IPSEC_IKE_RPC_DELETE +// +// MessageText: +// +// Deleted via RPC call +// +export const ERROR_IPSEC_IKE_RPC_DELETE = 13877; + +// +// MessageId: ERROR_IPSEC_IKE_BENIGN_REINIT +// +// MessageText: +// +// Temporary state created to perform reinit. This is not a real failure. +// +export const ERROR_IPSEC_IKE_BENIGN_REINIT = 13878; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY +// +// MessageText: +// +// The lifetime value received in the Responder Lifetime Notify is below the Windows 2000 configured minimum value. Please fix the policy on the peer machine. +// +export const ERROR_IPSEC_IKE_INVALID_RESPONDER_LIFETIME_NOTIFY = 13879; + +// +// MessageId: ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN +// +// MessageText: +// +// Key length in certificate is too small for configured security requirements. +// +export const ERROR_IPSEC_IKE_INVALID_CERT_KEYLEN = 13881; + +// +// MessageId: ERROR_IPSEC_IKE_MM_LIMIT +// +// MessageText: +// +// Max number of established MM SAs to peer exceeded. +// +export const ERROR_IPSEC_IKE_MM_LIMIT = 13882; + +// +// MessageId: ERROR_IPSEC_IKE_NEGOTIATION_DISABLED +// +// MessageText: +// +// IKE received a policy that disables negotiation. +// +export const ERROR_IPSEC_IKE_NEGOTIATION_DISABLED = 13883; + +// +// MessageId: ERROR_IPSEC_IKE_NEG_STATUS_END +// +// MessageText: +// +// ERROR_IPSEC_IKE_NEG_STATUS_END +// +export const ERROR_IPSEC_IKE_NEG_STATUS_END = 13884; diff --git a/ext/node/polyfills/internal_binding/ares.ts b/ext/node/polyfills/internal_binding/ares.ts new file mode 100644 index 0000000000..1c9abb0381 --- /dev/null +++ b/ext/node/polyfills/internal_binding/ares.ts @@ -0,0 +1,67 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +/* Copyright 1998 by the Massachusetts Institute of Technology. + * + * Permission to use, copy, modify, and distribute this + * software and its documentation for any purpose and without + * fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting + * documentation, and that the name of M.I.T. not be used in + * advertising or publicity pertaining to distribution of the + * software without specific, written prior permission. + * M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" + * without express or implied warranty. + */ + +// REF: https://github.com/nodejs/node/blob/master/deps/cares/include/ares.h#L190 + +export const ARES_AI_CANONNAME = 1 << 0; +export const ARES_AI_NUMERICHOST = 1 << 1; +export const ARES_AI_PASSIVE = 1 << 2; +export const ARES_AI_NUMERICSERV = 1 << 3; +export const AI_V4MAPPED = 1 << 4; +export const AI_ALL = 1 << 5; +export const AI_ADDRCONFIG = 1 << 6; +export const ARES_AI_NOSORT = 1 << 7; +export const ARES_AI_ENVHOSTS = 1 << 8; + +// REF: https://github.com/nodejs/node/blob/master/deps/cares/src/lib/ares_strerror.c + +// deno-lint-ignore camelcase +export function ares_strerror(code: number) { + /* Return a string literal from a table. */ + const errorText = [ + "Successful completion", + "DNS server returned answer with no data", + "DNS server claims query was misformatted", + "DNS server returned general failure", + "Domain name not found", + "DNS server does not implement requested operation", + "DNS server refused query", + "Misformatted DNS query", + "Misformatted domain name", + "Unsupported address family", + "Misformatted DNS reply", + "Could not contact DNS servers", + "Timeout while contacting DNS servers", + "End of file", + "Error reading file", + "Out of memory", + "Channel is being destroyed", + "Misformatted string", + "Illegal flags specified", + "Given hostname is not numeric", + "Illegal hints flags specified", + "c-ares library initialization not yet performed", + "Error loading iphlpapi.dll", + "Could not find GetNetworkParams function", + "DNS query cancelled", + ]; + + if (code >= 0 && code < errorText.length) { + return errorText[code]; + } else { + return "unknown"; + } +} diff --git a/ext/node/polyfills/internal_binding/async_wrap.ts b/ext/node/polyfills/internal_binding/async_wrap.ts new file mode 100644 index 0000000000..c8df985cda --- /dev/null +++ b/ext/node/polyfills/internal_binding/async_wrap.ts @@ -0,0 +1,152 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/async_wrap-inl.h +// - https://github.com/nodejs/node/blob/master/src/async_wrap.cc +// - https://github.com/nodejs/node/blob/master/src/async_wrap.h + +export function registerDestroyHook( + // deno-lint-ignore no-explicit-any + _target: any, + _asyncId: number, + _prop: { destroyed: boolean }, +) { + // TODO(kt3k): implement actual procedures +} + +export enum constants { + kInit, + kBefore, + kAfter, + kDestroy, + kPromiseResolve, + kTotals, + kCheck, + kExecutionAsyncId, + kTriggerAsyncId, + kAsyncIdCounter, + kDefaultTriggerAsyncId, + kUsesExecutionAsyncResource, + kStackLength, +} + +const asyncHookFields = new Uint32Array(Object.keys(constants).length); + +export { asyncHookFields as async_hook_fields }; + +// Increment the internal id counter and return the value. +export function newAsyncId() { + return ++asyncIdFields[constants.kAsyncIdCounter]; +} + +export enum UidFields { + kExecutionAsyncId, + kTriggerAsyncId, + kAsyncIdCounter, + kDefaultTriggerAsyncId, + kUidFieldsCount, +} + +const asyncIdFields = new Float64Array(Object.keys(UidFields).length); + +// `kAsyncIdCounter` should start at `1` because that'll be the id the execution +// context during bootstrap. +asyncIdFields[UidFields.kAsyncIdCounter] = 1; + +// `kDefaultTriggerAsyncId` should be `-1`, this indicates that there is no +// specified default value and it should fallback to the executionAsyncId. +// 0 is not used as the magic value, because that indicates a missing +// context which is different from a default context. +asyncIdFields[UidFields.kDefaultTriggerAsyncId] = -1; + +export { asyncIdFields }; + +export enum providerType { + NONE, + DIRHANDLE, + DNSCHANNEL, + ELDHISTOGRAM, + FILEHANDLE, + FILEHANDLECLOSEREQ, + FIXEDSIZEBLOBCOPY, + FSEVENTWRAP, + FSREQCALLBACK, + FSREQPROMISE, + GETADDRINFOREQWRAP, + GETNAMEINFOREQWRAP, + HEAPSNAPSHOT, + HTTP2SESSION, + HTTP2STREAM, + HTTP2PING, + HTTP2SETTINGS, + HTTPINCOMINGMESSAGE, + HTTPCLIENTREQUEST, + JSSTREAM, + JSUDPWRAP, + MESSAGEPORT, + PIPECONNECTWRAP, + PIPESERVERWRAP, + PIPEWRAP, + PROCESSWRAP, + PROMISE, + QUERYWRAP, + SHUTDOWNWRAP, + SIGNALWRAP, + STATWATCHER, + STREAMPIPE, + TCPCONNECTWRAP, + TCPSERVERWRAP, + TCPWRAP, + TTYWRAP, + UDPSENDWRAP, + UDPWRAP, + SIGINTWATCHDOG, + WORKER, + WORKERHEAPSNAPSHOT, + WRITEWRAP, + ZLIB, +} + +const kInvalidAsyncId = -1; + +export class AsyncWrap { + provider: providerType = providerType.NONE; + asyncId = kInvalidAsyncId; + + constructor(provider: providerType) { + this.provider = provider; + this.getAsyncId(); + } + + getAsyncId(): number { + this.asyncId = this.asyncId === kInvalidAsyncId + ? newAsyncId() + : this.asyncId; + + return this.asyncId; + } + + getProviderType() { + return this.provider; + } +} diff --git a/ext/node/polyfills/internal_binding/buffer.ts b/ext/node/polyfills/internal_binding/buffer.ts new file mode 100644 index 0000000000..58e104481d --- /dev/null +++ b/ext/node/polyfills/internal_binding/buffer.ts @@ -0,0 +1,160 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Encodings } from "internal:deno_node/polyfills/internal_binding/_node.ts"; + +export function indexOfNeedle( + source: Uint8Array, + needle: Uint8Array, + start = 0, +): number { + if (start >= source.length) { + return -1; + } + if (start < 0) { + start = Math.max(0, source.length + start); + } + const s = needle[0]; + for (let i = start; i < source.length; i++) { + if (source[i] !== s) continue; + const pin = i; + let matched = 1; + let j = i; + while (matched < needle.length) { + j++; + if (source[j] !== needle[j - pin]) { + break; + } + matched++; + } + if (matched === needle.length) { + return pin; + } + } + return -1; +} + +export function numberToBytes(n: number): Uint8Array { + if (n === 0) return new Uint8Array([0]); + + const bytes = []; + bytes.unshift(n & 255); + while (n >= 256) { + n = n >>> 8; + bytes.unshift(n & 255); + } + return new Uint8Array(bytes); +} + +// TODO(Soremwar) +// Check if offset or buffer can be transform in order to just use std's lastIndexOf directly +// This implementation differs from std's lastIndexOf in the fact that +// it also includes items outside of the offset as long as part of the +// set is contained inside of the offset +// Probably way slower too +function findLastIndex( + targetBuffer: Uint8Array, + buffer: Uint8Array, + offset: number, +) { + offset = offset > targetBuffer.length ? targetBuffer.length : offset; + + const searchableBuffer = targetBuffer.slice(0, offset + buffer.length); + const searchableBufferLastIndex = searchableBuffer.length - 1; + const bufferLastIndex = buffer.length - 1; + + // Important to keep track of the last match index in order to backtrack after an incomplete match + // Not doing this will cause the search to skip all possible matches that happened in the + // last match range + let lastMatchIndex = -1; + let matches = 0; + let index = -1; + for (let x = 0; x <= searchableBufferLastIndex; x++) { + if ( + searchableBuffer[searchableBufferLastIndex - x] === + buffer[bufferLastIndex - matches] + ) { + if (lastMatchIndex === -1) { + lastMatchIndex = x; + } + matches++; + } else { + matches = 0; + if (lastMatchIndex !== -1) { + // Restart the search right after the last index was ignored + x = lastMatchIndex + 1; + lastMatchIndex = -1; + } + continue; + } + + if (matches === buffer.length) { + index = x; + break; + } + } + + if (index === -1) return index; + + return searchableBufferLastIndex - index; +} + +// TODO(@bartlomieju): +// Take encoding into account when evaluating index +function indexOfBuffer( + targetBuffer: Uint8Array, + buffer: Uint8Array, + byteOffset: number, + encoding: Encodings, + forwardDirection: boolean, +) { + if (!Encodings[encoding] === undefined) { + throw new Error(`Unknown encoding code ${encoding}`); + } + + if (!forwardDirection) { + // If negative the offset is calculated from the end of the buffer + + if (byteOffset < 0) { + byteOffset = targetBuffer.length + byteOffset; + } + + if (buffer.length === 0) { + return byteOffset <= targetBuffer.length + ? byteOffset + : targetBuffer.length; + } + + return findLastIndex(targetBuffer, buffer, byteOffset); + } + + if (buffer.length === 0) { + return byteOffset <= targetBuffer.length ? byteOffset : targetBuffer.length; + } + + return indexOfNeedle(targetBuffer, buffer, byteOffset); +} + +// TODO(Soremwar) +// Node's implementation is a very obscure algorithm that I haven't been able to crack just yet +function indexOfNumber( + targetBuffer: Uint8Array, + number: number, + byteOffset: number, + forwardDirection: boolean, +) { + const bytes = numberToBytes(number); + + if (bytes.length > 1) { + throw new Error("Multi byte number search is not supported"); + } + + return indexOfBuffer( + targetBuffer, + numberToBytes(number), + byteOffset, + Encodings.UTF8, + forwardDirection, + ); +} + +export default { indexOfBuffer, indexOfNumber }; +export { indexOfBuffer, indexOfNumber }; diff --git a/ext/node/polyfills/internal_binding/cares_wrap.ts b/ext/node/polyfills/internal_binding/cares_wrap.ts new file mode 100644 index 0000000000..1345e94638 --- /dev/null +++ b/ext/node/polyfills/internal_binding/cares_wrap.ts @@ -0,0 +1,545 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/cares_wrap.cc +// - https://github.com/nodejs/node/blob/master/src/cares_wrap.h + +import type { ErrnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { isIPv4 } from "internal:deno_node/polyfills/internal/net.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { + AsyncWrap, + providerType, +} from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; +// deno-lint-ignore camelcase +import { ares_strerror } from "internal:deno_node/polyfills/internal_binding/ares.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; + +interface LookupAddress { + address: string; + family: number; +} + +export class GetAddrInfoReqWrap extends AsyncWrap { + family!: number; + hostname!: string; + + callback!: ( + err: ErrnoException | null, + addressOrAddresses?: string | LookupAddress[] | null, + family?: number, + ) => void; + resolve!: (addressOrAddresses: LookupAddress | LookupAddress[]) => void; + reject!: (err: ErrnoException | null) => void; + oncomplete!: (err: number | null, addresses: string[]) => void; + + constructor() { + super(providerType.GETADDRINFOREQWRAP); + } +} + +export function getaddrinfo( + req: GetAddrInfoReqWrap, + hostname: string, + family: number, + _hints: number, + verbatim: boolean, +): number { + let addresses: string[] = []; + + // TODO(cmorten): use hints + // REF: https://nodejs.org/api/dns.html#dns_supported_getaddrinfo_flags + + const recordTypes: ("A" | "AAAA")[] = []; + + if (family === 0 || family === 4) { + recordTypes.push("A"); + } + if (family === 0 || family === 6) { + recordTypes.push("AAAA"); + } + + (async () => { + await Promise.allSettled( + recordTypes.map((recordType) => + Deno.resolveDns(hostname, recordType).then((records) => { + records.forEach((record) => addresses.push(record)); + }) + ), + ); + + const error = addresses.length ? 0 : codeMap.get("EAI_NODATA")!; + + // TODO(cmorten): needs work + // REF: https://github.com/nodejs/node/blob/master/src/cares_wrap.cc#L1444 + if (!verbatim) { + addresses.sort((a: string, b: string): number => { + if (isIPv4(a)) { + return -1; + } else if (isIPv4(b)) { + return 1; + } + + return 0; + }); + } + + // TODO(@bartlomieju): Forces IPv4 as a workaround for Deno not + // aligning with Node on implicit binding on Windows + // REF: https://github.com/denoland/deno/issues/10762 + if (isWindows && hostname === "localhost") { + addresses = addresses.filter((address) => isIPv4(address)); + } + + req.oncomplete(error, addresses); + })(); + + return 0; +} + +export class QueryReqWrap extends AsyncWrap { + bindingName!: string; + hostname!: string; + ttl!: boolean; + + callback!: ( + err: ErrnoException | null, + // deno-lint-ignore no-explicit-any + records?: any, + ) => void; + // deno-lint-ignore no-explicit-any + resolve!: (records: any) => void; + reject!: (err: ErrnoException | null) => void; + oncomplete!: ( + err: number, + // deno-lint-ignore no-explicit-any + records: any, + ttls?: number[], + ) => void; + + constructor() { + super(providerType.QUERYWRAP); + } +} + +export interface ChannelWrapQuery { + queryAny(req: QueryReqWrap, name: string): number; + queryA(req: QueryReqWrap, name: string): number; + queryAaaa(req: QueryReqWrap, name: string): number; + queryCaa(req: QueryReqWrap, name: string): number; + queryCname(req: QueryReqWrap, name: string): number; + queryMx(req: QueryReqWrap, name: string): number; + queryNs(req: QueryReqWrap, name: string): number; + queryTxt(req: QueryReqWrap, name: string): number; + querySrv(req: QueryReqWrap, name: string): number; + queryPtr(req: QueryReqWrap, name: string): number; + queryNaptr(req: QueryReqWrap, name: string): number; + querySoa(req: QueryReqWrap, name: string): number; + getHostByAddr(req: QueryReqWrap, name: string): number; +} + +function fqdnToHostname(fqdn: string): string { + return fqdn.replace(/\.$/, ""); +} + +function compressIPv6(address: string): string { + const formatted = address.replace(/\b(?:0+:){2,}/, ":"); + const finalAddress = formatted + .split(":") + .map((octet) => { + if (octet.match(/^\d+\.\d+\.\d+\.\d+$/)) { + // decimal + return Number(octet.replaceAll(".", "")).toString(16); + } + + return octet.replace(/\b0+/g, ""); + }) + .join(":"); + + return finalAddress; +} + +export class ChannelWrap extends AsyncWrap implements ChannelWrapQuery { + #servers: [string, number][] = []; + #timeout: number; + #tries: number; + + constructor(timeout: number, tries: number) { + super(providerType.DNSCHANNEL); + + this.#timeout = timeout; + this.#tries = tries; + } + + async #query(query: string, recordType: Deno.RecordType) { + // TODO(@bartlomieju): TTL logic. + + let code: number; + let ret: Awaited>; + + if (this.#servers.length) { + for (const [ipAddr, port] of this.#servers) { + const resolveOptions = { + nameServer: { + ipAddr, + port, + }, + }; + + ({ code, ret } = await this.#resolve( + query, + recordType, + resolveOptions, + )); + + if (code === 0 || code === codeMap.get("EAI_NODATA")!) { + break; + } + } + } else { + ({ code, ret } = await this.#resolve(query, recordType)); + } + + return { code: code!, ret: ret! }; + } + + async #resolve( + query: string, + recordType: Deno.RecordType, + resolveOptions?: Deno.ResolveDnsOptions, + ): Promise<{ + code: number; + ret: Awaited>; + }> { + let ret: Awaited> = []; + let code = 0; + + try { + ret = await Deno.resolveDns(query, recordType, resolveOptions); + } catch (e) { + if (e instanceof Deno.errors.NotFound) { + code = codeMap.get("EAI_NODATA")!; + } else { + // TODO(cmorten): map errors to appropriate error codes. + code = codeMap.get("UNKNOWN")!; + } + } + + return { code, ret }; + } + + queryAny(req: QueryReqWrap, name: string): number { + // TODO(@bartlomieju): implemented temporary measure to allow limited usage of + // `resolveAny` like APIs. + // + // Ideally we move to using the "ANY" / "*" DNS query in future + // REF: https://github.com/denoland/deno/issues/14492 + (async () => { + const records: { type: Deno.RecordType; [key: string]: unknown }[] = []; + + await Promise.allSettled([ + this.#query(name, "A").then(({ ret }) => { + ret.forEach((record) => records.push({ type: "A", address: record })); + }), + this.#query(name, "AAAA").then(({ ret }) => { + (ret as string[]).forEach((record) => + records.push({ type: "AAAA", address: compressIPv6(record) }) + ); + }), + this.#query(name, "CAA").then(({ ret }) => { + (ret as Deno.CAARecord[]).forEach(({ critical, tag, value }) => + records.push({ + type: "CAA", + [tag]: value, + critical: +critical && 128, + }) + ); + }), + this.#query(name, "CNAME").then(({ ret }) => { + ret.forEach((record) => + records.push({ type: "CNAME", value: record }) + ); + }), + this.#query(name, "MX").then(({ ret }) => { + (ret as Deno.MXRecord[]).forEach(({ preference, exchange }) => + records.push({ + type: "MX", + priority: preference, + exchange: fqdnToHostname(exchange), + }) + ); + }), + this.#query(name, "NAPTR").then(({ ret }) => { + (ret as Deno.NAPTRRecord[]).forEach( + ({ order, preference, flags, services, regexp, replacement }) => + records.push({ + type: "NAPTR", + order, + preference, + flags, + service: services, + regexp, + replacement, + }), + ); + }), + this.#query(name, "NS").then(({ ret }) => { + (ret as string[]).forEach((record) => + records.push({ type: "NS", value: fqdnToHostname(record) }) + ); + }), + this.#query(name, "PTR").then(({ ret }) => { + (ret as string[]).forEach((record) => + records.push({ type: "PTR", value: fqdnToHostname(record) }) + ); + }), + this.#query(name, "SOA").then(({ ret }) => { + (ret as Deno.SOARecord[]).forEach( + ({ mname, rname, serial, refresh, retry, expire, minimum }) => + records.push({ + type: "SOA", + nsname: fqdnToHostname(mname), + hostmaster: fqdnToHostname(rname), + serial, + refresh, + retry, + expire, + minttl: minimum, + }), + ); + }), + this.#query(name, "SRV").then(({ ret }) => { + (ret as Deno.SRVRecord[]).forEach( + ({ priority, weight, port, target }) => + records.push({ + type: "SRV", + priority, + weight, + port, + name: target, + }), + ); + }), + this.#query(name, "TXT").then(({ ret }) => { + ret.forEach((record) => + records.push({ type: "TXT", entries: record }) + ); + }), + ]); + + const err = records.length ? 0 : codeMap.get("EAI_NODATA")!; + + req.oncomplete(err, records); + })(); + + return 0; + } + + queryA(req: QueryReqWrap, name: string): number { + this.#query(name, "A").then(({ code, ret }) => { + req.oncomplete(code, ret); + }); + + return 0; + } + + queryAaaa(req: QueryReqWrap, name: string): number { + this.#query(name, "AAAA").then(({ code, ret }) => { + const records = (ret as string[]).map((record) => compressIPv6(record)); + + req.oncomplete(code, records); + }); + + return 0; + } + + queryCaa(req: QueryReqWrap, name: string): number { + this.#query(name, "CAA").then(({ code, ret }) => { + const records = (ret as Deno.CAARecord[]).map( + ({ critical, tag, value }) => ({ + [tag]: value, + critical: +critical && 128, + }), + ); + + req.oncomplete(code, records); + }); + + return 0; + } + + queryCname(req: QueryReqWrap, name: string): number { + this.#query(name, "CNAME").then(({ code, ret }) => { + req.oncomplete(code, ret); + }); + + return 0; + } + + queryMx(req: QueryReqWrap, name: string): number { + this.#query(name, "MX").then(({ code, ret }) => { + const records = (ret as Deno.MXRecord[]).map( + ({ preference, exchange }) => ({ + priority: preference, + exchange: fqdnToHostname(exchange), + }), + ); + + req.oncomplete(code, records); + }); + + return 0; + } + + queryNaptr(req: QueryReqWrap, name: string): number { + this.#query(name, "NAPTR").then(({ code, ret }) => { + const records = (ret as Deno.NAPTRRecord[]).map( + ({ order, preference, flags, services, regexp, replacement }) => ({ + flags, + service: services, + regexp, + replacement, + order, + preference, + }), + ); + + req.oncomplete(code, records); + }); + + return 0; + } + + queryNs(req: QueryReqWrap, name: string): number { + this.#query(name, "NS").then(({ code, ret }) => { + const records = (ret as string[]).map((record) => fqdnToHostname(record)); + + req.oncomplete(code, records); + }); + + return 0; + } + + queryPtr(req: QueryReqWrap, name: string): number { + this.#query(name, "PTR").then(({ code, ret }) => { + const records = (ret as string[]).map((record) => fqdnToHostname(record)); + + req.oncomplete(code, records); + }); + + return 0; + } + + querySoa(req: QueryReqWrap, name: string): number { + this.#query(name, "SOA").then(({ code, ret }) => { + let record = {}; + + if (ret.length) { + const { mname, rname, serial, refresh, retry, expire, minimum } = + ret[0] as Deno.SOARecord; + + record = { + nsname: fqdnToHostname(mname), + hostmaster: fqdnToHostname(rname), + serial, + refresh, + retry, + expire, + minttl: minimum, + }; + } + + req.oncomplete(code, record); + }); + + return 0; + } + + querySrv(req: QueryReqWrap, name: string): number { + this.#query(name, "SRV").then(({ code, ret }) => { + const records = (ret as Deno.SRVRecord[]).map( + ({ priority, weight, port, target }) => ({ + priority, + weight, + port, + name: target, + }), + ); + + req.oncomplete(code, records); + }); + + return 0; + } + + queryTxt(req: QueryReqWrap, name: string): number { + this.#query(name, "TXT").then(({ code, ret }) => { + req.oncomplete(code, ret); + }); + + return 0; + } + + getHostByAddr(_req: QueryReqWrap, _name: string): number { + // TODO(@bartlomieju): https://github.com/denoland/deno/issues/14432 + notImplemented("cares.ChannelWrap.prototype.getHostByAddr"); + } + + getServers(): [string, number][] { + return this.#servers; + } + + setServers(servers: string | [number, string, number][]): number { + if (typeof servers === "string") { + const tuples: [string, number][] = []; + + for (let i = 0; i < servers.length; i += 2) { + tuples.push([servers[i], parseInt(servers[i + 1])]); + } + + this.#servers = tuples; + } else { + this.#servers = servers.map(([_ipVersion, ip, port]) => [ip, port]); + } + + return 0; + } + + setLocalAddress(_addr0: string, _addr1?: string) { + notImplemented("cares.ChannelWrap.prototype.setLocalAddress"); + } + + cancel() { + notImplemented("cares.ChannelWrap.prototype.cancel"); + } +} + +const DNS_ESETSRVPENDING = -1000; +const EMSG_ESETSRVPENDING = "There are pending queries."; + +export function strerror(code: number) { + return code === DNS_ESETSRVPENDING + ? EMSG_ESETSRVPENDING + : ares_strerror(code); +} diff --git a/ext/node/polyfills/internal_binding/config.ts b/ext/node/polyfills/internal_binding/config.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/config.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/connection_wrap.ts b/ext/node/polyfills/internal_binding/connection_wrap.ts new file mode 100644 index 0000000000..1ce95cc1bf --- /dev/null +++ b/ext/node/polyfills/internal_binding/connection_wrap.ts @@ -0,0 +1,94 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/connection_wrap.cc +// - https://github.com/nodejs/node/blob/master/src/connection_wrap.h + +import { LibuvStreamWrap } from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { + AsyncWrap, + providerType, +} from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; + +interface Reader { + read(p: Uint8Array): Promise; +} + +interface Writer { + write(p: Uint8Array): Promise; +} + +export interface Closer { + close(): void; +} + +type Ref = { ref(): void; unref(): void }; + +export class ConnectionWrap extends LibuvStreamWrap { + /** Optional connection callback. */ + onconnection: ((status: number, handle?: ConnectionWrap) => void) | null = + null; + + /** + * Creates a new ConnectionWrap class instance. + * @param provider Provider type. + * @param object Optional stream object. + */ + constructor( + provider: providerType, + object?: Reader & Writer & Closer & Ref, + ) { + super(provider, object); + } + + /** + * @param req A connect request. + * @param status An error status code. + */ + afterConnect< + T extends AsyncWrap & { + oncomplete( + status: number, + handle: ConnectionWrap, + req: T, + readable: boolean, + writeable: boolean, + ): void; + }, + >( + req: T, + status: number, + ) { + const isSuccessStatus = !status; + const readable = isSuccessStatus; + const writable = isSuccessStatus; + + try { + req.oncomplete(status, this, req, readable, writable); + } catch { + // swallow callback errors. + } + + return; + } +} diff --git a/ext/node/polyfills/internal_binding/constants.ts b/ext/node/polyfills/internal_binding/constants.ts new file mode 100644 index 0000000000..e6fd2d3ea0 --- /dev/null +++ b/ext/node/polyfills/internal_binding/constants.ts @@ -0,0 +1,903 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +let os: { + dlopen: { + RTLD_DEEPBIND?: number; + RTLD_GLOBAL?: number; + RTLD_LAZY?: number; + RTLD_LOCAL?: number; + RTLD_NOW?: number; + }; + errno: { + E2BIG: number; + EACCES: number; + EADDRINUSE: number; + EADDRNOTAVAIL: number; + EAFNOSUPPORT: number; + EAGAIN: number; + EALREADY: number; + EBADF: number; + EBADMSG: number; + EBUSY: number; + ECANCELED: number; + ECHILD: number; + ECONNABORTED: number; + ECONNREFUSED: number; + ECONNRESET: number; + EDEADLK: number; + EDESTADDRREQ: number; + EDOM: number; + EDQUOT?: number; + EEXIST: number; + EFAULT: number; + EFBIG: number; + EHOSTUNREACH: number; + EIDRM: number; + EILSEQ: number; + EINPROGRESS: number; + EINTR: number; + EINVAL: number; + EIO: number; + EISCONN: number; + EISDIR: number; + ELOOP: number; + EMFILE: number; + EMLINK: number; + EMSGSIZE: number; + EMULTIHOP?: number; + ENAMETOOLONG: number; + ENETDOWN: number; + ENETRESET: number; + ENETUNREACH: number; + ENFILE: number; + ENOBUFS: number; + ENODATA: number; + ENODEV: number; + ENOENT: number; + ENOEXEC: number; + ENOLCK: number; + ENOLINK: number; + ENOMEM: number; + ENOMSG: number; + ENOPROTOOPT: number; + ENOSPC: number; + ENOSR: number; + ENOSTR: number; + ENOSYS: number; + ENOTCONN: number; + ENOTDIR: number; + ENOTEMPTY: number; + ENOTSOCK: number; + ENOTSUP: number; + ENOTTY: number; + ENXIO: number; + EOPNOTSUPP: number; + EOVERFLOW: number; + EPERM: number; + EPIPE: number; + EPROTO: number; + EPROTONOSUPPORT: number; + EPROTOTYPE: number; + ERANGE: number; + EROFS: number; + ESPIPE: number; + ESRCH: number; + ESTALE?: number; + ETIME: number; + ETIMEDOUT: number; + ETXTBSY: number; + EWOULDBLOCK: number; + EXDEV: number; + WSA_E_CANCELLED?: number; + WSA_E_NO_MORE?: number; + WSAEACCES?: number; + WSAEADDRINUSE?: number; + WSAEADDRNOTAVAIL?: number; + WSAEAFNOSUPPORT?: number; + WSAEALREADY?: number; + WSAEBADF?: number; + WSAECANCELLED?: number; + WSAECONNABORTED?: number; + WSAECONNREFUSED?: number; + WSAECONNRESET?: number; + WSAEDESTADDRREQ?: number; + WSAEDISCON?: number; + WSAEDQUOT?: number; + WSAEFAULT?: number; + WSAEHOSTDOWN?: number; + WSAEHOSTUNREACH?: number; + WSAEINPROGRESS?: number; + WSAEINTR?: number; + WSAEINVAL?: number; + WSAEINVALIDPROCTABLE?: number; + WSAEINVALIDPROVIDER?: number; + WSAEISCONN?: number; + WSAELOOP?: number; + WSAEMFILE?: number; + WSAEMSGSIZE?: number; + WSAENAMETOOLONG?: number; + WSAENETDOWN?: number; + WSAENETRESET?: number; + WSAENETUNREACH?: number; + WSAENOBUFS?: number; + WSAENOMORE?: number; + WSAENOPROTOOPT?: number; + WSAENOTCONN?: number; + WSAENOTEMPTY?: number; + WSAENOTSOCK?: number; + WSAEOPNOTSUPP?: number; + WSAEPFNOSUPPORT?: number; + WSAEPROCLIM?: number; + WSAEPROTONOSUPPORT?: number; + WSAEPROTOTYPE?: number; + WSAEPROVIDERFAILEDINIT?: number; + WSAEREFUSED?: number; + WSAEREMOTE?: number; + WSAESHUTDOWN?: number; + WSAESOCKTNOSUPPORT?: number; + WSAESTALE?: number; + WSAETIMEDOUT?: number; + WSAETOOMANYREFS?: number; + WSAEUSERS?: number; + WSAEWOULDBLOCK?: number; + WSANOTINITIALISED?: number; + WSASERVICE_NOT_FOUND?: number; + WSASYSCALLFAILURE?: number; + WSASYSNOTREADY?: number; + WSATYPE_NOT_FOUND?: number; + WSAVERNOTSUPPORTED?: number; + }; + priority: { + PRIORITY_ABOVE_NORMAL: number; + PRIORITY_BELOW_NORMAL: number; + PRIORITY_HIGH: number; + PRIORITY_HIGHEST: number; + PRIORITY_LOW: number; + PRIORITY_NORMAL: number; + }; + signals: { + SIGABRT: number; + SIGALRM?: number; + SIGBREAK?: number; + SIGBUS?: number; + SIGCHLD?: number; + SIGCONT?: number; + SIGFPE: number; + SIGHUP: number; + SIGILL: number; + SIGINFO?: number; + SIGINT: number; + SIGIO?: number; + SIGIOT?: number; + SIGKILL: number; + SIGPIPE?: number; + SIGPOLL?: number; + SIGPROF?: number; + SIGPWR?: number; + SIGQUIT?: number; + SIGSEGV: number; + SIGSTKFLT?: number; + SIGSTOP?: number; + SIGSYS?: number; + SIGTERM: number; + SIGTRAP?: number; + SIGTSTP?: number; + SIGTTIN?: number; + SIGTTOU?: number; + SIGUNUSED?: number; + SIGURG?: number; + SIGUSR1?: number; + SIGUSR2?: number; + SIGVTALRM?: number; + SIGWINCH: number; + SIGXCPU?: number; + SIGXFSZ?: number; + }; + UV_UDP_IPV6ONLY?: number; + UV_UDP_REUSEADDR: number; +}; + +const core = globalThis.__bootstrap.core; +const buildOs = core.ops.op_node_build_os(); +if (buildOs === "darwin") { + os = { + UV_UDP_REUSEADDR: 4, + dlopen: { + RTLD_LAZY: 1, + RTLD_NOW: 2, + RTLD_GLOBAL: 8, + RTLD_LOCAL: 4, + }, + errno: { + E2BIG: 7, + EACCES: 13, + EADDRINUSE: 48, + EADDRNOTAVAIL: 49, + EAFNOSUPPORT: 47, + EAGAIN: 35, + EALREADY: 37, + EBADF: 9, + EBADMSG: 94, + EBUSY: 16, + ECANCELED: 89, + ECHILD: 10, + ECONNABORTED: 53, + ECONNREFUSED: 61, + ECONNRESET: 54, + EDEADLK: 11, + EDESTADDRREQ: 39, + EDOM: 33, + EDQUOT: 69, + EEXIST: 17, + EFAULT: 14, + EFBIG: 27, + EHOSTUNREACH: 65, + EIDRM: 90, + EILSEQ: 92, + EINPROGRESS: 36, + EINTR: 4, + EINVAL: 22, + EIO: 5, + EISCONN: 56, + EISDIR: 21, + ELOOP: 62, + EMFILE: 24, + EMLINK: 31, + EMSGSIZE: 40, + EMULTIHOP: 95, + ENAMETOOLONG: 63, + ENETDOWN: 50, + ENETRESET: 52, + ENETUNREACH: 51, + ENFILE: 23, + ENOBUFS: 55, + ENODATA: 96, + ENODEV: 19, + ENOENT: 2, + ENOEXEC: 8, + ENOLCK: 77, + ENOLINK: 97, + ENOMEM: 12, + ENOMSG: 91, + ENOPROTOOPT: 42, + ENOSPC: 28, + ENOSR: 98, + ENOSTR: 99, + ENOSYS: 78, + ENOTCONN: 57, + ENOTDIR: 20, + ENOTEMPTY: 66, + ENOTSOCK: 38, + ENOTSUP: 45, + ENOTTY: 25, + ENXIO: 6, + EOPNOTSUPP: 102, + EOVERFLOW: 84, + EPERM: 1, + EPIPE: 32, + EPROTO: 100, + EPROTONOSUPPORT: 43, + EPROTOTYPE: 41, + ERANGE: 34, + EROFS: 30, + ESPIPE: 29, + ESRCH: 3, + ESTALE: 70, + ETIME: 101, + ETIMEDOUT: 60, + ETXTBSY: 26, + EWOULDBLOCK: 35, + EXDEV: 18, + }, + signals: { + SIGHUP: 1, + SIGINT: 2, + SIGQUIT: 3, + SIGILL: 4, + SIGTRAP: 5, + SIGABRT: 6, + SIGIOT: 6, + SIGBUS: 10, + SIGFPE: 8, + SIGKILL: 9, + SIGUSR1: 30, + SIGSEGV: 11, + SIGUSR2: 31, + SIGPIPE: 13, + SIGALRM: 14, + SIGTERM: 15, + SIGCHLD: 20, + SIGCONT: 19, + SIGSTOP: 17, + SIGTSTP: 18, + SIGTTIN: 21, + SIGTTOU: 22, + SIGURG: 16, + SIGXCPU: 24, + SIGXFSZ: 25, + SIGVTALRM: 26, + SIGPROF: 27, + SIGWINCH: 28, + SIGIO: 23, + SIGINFO: 29, + SIGSYS: 12, + }, + priority: { + PRIORITY_LOW: 19, + PRIORITY_BELOW_NORMAL: 10, + PRIORITY_NORMAL: 0, + PRIORITY_ABOVE_NORMAL: -7, + PRIORITY_HIGH: -14, + PRIORITY_HIGHEST: -20, + }, + }; +} else if (buildOs === "linux") { + os = { + UV_UDP_REUSEADDR: 4, + dlopen: { + RTLD_LAZY: 1, + RTLD_NOW: 2, + RTLD_GLOBAL: 256, + RTLD_LOCAL: 0, + RTLD_DEEPBIND: 8, + }, + errno: { + E2BIG: 7, + EACCES: 13, + EADDRINUSE: 98, + EADDRNOTAVAIL: 99, + EAFNOSUPPORT: 97, + EAGAIN: 11, + EALREADY: 114, + EBADF: 9, + EBADMSG: 74, + EBUSY: 16, + ECANCELED: 125, + ECHILD: 10, + ECONNABORTED: 103, + ECONNREFUSED: 111, + ECONNRESET: 104, + EDEADLK: 35, + EDESTADDRREQ: 89, + EDOM: 33, + EDQUOT: 122, + EEXIST: 17, + EFAULT: 14, + EFBIG: 27, + EHOSTUNREACH: 113, + EIDRM: 43, + EILSEQ: 84, + EINPROGRESS: 115, + EINTR: 4, + EINVAL: 22, + EIO: 5, + EISCONN: 106, + EISDIR: 21, + ELOOP: 40, + EMFILE: 24, + EMLINK: 31, + EMSGSIZE: 90, + EMULTIHOP: 72, + ENAMETOOLONG: 36, + ENETDOWN: 100, + ENETRESET: 102, + ENETUNREACH: 101, + ENFILE: 23, + ENOBUFS: 105, + ENODATA: 61, + ENODEV: 19, + ENOENT: 2, + ENOEXEC: 8, + ENOLCK: 37, + ENOLINK: 67, + ENOMEM: 12, + ENOMSG: 42, + ENOPROTOOPT: 92, + ENOSPC: 28, + ENOSR: 63, + ENOSTR: 60, + ENOSYS: 38, + ENOTCONN: 107, + ENOTDIR: 20, + ENOTEMPTY: 39, + ENOTSOCK: 88, + ENOTSUP: 95, + ENOTTY: 25, + ENXIO: 6, + EOPNOTSUPP: 95, + EOVERFLOW: 75, + EPERM: 1, + EPIPE: 32, + EPROTO: 71, + EPROTONOSUPPORT: 93, + EPROTOTYPE: 91, + ERANGE: 34, + EROFS: 30, + ESPIPE: 29, + ESRCH: 3, + ESTALE: 116, + ETIME: 62, + ETIMEDOUT: 110, + ETXTBSY: 26, + EWOULDBLOCK: 11, + EXDEV: 18, + }, + signals: { + SIGHUP: 1, + SIGINT: 2, + SIGQUIT: 3, + SIGILL: 4, + SIGTRAP: 5, + SIGABRT: 6, + SIGIOT: 6, + SIGBUS: 7, + SIGFPE: 8, + SIGKILL: 9, + SIGUSR1: 10, + SIGSEGV: 11, + SIGUSR2: 12, + SIGPIPE: 13, + SIGALRM: 14, + SIGTERM: 15, + SIGCHLD: 17, + SIGSTKFLT: 16, + SIGCONT: 18, + SIGSTOP: 19, + SIGTSTP: 20, + SIGTTIN: 21, + SIGTTOU: 22, + SIGURG: 23, + SIGXCPU: 24, + SIGXFSZ: 25, + SIGVTALRM: 26, + SIGPROF: 27, + SIGWINCH: 28, + SIGIO: 29, + SIGPOLL: 29, + SIGPWR: 30, + SIGSYS: 31, + SIGUNUSED: 31, + }, + priority: { + PRIORITY_LOW: 19, + PRIORITY_BELOW_NORMAL: 10, + PRIORITY_NORMAL: 0, + PRIORITY_ABOVE_NORMAL: -7, + PRIORITY_HIGH: -14, + PRIORITY_HIGHEST: -20, + }, + }; +} else { + os = { + UV_UDP_REUSEADDR: 4, + dlopen: {}, + errno: { + E2BIG: 7, + EACCES: 13, + EADDRINUSE: 100, + EADDRNOTAVAIL: 101, + EAFNOSUPPORT: 102, + EAGAIN: 11, + EALREADY: 103, + EBADF: 9, + EBADMSG: 104, + EBUSY: 16, + ECANCELED: 105, + ECHILD: 10, + ECONNABORTED: 106, + ECONNREFUSED: 107, + ECONNRESET: 108, + EDEADLK: 36, + EDESTADDRREQ: 109, + EDOM: 33, + EEXIST: 17, + EFAULT: 14, + EFBIG: 27, + EHOSTUNREACH: 110, + EIDRM: 111, + EILSEQ: 42, + EINPROGRESS: 112, + EINTR: 4, + EINVAL: 22, + EIO: 5, + EISCONN: 113, + EISDIR: 21, + ELOOP: 114, + EMFILE: 24, + EMLINK: 31, + EMSGSIZE: 115, + ENAMETOOLONG: 38, + ENETDOWN: 116, + ENETRESET: 117, + ENETUNREACH: 118, + ENFILE: 23, + ENOBUFS: 119, + ENODATA: 120, + ENODEV: 19, + ENOENT: 2, + ENOEXEC: 8, + ENOLCK: 39, + ENOLINK: 121, + ENOMEM: 12, + ENOMSG: 122, + ENOPROTOOPT: 123, + ENOSPC: 28, + ENOSR: 124, + ENOSTR: 125, + ENOSYS: 40, + ENOTCONN: 126, + ENOTDIR: 20, + ENOTEMPTY: 41, + ENOTSOCK: 128, + ENOTSUP: 129, + ENOTTY: 25, + ENXIO: 6, + EOPNOTSUPP: 130, + EOVERFLOW: 132, + EPERM: 1, + EPIPE: 32, + EPROTO: 134, + EPROTONOSUPPORT: 135, + EPROTOTYPE: 136, + ERANGE: 34, + EROFS: 30, + ESPIPE: 29, + ESRCH: 3, + ETIME: 137, + ETIMEDOUT: 138, + ETXTBSY: 139, + EWOULDBLOCK: 140, + EXDEV: 18, + WSAEINTR: 10004, + WSAEBADF: 10009, + WSAEACCES: 10013, + WSAEFAULT: 10014, + WSAEINVAL: 10022, + WSAEMFILE: 10024, + WSAEWOULDBLOCK: 10035, + WSAEINPROGRESS: 10036, + WSAEALREADY: 10037, + WSAENOTSOCK: 10038, + WSAEDESTADDRREQ: 10039, + WSAEMSGSIZE: 10040, + WSAEPROTOTYPE: 10041, + WSAENOPROTOOPT: 10042, + WSAEPROTONOSUPPORT: 10043, + WSAESOCKTNOSUPPORT: 10044, + WSAEOPNOTSUPP: 10045, + WSAEPFNOSUPPORT: 10046, + WSAEAFNOSUPPORT: 10047, + WSAEADDRINUSE: 10048, + WSAEADDRNOTAVAIL: 10049, + WSAENETDOWN: 10050, + WSAENETUNREACH: 10051, + WSAENETRESET: 10052, + WSAECONNABORTED: 10053, + WSAECONNRESET: 10054, + WSAENOBUFS: 10055, + WSAEISCONN: 10056, + WSAENOTCONN: 10057, + WSAESHUTDOWN: 10058, + WSAETOOMANYREFS: 10059, + WSAETIMEDOUT: 10060, + WSAECONNREFUSED: 10061, + WSAELOOP: 10062, + WSAENAMETOOLONG: 10063, + WSAEHOSTDOWN: 10064, + WSAEHOSTUNREACH: 10065, + WSAENOTEMPTY: 10066, + WSAEPROCLIM: 10067, + WSAEUSERS: 10068, + WSAEDQUOT: 10069, + WSAESTALE: 10070, + WSAEREMOTE: 10071, + WSASYSNOTREADY: 10091, + WSAVERNOTSUPPORTED: 10092, + WSANOTINITIALISED: 10093, + WSAEDISCON: 10101, + WSAENOMORE: 10102, + WSAECANCELLED: 10103, + WSAEINVALIDPROCTABLE: 10104, + WSAEINVALIDPROVIDER: 10105, + WSAEPROVIDERFAILEDINIT: 10106, + WSASYSCALLFAILURE: 10107, + WSASERVICE_NOT_FOUND: 10108, + WSATYPE_NOT_FOUND: 10109, + WSA_E_NO_MORE: 10110, + WSA_E_CANCELLED: 10111, + WSAEREFUSED: 10112, + }, + signals: { + SIGHUP: 1, + SIGINT: 2, + SIGILL: 4, + SIGABRT: 22, + SIGFPE: 8, + SIGKILL: 9, + SIGSEGV: 11, + SIGTERM: 15, + SIGBREAK: 21, + SIGWINCH: 28, + }, + priority: { + PRIORITY_LOW: 19, + PRIORITY_BELOW_NORMAL: 10, + PRIORITY_NORMAL: 0, + PRIORITY_ABOVE_NORMAL: -7, + PRIORITY_HIGH: -14, + PRIORITY_HIGHEST: -20, + }, + }; +} + +export { os }; + +export const fs = { + UV_FS_SYMLINK_DIR: 1, + UV_FS_SYMLINK_JUNCTION: 2, + O_RDONLY: 0, + O_WRONLY: 1, + O_RDWR: 2, + UV_DIRENT_UNKNOWN: 0, + UV_DIRENT_FILE: 1, + UV_DIRENT_DIR: 2, + UV_DIRENT_LINK: 3, + UV_DIRENT_FIFO: 4, + UV_DIRENT_SOCKET: 5, + UV_DIRENT_CHAR: 6, + UV_DIRENT_BLOCK: 7, + S_IFMT: 61440, + S_IFREG: 32768, + S_IFDIR: 16384, + S_IFCHR: 8192, + S_IFBLK: 24576, + S_IFIFO: 4096, + S_IFLNK: 40960, + S_IFSOCK: 49152, + O_CREAT: 512, + O_EXCL: 2048, + UV_FS_O_FILEMAP: 0, + O_NOCTTY: 131072, + O_TRUNC: 1024, + O_APPEND: 8, + O_DIRECTORY: 1048576, + O_NOFOLLOW: 256, + O_SYNC: 128, + O_DSYNC: 4194304, + O_SYMLINK: 2097152, + O_NONBLOCK: 4, + S_IRWXU: 448, + S_IRUSR: 256, + S_IWUSR: 128, + S_IXUSR: 64, + S_IRWXG: 56, + S_IRGRP: 32, + S_IWGRP: 16, + S_IXGRP: 8, + S_IRWXO: 7, + S_IROTH: 4, + S_IWOTH: 2, + S_IXOTH: 1, + F_OK: 0, + R_OK: 4, + W_OK: 2, + X_OK: 1, + UV_FS_COPYFILE_EXCL: 1, + COPYFILE_EXCL: 1, + UV_FS_COPYFILE_FICLONE: 2, + COPYFILE_FICLONE: 2, + UV_FS_COPYFILE_FICLONE_FORCE: 4, + COPYFILE_FICLONE_FORCE: 4, +} as const; +export const crypto = { + OPENSSL_VERSION_NUMBER: 269488319, + SSL_OP_ALL: 2147485780, + SSL_OP_ALLOW_NO_DHE_KEX: 1024, + SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION: 262144, + SSL_OP_CIPHER_SERVER_PREFERENCE: 4194304, + SSL_OP_CISCO_ANYCONNECT: 32768, + SSL_OP_COOKIE_EXCHANGE: 8192, + SSL_OP_CRYPTOPRO_TLSEXT_BUG: 2147483648, + SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS: 2048, + SSL_OP_EPHEMERAL_RSA: 0, + SSL_OP_LEGACY_SERVER_CONNECT: 4, + SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER: 0, + SSL_OP_MICROSOFT_SESS_ID_BUG: 0, + SSL_OP_MSIE_SSLV2_RSA_PADDING: 0, + SSL_OP_NETSCAPE_CA_DN_BUG: 0, + SSL_OP_NETSCAPE_CHALLENGE_BUG: 0, + SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG: 0, + SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG: 0, + SSL_OP_NO_COMPRESSION: 131072, + SSL_OP_NO_ENCRYPT_THEN_MAC: 524288, + SSL_OP_NO_QUERY_MTU: 4096, + SSL_OP_NO_RENEGOTIATION: 1073741824, + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION: 65536, + SSL_OP_NO_SSLv2: 0, + SSL_OP_NO_SSLv3: 33554432, + SSL_OP_NO_TICKET: 16384, + SSL_OP_NO_TLSv1: 67108864, + SSL_OP_NO_TLSv1_1: 268435456, + SSL_OP_NO_TLSv1_2: 134217728, + SSL_OP_NO_TLSv1_3: 536870912, + SSL_OP_PKCS1_CHECK_1: 0, + SSL_OP_PKCS1_CHECK_2: 0, + SSL_OP_PRIORITIZE_CHACHA: 2097152, + SSL_OP_SINGLE_DH_USE: 0, + SSL_OP_SINGLE_ECDH_USE: 0, + SSL_OP_SSLEAY_080_CLIENT_DH_BUG: 0, + SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG: 0, + SSL_OP_TLS_BLOCK_PADDING_BUG: 0, + SSL_OP_TLS_D5_BUG: 0, + SSL_OP_TLS_ROLLBACK_BUG: 8388608, + ENGINE_METHOD_RSA: 1, + ENGINE_METHOD_DSA: 2, + ENGINE_METHOD_DH: 4, + ENGINE_METHOD_RAND: 8, + ENGINE_METHOD_EC: 2048, + ENGINE_METHOD_CIPHERS: 64, + ENGINE_METHOD_DIGESTS: 128, + ENGINE_METHOD_PKEY_METHS: 512, + ENGINE_METHOD_PKEY_ASN1_METHS: 1024, + ENGINE_METHOD_ALL: 65535, + ENGINE_METHOD_NONE: 0, + DH_CHECK_P_NOT_SAFE_PRIME: 2, + DH_CHECK_P_NOT_PRIME: 1, + DH_UNABLE_TO_CHECK_GENERATOR: 4, + DH_NOT_SUITABLE_GENERATOR: 8, + ALPN_ENABLED: 1, + RSA_PKCS1_PADDING: 1, + RSA_SSLV23_PADDING: 2, + RSA_NO_PADDING: 3, + RSA_PKCS1_OAEP_PADDING: 4, + RSA_X931_PADDING: 5, + RSA_PKCS1_PSS_PADDING: 6, + RSA_PSS_SALTLEN_DIGEST: -1, + RSA_PSS_SALTLEN_MAX_SIGN: -2, + RSA_PSS_SALTLEN_AUTO: -2, + defaultCoreCipherList: + "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA256:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA256:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!SRP:!CAMELLIA", + TLS1_VERSION: 769, + TLS1_1_VERSION: 770, + TLS1_2_VERSION: 771, + TLS1_3_VERSION: 772, + POINT_CONVERSION_COMPRESSED: 2, + POINT_CONVERSION_UNCOMPRESSED: 4, + POINT_CONVERSION_HYBRID: 6, +} as const; +export const zlib = { + Z_NO_FLUSH: 0, + Z_PARTIAL_FLUSH: 1, + Z_SYNC_FLUSH: 2, + Z_FULL_FLUSH: 3, + Z_FINISH: 4, + Z_BLOCK: 5, + Z_OK: 0, + Z_STREAM_END: 1, + Z_NEED_DICT: 2, + Z_ERRNO: -1, + Z_STREAM_ERROR: -2, + Z_DATA_ERROR: -3, + Z_MEM_ERROR: -4, + Z_BUF_ERROR: -5, + Z_VERSION_ERROR: -6, + Z_NO_COMPRESSION: 0, + Z_BEST_SPEED: 1, + Z_BEST_COMPRESSION: 9, + Z_DEFAULT_COMPRESSION: -1, + Z_FILTERED: 1, + Z_HUFFMAN_ONLY: 2, + Z_RLE: 3, + Z_FIXED: 4, + Z_DEFAULT_STRATEGY: 0, + ZLIB_VERNUM: 4784, + DEFLATE: 1, + INFLATE: 2, + GZIP: 3, + GUNZIP: 4, + DEFLATERAW: 5, + INFLATERAW: 6, + UNZIP: 7, + BROTLI_DECODE: 8, + BROTLI_ENCODE: 9, + Z_MIN_WINDOWBITS: 8, + Z_MAX_WINDOWBITS: 15, + Z_DEFAULT_WINDOWBITS: 15, + Z_MIN_CHUNK: 64, + Z_MAX_CHUNK: Infinity, + Z_DEFAULT_CHUNK: 16384, + Z_MIN_MEMLEVEL: 1, + Z_MAX_MEMLEVEL: 9, + Z_DEFAULT_MEMLEVEL: 8, + Z_MIN_LEVEL: -1, + Z_MAX_LEVEL: 9, + Z_DEFAULT_LEVEL: -1, + BROTLI_OPERATION_PROCESS: 0, + BROTLI_OPERATION_FLUSH: 1, + BROTLI_OPERATION_FINISH: 2, + BROTLI_OPERATION_EMIT_METADATA: 3, + BROTLI_PARAM_MODE: 0, + BROTLI_MODE_GENERIC: 0, + BROTLI_MODE_TEXT: 1, + BROTLI_MODE_FONT: 2, + BROTLI_DEFAULT_MODE: 0, + BROTLI_PARAM_QUALITY: 1, + BROTLI_MIN_QUALITY: 0, + BROTLI_MAX_QUALITY: 11, + BROTLI_DEFAULT_QUALITY: 11, + BROTLI_PARAM_LGWIN: 2, + BROTLI_MIN_WINDOW_BITS: 10, + BROTLI_MAX_WINDOW_BITS: 24, + BROTLI_LARGE_MAX_WINDOW_BITS: 30, + BROTLI_DEFAULT_WINDOW: 22, + BROTLI_PARAM_LGBLOCK: 3, + BROTLI_MIN_INPUT_BLOCK_BITS: 16, + BROTLI_MAX_INPUT_BLOCK_BITS: 24, + BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING: 4, + BROTLI_PARAM_SIZE_HINT: 5, + BROTLI_PARAM_LARGE_WINDOW: 6, + BROTLI_PARAM_NPOSTFIX: 7, + BROTLI_PARAM_NDIRECT: 8, + BROTLI_DECODER_RESULT_ERROR: 0, + BROTLI_DECODER_RESULT_SUCCESS: 1, + BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: 2, + BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: 3, + BROTLI_DECODER_PARAM_DISABLE_RING_BUFFER_REALLOCATION: 0, + BROTLI_DECODER_PARAM_LARGE_WINDOW: 1, + BROTLI_DECODER_NO_ERROR: 0, + BROTLI_DECODER_SUCCESS: 1, + BROTLI_DECODER_NEEDS_MORE_INPUT: 2, + BROTLI_DECODER_NEEDS_MORE_OUTPUT: 3, + BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_NIBBLE: -1, + BROTLI_DECODER_ERROR_FORMAT_RESERVED: -2, + BROTLI_DECODER_ERROR_FORMAT_EXUBERANT_META_NIBBLE: -3, + BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_ALPHABET: -4, + BROTLI_DECODER_ERROR_FORMAT_SIMPLE_HUFFMAN_SAME: -5, + BROTLI_DECODER_ERROR_FORMAT_CL_SPACE: -6, + BROTLI_DECODER_ERROR_FORMAT_HUFFMAN_SPACE: -7, + BROTLI_DECODER_ERROR_FORMAT_CONTEXT_MAP_REPEAT: -8, + BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_1: -9, + BROTLI_DECODER_ERROR_FORMAT_BLOCK_LENGTH_2: -10, + BROTLI_DECODER_ERROR_FORMAT_TRANSFORM: -11, + BROTLI_DECODER_ERROR_FORMAT_DICTIONARY: -12, + BROTLI_DECODER_ERROR_FORMAT_WINDOW_BITS: -13, + BROTLI_DECODER_ERROR_FORMAT_PADDING_1: -14, + BROTLI_DECODER_ERROR_FORMAT_PADDING_2: -15, + BROTLI_DECODER_ERROR_FORMAT_DISTANCE: -16, + BROTLI_DECODER_ERROR_DICTIONARY_NOT_SET: -19, + BROTLI_DECODER_ERROR_INVALID_ARGUMENTS: -20, + BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MODES: -21, + BROTLI_DECODER_ERROR_ALLOC_TREE_GROUPS: -22, + BROTLI_DECODER_ERROR_ALLOC_CONTEXT_MAP: -25, + BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_1: -26, + BROTLI_DECODER_ERROR_ALLOC_RING_BUFFER_2: -27, + BROTLI_DECODER_ERROR_ALLOC_BLOCK_TYPE_TREES: -30, + BROTLI_DECODER_ERROR_UNREACHABLE: -31, +} as const; +export const trace = { + TRACE_EVENT_PHASE_BEGIN: 66, + TRACE_EVENT_PHASE_END: 69, + TRACE_EVENT_PHASE_COMPLETE: 88, + TRACE_EVENT_PHASE_INSTANT: 73, + TRACE_EVENT_PHASE_ASYNC_BEGIN: 83, + TRACE_EVENT_PHASE_ASYNC_STEP_INTO: 84, + TRACE_EVENT_PHASE_ASYNC_STEP_PAST: 112, + TRACE_EVENT_PHASE_ASYNC_END: 70, + TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN: 98, + TRACE_EVENT_PHASE_NESTABLE_ASYNC_END: 101, + TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT: 110, + TRACE_EVENT_PHASE_FLOW_BEGIN: 115, + TRACE_EVENT_PHASE_FLOW_STEP: 116, + TRACE_EVENT_PHASE_FLOW_END: 102, + TRACE_EVENT_PHASE_METADATA: 77, + TRACE_EVENT_PHASE_COUNTER: 67, + TRACE_EVENT_PHASE_SAMPLE: 80, + TRACE_EVENT_PHASE_CREATE_OBJECT: 78, + TRACE_EVENT_PHASE_SNAPSHOT_OBJECT: 79, + TRACE_EVENT_PHASE_DELETE_OBJECT: 68, + TRACE_EVENT_PHASE_MEMORY_DUMP: 118, + TRACE_EVENT_PHASE_MARK: 82, + TRACE_EVENT_PHASE_CLOCK_SYNC: 99, + TRACE_EVENT_PHASE_ENTER_CONTEXT: 40, + TRACE_EVENT_PHASE_LEAVE_CONTEXT: 41, + TRACE_EVENT_PHASE_LINK_IDS: 61, +} as const; diff --git a/ext/node/polyfills/internal_binding/contextify.ts b/ext/node/polyfills/internal_binding/contextify.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/contextify.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/credentials.ts b/ext/node/polyfills/internal_binding/credentials.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/credentials.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/crypto.ts b/ext/node/polyfills/internal_binding/crypto.ts new file mode 100644 index 0000000000..ce4739b3da --- /dev/null +++ b/ext/node/polyfills/internal_binding/crypto.ts @@ -0,0 +1,14 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export { timingSafeEqual } from "internal:deno_node/polyfills/internal_binding/_timingSafeEqual.ts"; + +export function getFipsCrypto(): boolean { + notImplemented("crypto.getFipsCrypto"); +} + +export function setFipsCrypto(_fips: boolean) { + notImplemented("crypto.setFipsCrypto"); +} diff --git a/ext/node/polyfills/internal_binding/errors.ts b/ext/node/polyfills/internal_binding/errors.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/errors.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/fs.ts b/ext/node/polyfills/internal_binding/fs.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/fs.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/fs_dir.ts b/ext/node/polyfills/internal_binding/fs_dir.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/fs_dir.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/fs_event_wrap.ts b/ext/node/polyfills/internal_binding/fs_event_wrap.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/fs_event_wrap.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/handle_wrap.ts b/ext/node/polyfills/internal_binding/handle_wrap.ts new file mode 100644 index 0000000000..98c6a9f168 --- /dev/null +++ b/ext/node/polyfills/internal_binding/handle_wrap.ts @@ -0,0 +1,53 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/handle_wrap.cc +// - https://github.com/nodejs/node/blob/master/src/handle_wrap.h + +import { unreachable } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { + AsyncWrap, + providerType, +} from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; + +export class HandleWrap extends AsyncWrap { + constructor(provider: providerType) { + super(provider); + } + + close(cb: () => void = () => {}) { + this._onClose(); + queueMicrotask(cb); + } + + ref() { + unreachable(); + } + + unref() { + unreachable(); + } + + // deno-lint-ignore no-explicit-any + _onClose(): any {} +} diff --git a/ext/node/polyfills/internal_binding/heap_utils.ts b/ext/node/polyfills/internal_binding/heap_utils.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/heap_utils.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/http_parser.ts b/ext/node/polyfills/internal_binding/http_parser.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/http_parser.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/icu.ts b/ext/node/polyfills/internal_binding/icu.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/icu.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/inspector.ts b/ext/node/polyfills/internal_binding/inspector.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/inspector.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/js_stream.ts b/ext/node/polyfills/internal_binding/js_stream.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/js_stream.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/messaging.ts b/ext/node/polyfills/internal_binding/messaging.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/messaging.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/mod.ts b/ext/node/polyfills/internal_binding/mod.ts new file mode 100644 index 0000000000..6273b263b9 --- /dev/null +++ b/ext/node/polyfills/internal_binding/mod.ts @@ -0,0 +1,108 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import * as asyncWrap from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; +import * as buffer from "internal:deno_node/polyfills/internal_binding/buffer.ts"; +import * as config from "internal:deno_node/polyfills/internal_binding/config.ts"; +import * as caresWrap from "internal:deno_node/polyfills/internal_binding/cares_wrap.ts"; +import * as constants from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import * as contextify from "internal:deno_node/polyfills/internal_binding/contextify.ts"; +import * as crypto from "internal:deno_node/polyfills/internal_binding/crypto.ts"; +import * as credentials from "internal:deno_node/polyfills/internal_binding/credentials.ts"; +import * as errors from "internal:deno_node/polyfills/internal_binding/errors.ts"; +import * as fs from "internal:deno_node/polyfills/internal_binding/fs.ts"; +import * as fsDir from "internal:deno_node/polyfills/internal_binding/fs_dir.ts"; +import * as fsEventWrap from "internal:deno_node/polyfills/internal_binding/fs_event_wrap.ts"; +import * as heapUtils from "internal:deno_node/polyfills/internal_binding/heap_utils.ts"; +import * as httpParser from "internal:deno_node/polyfills/internal_binding/http_parser.ts"; +import * as icu from "internal:deno_node/polyfills/internal_binding/icu.ts"; +import * as inspector from "internal:deno_node/polyfills/internal_binding/inspector.ts"; +import * as jsStream from "internal:deno_node/polyfills/internal_binding/js_stream.ts"; +import * as messaging from "internal:deno_node/polyfills/internal_binding/messaging.ts"; +import * as moduleWrap from "internal:deno_node/polyfills/internal_binding/module_wrap.ts"; +import * as nativeModule from "internal:deno_node/polyfills/internal_binding/native_module.ts"; +import * as natives from "internal:deno_node/polyfills/internal_binding/natives.ts"; +import * as options from "internal:deno_node/polyfills/internal_binding/options.ts"; +import * as os from "internal:deno_node/polyfills/internal_binding/os.ts"; +import * as pipeWrap from "internal:deno_node/polyfills/internal_binding/pipe_wrap.ts"; +import * as performance from "internal:deno_node/polyfills/internal_binding/performance.ts"; +import * as processMethods from "internal:deno_node/polyfills/internal_binding/process_methods.ts"; +import * as report from "internal:deno_node/polyfills/internal_binding/report.ts"; +import * as serdes from "internal:deno_node/polyfills/internal_binding/serdes.ts"; +import * as signalWrap from "internal:deno_node/polyfills/internal_binding/signal_wrap.ts"; +import * as spawnSync from "internal:deno_node/polyfills/internal_binding/spawn_sync.ts"; +import * as streamWrap from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import * as stringDecoder from "internal:deno_node/polyfills/internal_binding/string_decoder.ts"; +import * as symbols from "internal:deno_node/polyfills/internal_binding/symbols.ts"; +import * as taskQueue from "internal:deno_node/polyfills/internal_binding/task_queue.ts"; +import * as tcpWrap from "internal:deno_node/polyfills/internal_binding/tcp_wrap.ts"; +import * as timers from "internal:deno_node/polyfills/internal_binding/timers.ts"; +import * as tlsWrap from "internal:deno_node/polyfills/internal_binding/tls_wrap.ts"; +import * as traceEvents from "internal:deno_node/polyfills/internal_binding/trace_events.ts"; +import * as ttyWrap from "internal:deno_node/polyfills/internal_binding/tty_wrap.ts"; +import * as types from "internal:deno_node/polyfills/internal_binding/types.ts"; +import * as udpWrap from "internal:deno_node/polyfills/internal_binding/udp_wrap.ts"; +import * as url from "internal:deno_node/polyfills/internal_binding/url.ts"; +import * as util from "internal:deno_node/polyfills/internal_binding/util.ts"; +import * as uv from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import * as v8 from "internal:deno_node/polyfills/internal_binding/v8.ts"; +import * as worker from "internal:deno_node/polyfills/internal_binding/worker.ts"; +import * as zlib from "internal:deno_node/polyfills/internal_binding/zlib.ts"; + +const modules = { + "async_wrap": asyncWrap, + buffer, + "cares_wrap": caresWrap, + config, + constants, + contextify, + credentials, + crypto, + errors, + fs, + "fs_dir": fsDir, + "fs_event_wrap": fsEventWrap, + "heap_utils": heapUtils, + "http_parser": httpParser, + icu, + inspector, + "js_stream": jsStream, + messaging, + "module_wrap": moduleWrap, + "native_module": nativeModule, + natives, + options, + os, + performance, + "pipe_wrap": pipeWrap, + "process_methods": processMethods, + report, + serdes, + "signal_wrap": signalWrap, + "spawn_sync": spawnSync, + "stream_wrap": streamWrap, + "string_decoder": stringDecoder, + symbols, + "task_queue": taskQueue, + "tcp_wrap": tcpWrap, + timers, + "tls_wrap": tlsWrap, + "trace_events": traceEvents, + "tty_wrap": ttyWrap, + types, + "udp_wrap": udpWrap, + url, + util, + uv, + v8, + worker, + zlib, +}; + +export type BindingName = keyof typeof modules; + +export function getBinding(name: BindingName) { + const mod = modules[name]; + if (!mod) { + throw new Error(`No such module: ${name}`); + } + return mod; +} diff --git a/ext/node/polyfills/internal_binding/module_wrap.ts b/ext/node/polyfills/internal_binding/module_wrap.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/module_wrap.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/native_module.ts b/ext/node/polyfills/internal_binding/native_module.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/native_module.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/natives.ts b/ext/node/polyfills/internal_binding/natives.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/natives.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/node_file.ts b/ext/node/polyfills/internal_binding/node_file.ts new file mode 100644 index 0000000000..742217b199 --- /dev/null +++ b/ext/node/polyfills/internal_binding/node_file.ts @@ -0,0 +1,84 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/node_file-inl.h +// - https://github.com/nodejs/node/blob/master/src/node_file.cc +// - https://github.com/nodejs/node/blob/master/src/node_file.h + +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; + +/** + * Write to the given file from the given buffer synchronously. + * + * Implements sync part of WriteBuffer in src/node_file.cc + * See: https://github.com/nodejs/node/blob/e9ed113/src/node_file.cc#L1818 + * + * @param fs file descriptor + * @param buffer the data to write + * @param offset where in the buffer to start from + * @param length how much to write + * @param position if integer, position to write at in the file. if null, write from the current position + * @param context context object for passing error number + */ +export function writeBuffer( + fd: number, + buffer: Uint8Array, + offset: number, + length: number, + position: number | null, + ctx: { errno?: number }, +) { + assert(offset >= 0, "offset should be greater or equal to 0"); + assert( + offset + length <= buffer.byteLength, + `buffer doesn't have enough data: byteLength = ${buffer.byteLength}, offset + length = ${ + offset + + length + }`, + ); + + if (position) { + Deno.seekSync(fd, position, Deno.SeekMode.Current); + } + + const subarray = buffer.subarray(offset, offset + length); + + try { + return Deno.writeSync(fd, subarray); + } catch (e) { + ctx.errno = extractOsErrorNumberFromErrorMessage(e); + return 0; + } +} + +function extractOsErrorNumberFromErrorMessage(e: unknown): number { + const match = e instanceof Error + ? e.message.match(/\(os error (\d+)\)/) + : false; + + if (match) { + return +match[1]; + } + + return 255; // Unknown error +} diff --git a/ext/node/polyfills/internal_binding/node_options.ts b/ext/node/polyfills/internal_binding/node_options.ts new file mode 100644 index 0000000000..6109cf6db3 --- /dev/null +++ b/ext/node/polyfills/internal_binding/node_options.ts @@ -0,0 +1,39 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/node_options-inl.h +// - https://github.com/nodejs/node/blob/master/src/node_options.cc +// - https://github.com/nodejs/node/blob/master/src/node_options.h + +export function getOptions() { + // TODO(kt3k): Return option arguments as parsed object + return { options: new Map() }; + + // const { Deno } = globalThis as any; + // const args = parse(Deno?.args ?? []); + // const options = new Map( + // Object.entries(args).map(([key, value]) => [key, { value }]), + // ); + // + // return { options }; +} diff --git a/ext/node/polyfills/internal_binding/options.ts b/ext/node/polyfills/internal_binding/options.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/options.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/os.ts b/ext/node/polyfills/internal_binding/os.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/os.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/performance.ts b/ext/node/polyfills/internal_binding/performance.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/performance.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/pipe_wrap.ts b/ext/node/polyfills/internal_binding/pipe_wrap.ts new file mode 100644 index 0000000000..1e0d551a48 --- /dev/null +++ b/ext/node/polyfills/internal_binding/pipe_wrap.ts @@ -0,0 +1,397 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/pipe_wrap.cc +// - https://github.com/nodejs/node/blob/master/src/pipe_wrap.h + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { unreachable } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { ConnectionWrap } from "internal:deno_node/polyfills/internal_binding/connection_wrap.ts"; +import { + AsyncWrap, + providerType, +} from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; +import { LibuvStreamWrap } from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { delay } from "internal:deno_node/polyfills/_util/async.ts"; +import { kStreamBaseField } from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { + ceilPowOf2, + INITIAL_ACCEPT_BACKOFF_DELAY, + MAX_ACCEPT_BACKOFF_DELAY, +} from "internal:deno_node/polyfills/internal_binding/_listen.ts"; +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; +import { fs } from "internal:deno_node/polyfills/internal_binding/constants.ts"; + +export enum socketType { + SOCKET, + SERVER, + IPC, +} + +export class Pipe extends ConnectionWrap { + override reading = false; + ipc: boolean; + + // REF: https://github.com/nodejs/node/blob/master/deps/uv/src/win/pipe.c#L48 + #pendingInstances = 4; + + #address?: string; + + #backlog?: number; + #listener!: Deno.Listener; + #connections = 0; + + #closed = false; + #acceptBackoffDelay?: number; + + constructor(type: number, conn?: Deno.UnixConn) { + let provider: providerType; + let ipc: boolean; + + switch (type) { + case socketType.SOCKET: { + provider = providerType.PIPEWRAP; + ipc = false; + + break; + } + case socketType.SERVER: { + provider = providerType.PIPESERVERWRAP; + ipc = false; + + break; + } + case socketType.IPC: { + provider = providerType.PIPEWRAP; + ipc = true; + + break; + } + default: { + unreachable(); + } + } + + super(provider, conn); + + this.ipc = ipc; + + if (conn && provider === providerType.PIPEWRAP) { + const localAddr = conn.localAddr as Deno.UnixAddr; + this.#address = localAddr.path; + } + } + + open(_fd: number): number { + // REF: https://github.com/denoland/deno/issues/6529 + notImplemented("Pipe.prototype.open"); + } + + /** + * Bind to a Unix domain or Windows named pipe. + * @param name Unix domain or Windows named pipe the server should listen to. + * @return An error status code. + */ + bind(name: string) { + // Deno doesn't currently separate bind from connect. For now we noop under + // the assumption we will connect shortly. + // REF: https://doc.deno.land/deno/unstable/~/Deno.connect + + this.#address = name; + + return 0; + } + + /** + * Connect to a Unix domain or Windows named pipe. + * @param req A PipeConnectWrap instance. + * @param address Unix domain or Windows named pipe the server should connect to. + * @return An error status code. + */ + connect(req: PipeConnectWrap, address: string) { + if (isWindows) { + // REF: https://github.com/denoland/deno/issues/10244 + notImplemented("Pipe.prototype.connect - Windows"); + } + + const connectOptions: Deno.UnixConnectOptions = { + path: address, + transport: "unix", + }; + + Deno.connect(connectOptions).then( + (conn: Deno.UnixConn) => { + const localAddr = conn.localAddr as Deno.UnixAddr; + + this.#address = req.address = localAddr.path; + this[kStreamBaseField] = conn; + + try { + this.afterConnect(req, 0); + } catch { + // swallow callback errors. + } + }, + (e) => { + // TODO(cmorten): correct mapping of connection error to status code. + let code: number; + + if (e instanceof Deno.errors.NotFound) { + code = codeMap.get("ENOENT")!; + } else if (e instanceof Deno.errors.PermissionDenied) { + code = codeMap.get("EACCES")!; + } else { + code = codeMap.get("ECONNREFUSED")!; + } + + try { + this.afterConnect(req, code); + } catch { + // swallow callback errors. + } + }, + ); + + return 0; + } + + /** + * Listen for new connections. + * @param backlog The maximum length of the queue of pending connections. + * @return An error status code. + */ + listen(backlog: number): number { + if (isWindows) { + // REF: https://github.com/denoland/deno/issues/10244 + notImplemented("Pipe.prototype.listen - Windows"); + } + + this.#backlog = isWindows + ? this.#pendingInstances + : ceilPowOf2(backlog + 1); + + const listenOptions = { + path: this.#address!, + transport: "unix" as const, + }; + + let listener; + + try { + listener = Deno.listen(listenOptions); + } catch (e) { + if (e instanceof Deno.errors.AddrInUse) { + return codeMap.get("EADDRINUSE")!; + } else if (e instanceof Deno.errors.AddrNotAvailable) { + return codeMap.get("EADDRNOTAVAIL")!; + } else if (e instanceof Deno.errors.PermissionDenied) { + throw e; + } + + // TODO(cmorten): map errors to appropriate error codes. + return codeMap.get("UNKNOWN")!; + } + + const address = listener.addr as Deno.UnixAddr; + this.#address = address.path; + + this.#listener = listener; + this.#accept(); + + return 0; + } + + override ref() { + if (this.#listener) { + this.#listener.ref(); + } + } + + override unref() { + if (this.#listener) { + this.#listener.unref(); + } + } + + /** + * Set the number of pending pipe instance handles when the pipe server is + * waiting for connections. This setting applies to Windows only. + * @param instances Number of pending pipe instances. + */ + setPendingInstances(instances: number) { + this.#pendingInstances = instances; + } + + /** + * Alters pipe permissions, allowing it to be accessed from processes run by + * different users. Makes the pipe writable or readable by all users. Mode + * can be `UV_WRITABLE`, `UV_READABLE` or `UV_WRITABLE | UV_READABLE`. This + * function is blocking. + * @param mode Pipe permissions mode. + * @return An error status code. + */ + fchmod(mode: number) { + if ( + mode != constants.UV_READABLE && + mode != constants.UV_WRITABLE && + mode != (constants.UV_WRITABLE | constants.UV_READABLE) + ) { + return codeMap.get("EINVAL"); + } + + let desiredMode = 0; + + if (mode & constants.UV_READABLE) { + desiredMode |= fs.S_IRUSR | fs.S_IRGRP | fs.S_IROTH; + } + if (mode & constants.UV_WRITABLE) { + desiredMode |= fs.S_IWUSR | fs.S_IWGRP | fs.S_IWOTH; + } + + // TODO(cmorten): this will incorrectly throw on Windows + // REF: https://github.com/denoland/deno/issues/4357 + try { + Deno.chmodSync(this.#address!, desiredMode); + } catch { + // TODO(cmorten): map errors to appropriate error codes. + return codeMap.get("UNKNOWN")!; + } + + return 0; + } + + /** Handle backoff delays following an unsuccessful accept. */ + async #acceptBackoff() { + // Backoff after transient errors to allow time for the system to + // recover, and avoid blocking up the event loop with a continuously + // running loop. + if (!this.#acceptBackoffDelay) { + this.#acceptBackoffDelay = INITIAL_ACCEPT_BACKOFF_DELAY; + } else { + this.#acceptBackoffDelay *= 2; + } + + if (this.#acceptBackoffDelay >= MAX_ACCEPT_BACKOFF_DELAY) { + this.#acceptBackoffDelay = MAX_ACCEPT_BACKOFF_DELAY; + } + + await delay(this.#acceptBackoffDelay); + + this.#accept(); + } + + /** Accept new connections. */ + async #accept(): Promise { + if (this.#closed) { + return; + } + + if (this.#connections > this.#backlog!) { + this.#acceptBackoff(); + + return; + } + + let connection: Deno.Conn; + + try { + connection = await this.#listener.accept(); + } catch (e) { + if (e instanceof Deno.errors.BadResource && this.#closed) { + // Listener and server has closed. + return; + } + + try { + // TODO(cmorten): map errors to appropriate error codes. + this.onconnection!(codeMap.get("UNKNOWN")!, undefined); + } catch { + // swallow callback errors. + } + + this.#acceptBackoff(); + + return; + } + + // Reset the backoff delay upon successful accept. + this.#acceptBackoffDelay = undefined; + + const connectionHandle = new Pipe(socketType.SOCKET, connection); + this.#connections++; + + try { + this.onconnection!(0, connectionHandle); + } catch { + // swallow callback errors. + } + + return this.#accept(); + } + + /** Handle server closure. */ + override _onClose(): number { + this.#closed = true; + this.reading = false; + + this.#address = undefined; + + this.#backlog = undefined; + this.#connections = 0; + this.#acceptBackoffDelay = undefined; + + if (this.provider === providerType.PIPESERVERWRAP) { + try { + this.#listener.close(); + } catch { + // listener already closed + } + } + + return LibuvStreamWrap.prototype._onClose.call(this); + } +} + +export class PipeConnectWrap extends AsyncWrap { + oncomplete!: ( + status: number, + handle: ConnectionWrap, + req: PipeConnectWrap, + readable: boolean, + writeable: boolean, + ) => void; + address!: string; + + constructor() { + super(providerType.PIPECONNECTWRAP); + } +} + +export enum constants { + SOCKET = socketType.SOCKET, + SERVER = socketType.SERVER, + IPC = socketType.IPC, + UV_READABLE = 1, + UV_WRITABLE = 2, +} diff --git a/ext/node/polyfills/internal_binding/process_methods.ts b/ext/node/polyfills/internal_binding/process_methods.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/process_methods.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/report.ts b/ext/node/polyfills/internal_binding/report.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/report.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/serdes.ts b/ext/node/polyfills/internal_binding/serdes.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/serdes.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/signal_wrap.ts b/ext/node/polyfills/internal_binding/signal_wrap.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/signal_wrap.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/spawn_sync.ts b/ext/node/polyfills/internal_binding/spawn_sync.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/spawn_sync.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/stream_wrap.ts b/ext/node/polyfills/internal_binding/stream_wrap.ts new file mode 100644 index 0000000000..3aee3b9daf --- /dev/null +++ b/ext/node/polyfills/internal_binding/stream_wrap.ts @@ -0,0 +1,374 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/stream_base-inl.h +// - https://github.com/nodejs/node/blob/master/src/stream_base.h +// - https://github.com/nodejs/node/blob/master/src/stream_base.cc +// - https://github.com/nodejs/node/blob/master/src/stream_wrap.h +// - https://github.com/nodejs/node/blob/master/src/stream_wrap.cc + +import { TextEncoder } from "internal:deno_web/08_text_encoding.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { HandleWrap } from "internal:deno_node/polyfills/internal_binding/handle_wrap.ts"; +import { + AsyncWrap, + providerType, +} from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; + +interface Reader { + read(p: Uint8Array): Promise; +} + +interface Writer { + write(p: Uint8Array): Promise; +} + +export interface Closer { + close(): void; +} + +type Ref = { ref(): void; unref(): void }; + +enum StreamBaseStateFields { + kReadBytesOrError, + kArrayBufferOffset, + kBytesWritten, + kLastWriteWasAsync, + kNumStreamBaseStateFields, +} + +export const kReadBytesOrError = StreamBaseStateFields.kReadBytesOrError; +export const kArrayBufferOffset = StreamBaseStateFields.kArrayBufferOffset; +export const kBytesWritten = StreamBaseStateFields.kBytesWritten; +export const kLastWriteWasAsync = StreamBaseStateFields.kLastWriteWasAsync; +export const kNumStreamBaseStateFields = + StreamBaseStateFields.kNumStreamBaseStateFields; + +export const streamBaseState = new Uint8Array(5); + +// This is Deno, it always will be async. +streamBaseState[kLastWriteWasAsync] = 1; + +export class WriteWrap extends AsyncWrap { + handle!: H; + oncomplete!: (status: number) => void; + async!: boolean; + bytes!: number; + buffer!: unknown; + callback!: unknown; + _chunks!: unknown[]; + + constructor() { + super(providerType.WRITEWRAP); + } +} + +export class ShutdownWrap extends AsyncWrap { + handle!: H; + oncomplete!: (status: number) => void; + callback!: () => void; + + constructor() { + super(providerType.SHUTDOWNWRAP); + } +} + +export const kStreamBaseField = Symbol("kStreamBaseField"); + +const SUGGESTED_SIZE = 64 * 1024; + +export class LibuvStreamWrap extends HandleWrap { + [kStreamBaseField]?: Reader & Writer & Closer & Ref; + + reading!: boolean; + #reading = false; + destroyed = false; + writeQueueSize = 0; + bytesRead = 0; + bytesWritten = 0; + + onread!: (_arrayBuffer: Uint8Array, _nread: number) => Uint8Array | undefined; + + constructor( + provider: providerType, + stream?: Reader & Writer & Closer & Ref, + ) { + super(provider); + this.#attachToObject(stream); + } + + /** + * Start the reading of the stream. + * @return An error status code. + */ + readStart(): number { + if (!this.#reading) { + this.#reading = true; + this.#read(); + } + + return 0; + } + + /** + * Stop the reading of the stream. + * @return An error status code. + */ + readStop(): number { + this.#reading = false; + + return 0; + } + + /** + * Shutdown the stream. + * @param req A shutdown request wrapper. + * @return An error status code. + */ + shutdown(req: ShutdownWrap): number { + const status = this._onClose(); + + try { + req.oncomplete(status); + } catch { + // swallow callback error. + } + + return 0; + } + + /** + * @param userBuf + * @return An error status code. + */ + useUserBuffer(_userBuf: unknown): number { + // TODO(cmorten) + notImplemented("LibuvStreamWrap.prototype.useUserBuffer"); + } + + /** + * Write a buffer to the stream. + * @param req A write request wrapper. + * @param data The Uint8Array buffer to write to the stream. + * @return An error status code. + */ + writeBuffer(req: WriteWrap, data: Uint8Array): number { + this.#write(req, data); + + return 0; + } + + /** + * Write multiple chunks at once. + * @param req A write request wrapper. + * @param chunks + * @param allBuffers + * @return An error status code. + */ + writev( + req: WriteWrap, + chunks: Buffer[] | (string | Buffer)[], + allBuffers: boolean, + ): number { + const count = allBuffers ? chunks.length : chunks.length >> 1; + const buffers: Buffer[] = new Array(count); + + if (!allBuffers) { + for (let i = 0; i < count; i++) { + const chunk = chunks[i * 2]; + + if (Buffer.isBuffer(chunk)) { + buffers[i] = chunk; + } + + // String chunk + const encoding: string = chunks[i * 2 + 1] as string; + buffers[i] = Buffer.from(chunk as string, encoding); + } + } else { + for (let i = 0; i < count; i++) { + buffers[i] = chunks[i] as Buffer; + } + } + + return this.writeBuffer(req, Buffer.concat(buffers)); + } + + /** + * Write an ASCII string to the stream. + * @return An error status code. + */ + writeAsciiString(req: WriteWrap, data: string): number { + const buffer = new TextEncoder().encode(data); + + return this.writeBuffer(req, buffer); + } + + /** + * Write an UTF8 string to the stream. + * @return An error status code. + */ + writeUtf8String(req: WriteWrap, data: string): number { + const buffer = new TextEncoder().encode(data); + + return this.writeBuffer(req, buffer); + } + + /** + * Write an UCS2 string to the stream. + * @return An error status code. + */ + writeUcs2String(_req: WriteWrap, _data: string): number { + notImplemented("LibuvStreamWrap.prototype.writeUcs2String"); + } + + /** + * Write an LATIN1 string to the stream. + * @return An error status code. + */ + writeLatin1String(req: WriteWrap, data: string): number { + const buffer = Buffer.from(data, "latin1"); + return this.writeBuffer(req, buffer); + } + + override _onClose(): number { + let status = 0; + this.#reading = false; + + try { + this[kStreamBaseField]?.close(); + } catch { + status = codeMap.get("ENOTCONN")!; + } + + return status; + } + + /** + * Attaches the class to the underlying stream. + * @param stream The stream to attach to. + */ + #attachToObject(stream?: Reader & Writer & Closer & Ref) { + this[kStreamBaseField] = stream; + } + + /** Internal method for reading from the attached stream. */ + async #read() { + let buf = new Uint8Array(SUGGESTED_SIZE); + + let nread: number | null; + try { + nread = await this[kStreamBaseField]!.read(buf); + } catch (e) { + if ( + e instanceof Deno.errors.Interrupted || + e instanceof Deno.errors.BadResource + ) { + nread = codeMap.get("EOF")!; + } else if ( + e instanceof Deno.errors.ConnectionReset || + e instanceof Deno.errors.ConnectionAborted + ) { + nread = codeMap.get("ECONNRESET")!; + } else { + nread = codeMap.get("UNKNOWN")!; + } + + buf = new Uint8Array(0); + } + + nread ??= codeMap.get("EOF")!; + + streamBaseState[kReadBytesOrError] = nread; + + if (nread > 0) { + this.bytesRead += nread; + } + + buf = buf.slice(0, nread); + + streamBaseState[kArrayBufferOffset] = 0; + + try { + this.onread!(buf, nread); + } catch { + // swallow callback errors. + } + + if (nread >= 0 && this.#reading) { + this.#read(); + } + } + + /** + * Internal method for writing to the attached stream. + * @param req A write request wrapper. + * @param data The Uint8Array buffer to write to the stream. + */ + async #write(req: WriteWrap, data: Uint8Array) { + const { byteLength } = data; + + try { + // TODO(crowlKats): duplicate from runtime/js/13_buffer.js + let nwritten = 0; + while (nwritten < data.length) { + nwritten += await this[kStreamBaseField]!.write( + data.subarray(nwritten), + ); + } + } catch (e) { + let status: number; + + // TODO(cmorten): map err to status codes + if ( + e instanceof Deno.errors.BadResource || + e instanceof Deno.errors.BrokenPipe + ) { + status = codeMap.get("EBADF")!; + } else { + status = codeMap.get("UNKNOWN")!; + } + + try { + req.oncomplete(status); + } catch { + // swallow callback errors. + } + + return; + } + + streamBaseState[kBytesWritten] = byteLength; + this.bytesWritten += byteLength; + + try { + req.oncomplete(0); + } catch { + // swallow callback errors. + } + + return; + } +} diff --git a/ext/node/polyfills/internal_binding/string_decoder.ts b/ext/node/polyfills/internal_binding/string_decoder.ts new file mode 100644 index 0000000000..3df230aee6 --- /dev/null +++ b/ext/node/polyfills/internal_binding/string_decoder.ts @@ -0,0 +1,15 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Encodings } from "internal:deno_node/polyfills/internal_binding/_node.ts"; + +const encodings = []; +encodings[Encodings.ASCII] = "ascii"; +encodings[Encodings.BASE64] = "base64"; +encodings[Encodings.BASE64URL] = "base64url"; +encodings[Encodings.BUFFER] = "buffer"; +encodings[Encodings.HEX] = "hex"; +encodings[Encodings.LATIN1] = "latin1"; +encodings[Encodings.UCS2] = "utf16le"; +encodings[Encodings.UTF8] = "utf8"; + +export default { encodings }; +export { encodings }; diff --git a/ext/node/polyfills/internal_binding/symbols.ts b/ext/node/polyfills/internal_binding/symbols.ts new file mode 100644 index 0000000000..affa86fbe6 --- /dev/null +++ b/ext/node/polyfills/internal_binding/symbols.ts @@ -0,0 +1,27 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/node_symbols.cc + +export const asyncIdSymbol: unique symbol = Symbol("asyncIdSymbol"); +export const ownerSymbol: unique symbol = Symbol("ownerSymbol"); diff --git a/ext/node/polyfills/internal_binding/task_queue.ts b/ext/node/polyfills/internal_binding/task_queue.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/task_queue.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/tcp_wrap.ts b/ext/node/polyfills/internal_binding/tcp_wrap.ts new file mode 100644 index 0000000000..d0da3e10c9 --- /dev/null +++ b/ext/node/polyfills/internal_binding/tcp_wrap.ts @@ -0,0 +1,493 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/tcp_wrap.cc +// - https://github.com/nodejs/node/blob/master/src/tcp_wrap.h + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { unreachable } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { ConnectionWrap } from "internal:deno_node/polyfills/internal_binding/connection_wrap.ts"; +import { + AsyncWrap, + providerType, +} from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; +import { LibuvStreamWrap } from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { ownerSymbol } from "internal:deno_node/polyfills/internal_binding/symbols.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { delay } from "internal:deno_node/polyfills/_util/async.ts"; +import { kStreamBaseField } from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { isIP } from "internal:deno_node/polyfills/internal/net.ts"; +import { + ceilPowOf2, + INITIAL_ACCEPT_BACKOFF_DELAY, + MAX_ACCEPT_BACKOFF_DELAY, +} from "internal:deno_node/polyfills/internal_binding/_listen.ts"; + +/** The type of TCP socket. */ +enum socketType { + SOCKET, + SERVER, +} + +interface AddressInfo { + address: string; + family?: number; + port: number; +} + +export class TCPConnectWrap extends AsyncWrap { + oncomplete!: ( + status: number, + handle: ConnectionWrap, + req: TCPConnectWrap, + readable: boolean, + writeable: boolean, + ) => void; + address!: string; + port!: number; + localAddress!: string; + localPort!: number; + + constructor() { + super(providerType.TCPCONNECTWRAP); + } +} + +export enum constants { + SOCKET = socketType.SOCKET, + SERVER = socketType.SERVER, + UV_TCP_IPV6ONLY, +} + +export class TCP extends ConnectionWrap { + [ownerSymbol]: unknown = null; + override reading = false; + + #address?: string; + #port?: number; + + #remoteAddress?: string; + #remoteFamily?: number; + #remotePort?: number; + + #backlog?: number; + #listener!: Deno.Listener; + #connections = 0; + + #closed = false; + #acceptBackoffDelay?: number; + + /** + * Creates a new TCP class instance. + * @param type The socket type. + * @param conn Optional connection object to wrap. + */ + constructor(type: number, conn?: Deno.Conn) { + let provider: providerType; + + switch (type) { + case socketType.SOCKET: { + provider = providerType.TCPWRAP; + + break; + } + case socketType.SERVER: { + provider = providerType.TCPSERVERWRAP; + + break; + } + default: { + unreachable(); + } + } + + super(provider, conn); + + // TODO(cmorten): the handling of new connections and construction feels + // a little off. Suspect duplicating in some fashion. + if (conn && provider === providerType.TCPWRAP) { + const localAddr = conn.localAddr as Deno.NetAddr; + this.#address = localAddr.hostname; + this.#port = localAddr.port; + + const remoteAddr = conn.remoteAddr as Deno.NetAddr; + this.#remoteAddress = remoteAddr.hostname; + this.#remotePort = remoteAddr.port; + this.#remoteFamily = isIP(remoteAddr.hostname); + } + } + + /** + * Opens a file descriptor. + * @param fd The file descriptor to open. + * @return An error status code. + */ + open(_fd: number): number { + // REF: https://github.com/denoland/deno/issues/6529 + notImplemented("TCP.prototype.open"); + } + + /** + * Bind to an IPv4 address. + * @param address The hostname to bind to. + * @param port The port to bind to + * @return An error status code. + */ + bind(address: string, port: number): number { + return this.#bind(address, port, 0); + } + + /** + * Bind to an IPv6 address. + * @param address The hostname to bind to. + * @param port The port to bind to + * @return An error status code. + */ + bind6(address: string, port: number, flags: number): number { + return this.#bind(address, port, flags); + } + + /** + * Connect to an IPv4 address. + * @param req A TCPConnectWrap instance. + * @param address The hostname to connect to. + * @param port The port to connect to. + * @return An error status code. + */ + connect(req: TCPConnectWrap, address: string, port: number): number { + return this.#connect(req, address, port); + } + + /** + * Connect to an IPv6 address. + * @param req A TCPConnectWrap instance. + * @param address The hostname to connect to. + * @param port The port to connect to. + * @return An error status code. + */ + connect6(req: TCPConnectWrap, address: string, port: number): number { + return this.#connect(req, address, port); + } + + /** + * Listen for new connections. + * @param backlog The maximum length of the queue of pending connections. + * @return An error status code. + */ + listen(backlog: number): number { + this.#backlog = ceilPowOf2(backlog + 1); + + const listenOptions = { + hostname: this.#address!, + port: this.#port!, + transport: "tcp" as const, + }; + + let listener; + + try { + listener = Deno.listen(listenOptions); + } catch (e) { + if (e instanceof Deno.errors.AddrInUse) { + return codeMap.get("EADDRINUSE")!; + } else if (e instanceof Deno.errors.AddrNotAvailable) { + return codeMap.get("EADDRNOTAVAIL")!; + } else if (e instanceof Deno.errors.PermissionDenied) { + throw e; + } + + // TODO(cmorten): map errors to appropriate error codes. + return codeMap.get("UNKNOWN")!; + } + + const address = listener.addr as Deno.NetAddr; + this.#address = address.hostname; + this.#port = address.port; + + this.#listener = listener; + this.#accept(); + + return 0; + } + + override ref() { + if (this.#listener) { + this.#listener.ref(); + } + + if (this[kStreamBaseField]) { + this[kStreamBaseField].ref(); + } + } + + override unref() { + if (this.#listener) { + this.#listener.unref(); + } + + if (this[kStreamBaseField]) { + this[kStreamBaseField].unref(); + } + } + + /** + * Populates the provided object with local address entries. + * @param sockname An object to add the local address entries to. + * @return An error status code. + */ + getsockname(sockname: Record | AddressInfo): number { + if ( + typeof this.#address === "undefined" || + typeof this.#port === "undefined" + ) { + return codeMap.get("EADDRNOTAVAIL")!; + } + + sockname.address = this.#address; + sockname.port = this.#port; + sockname.family = isIP(this.#address); + + return 0; + } + + /** + * Populates the provided object with remote address entries. + * @param peername An object to add the remote address entries to. + * @return An error status code. + */ + getpeername(peername: Record | AddressInfo): number { + if ( + typeof this.#remoteAddress === "undefined" || + typeof this.#remotePort === "undefined" + ) { + return codeMap.get("EADDRNOTAVAIL")!; + } + + peername.address = this.#remoteAddress; + peername.port = this.#remotePort; + peername.family = this.#remoteFamily; + + return 0; + } + + /** + * @param noDelay + * @return An error status code. + */ + setNoDelay(_noDelay: boolean): number { + // TODO(bnoordhuis) https://github.com/denoland/deno/pull/13103 + return 0; + } + + /** + * @param enable + * @param initialDelay + * @return An error status code. + */ + setKeepAlive(_enable: boolean, _initialDelay: number): number { + // TODO(bnoordhuis) https://github.com/denoland/deno/pull/13103 + return 0; + } + + /** + * Windows only. + * + * Deprecated by Node. + * REF: https://github.com/nodejs/node/blob/master/lib/net.js#L1731 + * + * @param enable + * @return An error status code. + * @deprecated + */ + setSimultaneousAccepts(_enable: boolean) { + // Low priority to implement owing to it being deprecated in Node. + notImplemented("TCP.prototype.setSimultaneousAccepts"); + } + + /** + * Bind to an IPv4 or IPv6 address. + * @param address The hostname to bind to. + * @param port The port to bind to + * @param _flags + * @return An error status code. + */ + #bind(address: string, port: number, _flags: number): number { + // Deno doesn't currently separate bind from connect etc. + // REF: + // - https://doc.deno.land/deno/stable/~/Deno.connect + // - https://doc.deno.land/deno/stable/~/Deno.listen + // + // This also means we won't be connecting from the specified local address + // and port as providing these is not an option in Deno. + // REF: + // - https://doc.deno.land/deno/stable/~/Deno.ConnectOptions + // - https://doc.deno.land/deno/stable/~/Deno.ListenOptions + + this.#address = address; + this.#port = port; + + return 0; + } + + /** + * Connect to an IPv4 or IPv6 address. + * @param req A TCPConnectWrap instance. + * @param address The hostname to connect to. + * @param port The port to connect to. + * @return An error status code. + */ + #connect(req: TCPConnectWrap, address: string, port: number): number { + this.#remoteAddress = address; + this.#remotePort = port; + this.#remoteFamily = isIP(address); + + const connectOptions: Deno.ConnectOptions = { + hostname: address, + port, + transport: "tcp", + }; + + Deno.connect(connectOptions).then( + (conn: Deno.Conn) => { + // Incorrect / backwards, but correcting the local address and port with + // what was actually used given we can't actually specify these in Deno. + const localAddr = conn.localAddr as Deno.NetAddr; + this.#address = req.localAddress = localAddr.hostname; + this.#port = req.localPort = localAddr.port; + this[kStreamBaseField] = conn; + + try { + this.afterConnect(req, 0); + } catch { + // swallow callback errors. + } + }, + () => { + try { + // TODO(cmorten): correct mapping of connection error to status code. + this.afterConnect(req, codeMap.get("ECONNREFUSED")!); + } catch { + // swallow callback errors. + } + }, + ); + + return 0; + } + + /** Handle backoff delays following an unsuccessful accept. */ + async #acceptBackoff() { + // Backoff after transient errors to allow time for the system to + // recover, and avoid blocking up the event loop with a continuously + // running loop. + if (!this.#acceptBackoffDelay) { + this.#acceptBackoffDelay = INITIAL_ACCEPT_BACKOFF_DELAY; + } else { + this.#acceptBackoffDelay *= 2; + } + + if (this.#acceptBackoffDelay >= MAX_ACCEPT_BACKOFF_DELAY) { + this.#acceptBackoffDelay = MAX_ACCEPT_BACKOFF_DELAY; + } + + await delay(this.#acceptBackoffDelay); + + this.#accept(); + } + + /** Accept new connections. */ + async #accept(): Promise { + if (this.#closed) { + return; + } + + if (this.#connections > this.#backlog!) { + this.#acceptBackoff(); + + return; + } + + let connection: Deno.Conn; + + try { + connection = await this.#listener.accept(); + } catch (e) { + if (e instanceof Deno.errors.BadResource && this.#closed) { + // Listener and server has closed. + return; + } + + try { + // TODO(cmorten): map errors to appropriate error codes. + this.onconnection!(codeMap.get("UNKNOWN")!, undefined); + } catch { + // swallow callback errors. + } + + this.#acceptBackoff(); + + return; + } + + // Reset the backoff delay upon successful accept. + this.#acceptBackoffDelay = undefined; + + const connectionHandle = new TCP(socketType.SOCKET, connection); + this.#connections++; + + try { + this.onconnection!(0, connectionHandle); + } catch { + // swallow callback errors. + } + + return this.#accept(); + } + + /** Handle server closure. */ + override _onClose(): number { + this.#closed = true; + this.reading = false; + + this.#address = undefined; + this.#port = undefined; + + this.#remoteAddress = undefined; + this.#remoteFamily = undefined; + this.#remotePort = undefined; + + this.#backlog = undefined; + this.#connections = 0; + this.#acceptBackoffDelay = undefined; + + if (this.provider === providerType.TCPSERVERWRAP) { + try { + this.#listener.close(); + } catch { + // listener already closed + } + } + + return LibuvStreamWrap.prototype._onClose.call(this); + } +} diff --git a/ext/node/polyfills/internal_binding/timers.ts b/ext/node/polyfills/internal_binding/timers.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/timers.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/tls_wrap.ts b/ext/node/polyfills/internal_binding/tls_wrap.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/tls_wrap.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/trace_events.ts b/ext/node/polyfills/internal_binding/trace_events.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/trace_events.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/tty_wrap.ts b/ext/node/polyfills/internal_binding/tty_wrap.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/tty_wrap.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/types.ts b/ext/node/polyfills/internal_binding/types.ts new file mode 100644 index 0000000000..348d5553fd --- /dev/null +++ b/ext/node/polyfills/internal_binding/types.ts @@ -0,0 +1,377 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// +// Adapted from Node.js. 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. + +import { core } from "internal:deno_node/polyfills/_core.ts"; + +// https://tc39.es/ecma262/#sec-object.prototype.tostring +const _toString = Object.prototype.toString; + +// https://tc39.es/ecma262/#sec-bigint.prototype.valueof +const _bigIntValueOf = BigInt.prototype.valueOf; + +// https://tc39.es/ecma262/#sec-boolean.prototype.valueof +const _booleanValueOf = Boolean.prototype.valueOf; + +// https://tc39.es/ecma262/#sec-date.prototype.valueof +const _dateValueOf = Date.prototype.valueOf; + +// https://tc39.es/ecma262/#sec-number.prototype.valueof +const _numberValueOf = Number.prototype.valueOf; + +// https://tc39.es/ecma262/#sec-string.prototype.valueof +const _stringValueOf = String.prototype.valueOf; + +// https://tc39.es/ecma262/#sec-symbol.prototype.valueof +const _symbolValueOf = Symbol.prototype.valueOf; + +// https://tc39.es/ecma262/#sec-weakmap.prototype.has +const _weakMapHas = WeakMap.prototype.has; + +// https://tc39.es/ecma262/#sec-weakset.prototype.has +const _weakSetHas = WeakSet.prototype.has; + +// https://tc39.es/ecma262/#sec-get-arraybuffer.prototype.bytelength +const _getArrayBufferByteLength = Object.getOwnPropertyDescriptor( + ArrayBuffer.prototype, + "byteLength", +)!.get!; + +// https://tc39.es/ecma262/#sec-get-sharedarraybuffer.prototype.bytelength +const _getSharedArrayBufferByteLength = globalThis.SharedArrayBuffer + ? Object.getOwnPropertyDescriptor( + SharedArrayBuffer.prototype, + "byteLength", + )!.get! + : undefined; + +// https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag +const _getTypedArrayToStringTag = Object.getOwnPropertyDescriptor( + Object.getPrototypeOf(Uint8Array).prototype, + Symbol.toStringTag, +)!.get!; + +// https://tc39.es/ecma262/#sec-get-set.prototype.size +const _getSetSize = Object.getOwnPropertyDescriptor( + Set.prototype, + "size", +)!.get!; + +// https://tc39.es/ecma262/#sec-get-map.prototype.size +const _getMapSize = Object.getOwnPropertyDescriptor( + Map.prototype, + "size", +)!.get!; + +function isObjectLike( + value: unknown, +): value is Record { + return value !== null && typeof value === "object"; +} + +export function isAnyArrayBuffer( + value: unknown, +): value is ArrayBuffer | SharedArrayBuffer { + return isArrayBuffer(value) || isSharedArrayBuffer(value); +} + +export function isArgumentsObject(value: unknown): value is IArguments { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === undefined && + _toString.call(value) === "[object Arguments]" + ); +} + +export function isArrayBuffer(value: unknown): value is ArrayBuffer { + try { + _getArrayBufferByteLength.call(value); + return true; + } catch { + return false; + } +} + +export function isAsyncFunction( + value: unknown, +): value is (...args: unknown[]) => Promise { + return ( + typeof value === "function" && + // @ts-ignore: function is a kind of object + value[Symbol.toStringTag] === "AsyncFunction" + ); +} + +// deno-lint-ignore ban-types +export function isBooleanObject(value: unknown): value is Boolean { + if (!isObjectLike(value)) { + return false; + } + + try { + _booleanValueOf.call(value); + return true; + } catch { + return false; + } +} + +export function isBoxedPrimitive( + value: unknown, + // deno-lint-ignore ban-types +): value is Boolean | String | Number | Symbol | BigInt { + return ( + isBooleanObject(value) || + isStringObject(value) || + isNumberObject(value) || + isSymbolObject(value) || + isBigIntObject(value) + ); +} + +export function isDataView(value: unknown): value is DataView { + return ( + ArrayBuffer.isView(value) && + _getTypedArrayToStringTag.call(value) === undefined + ); +} + +export function isDate(value: unknown): value is Date { + try { + _dateValueOf.call(value); + return true; + } catch { + return false; + } +} + +export function isGeneratorFunction( + value: unknown, +): value is GeneratorFunction { + return ( + typeof value === "function" && + // @ts-ignore: function is a kind of object + value[Symbol.toStringTag] === "GeneratorFunction" + ); +} + +export function isGeneratorObject(value: unknown): value is Generator { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === "Generator" + ); +} + +export function isMap(value: unknown): value is Map { + try { + _getMapSize.call(value); + return true; + } catch { + return false; + } +} + +export function isMapIterator( + value: unknown, +): value is IterableIterator<[unknown, unknown]> { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === "Map Iterator" + ); +} + +export function isModuleNamespaceObject( + value: unknown, +): value is Record { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === "Module" + ); +} + +export function isNativeError(value: unknown): value is Error { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === undefined && + _toString.call(value) === "[object Error]" + ); +} + +// deno-lint-ignore ban-types +export function isNumberObject(value: unknown): value is Number { + if (!isObjectLike(value)) { + return false; + } + + try { + _numberValueOf.call(value); + return true; + } catch { + return false; + } +} + +export function isBigIntObject(value: unknown): value is bigint { + if (!isObjectLike(value)) { + return false; + } + + try { + _bigIntValueOf.call(value); + return true; + } catch { + return false; + } +} + +export function isPromise(value: unknown): value is Promise { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === "Promise" + ); +} + +export function isProxy( + value: unknown, +): value is Record { + return core.isProxy(value); +} + +export function isRegExp(value: unknown): value is RegExp { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === undefined && + _toString.call(value) === "[object RegExp]" + ); +} + +export function isSet(value: unknown): value is Set { + try { + _getSetSize.call(value); + return true; + } catch { + return false; + } +} + +export function isSetIterator( + value: unknown, +): value is IterableIterator { + return ( + isObjectLike(value) && + value[Symbol.toStringTag] === "Set Iterator" + ); +} + +export function isSharedArrayBuffer( + value: unknown, +): value is SharedArrayBuffer { + // SharedArrayBuffer is not available on this runtime + if (_getSharedArrayBufferByteLength === undefined) { + return false; + } + + try { + _getSharedArrayBufferByteLength.call(value); + return true; + } catch { + return false; + } +} + +// deno-lint-ignore ban-types +export function isStringObject(value: unknown): value is String { + if (!isObjectLike(value)) { + return false; + } + + try { + _stringValueOf.call(value); + return true; + } catch { + return false; + } +} + +// deno-lint-ignore ban-types +export function isSymbolObject(value: unknown): value is Symbol { + if (!isObjectLike(value)) { + return false; + } + + try { + _symbolValueOf.call(value); + return true; + } catch { + return false; + } +} + +export function isWeakMap( + value: unknown, +): value is WeakMap, unknown> { + try { + // deno-lint-ignore no-explicit-any + _weakMapHas.call(value, null as any); + return true; + } catch { + return false; + } +} + +export function isWeakSet( + value: unknown, +): value is WeakSet> { + try { + // deno-lint-ignore no-explicit-any + _weakSetHas.call(value, null as any); + return true; + } catch { + return false; + } +} + +export default { + isAsyncFunction, + isGeneratorFunction, + isAnyArrayBuffer, + isArrayBuffer, + isArgumentsObject, + isBoxedPrimitive, + isDataView, + // isExternal, + isMap, + isMapIterator, + isModuleNamespaceObject, + isNativeError, + isPromise, + isSet, + isSetIterator, + isWeakMap, + isWeakSet, + isRegExp, + isDate, + isStringObject, + isNumberObject, + isBooleanObject, + isBigIntObject, +}; diff --git a/ext/node/polyfills/internal_binding/udp_wrap.ts b/ext/node/polyfills/internal_binding/udp_wrap.ts new file mode 100644 index 0000000000..54173e3bac --- /dev/null +++ b/ext/node/polyfills/internal_binding/udp_wrap.ts @@ -0,0 +1,504 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { + AsyncWrap, + providerType, +} from "internal:deno_node/polyfills/internal_binding/async_wrap.ts"; +import { GetAddrInfoReqWrap } from "internal:deno_node/polyfills/internal_binding/cares_wrap.ts"; +import { HandleWrap } from "internal:deno_node/polyfills/internal_binding/handle_wrap.ts"; +import { ownerSymbol } from "internal:deno_node/polyfills/internal_binding/symbols.ts"; +import { + codeMap, + errorMap, +} from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import type { ErrnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { isIP } from "internal:deno_node/polyfills/internal/net.ts"; + +import { isLinux, isWindows } from "internal:deno_node/polyfills/_util/os.ts"; + +// @ts-ignore Deno[Deno.internal] is used on purpose here +const DenoListenDatagram = Deno[Deno.internal]?.nodeUnstable?.listenDatagram || + Deno.listenDatagram; + +type MessageType = string | Uint8Array | Buffer | DataView; + +const AF_INET = 2; +const AF_INET6 = 10; + +const UDP_DGRAM_MAXSIZE = 64 * 1024; + +export class SendWrap extends AsyncWrap { + list!: MessageType[]; + address!: string; + port!: number; + + callback!: (error: ErrnoException | null, bytes?: number) => void; + oncomplete!: (err: number | null, sent?: number) => void; + + constructor() { + super(providerType.UDPSENDWRAP); + } +} + +export class UDP extends HandleWrap { + [ownerSymbol]: unknown = null; + + #address?: string; + #family?: string; + #port?: number; + + #remoteAddress?: string; + #remoteFamily?: string; + #remotePort?: number; + + #listener?: Deno.DatagramConn; + #receiving = false; + + #recvBufferSize = UDP_DGRAM_MAXSIZE; + #sendBufferSize = UDP_DGRAM_MAXSIZE; + + onmessage!: ( + nread: number, + handle: UDP, + buf?: Buffer, + rinfo?: { + address: string; + family: "IPv4" | "IPv6"; + port: number; + size?: number; + }, + ) => void; + + lookup!: ( + address: string, + callback: ( + err: ErrnoException | null, + address: string, + family: number, + ) => void, + ) => GetAddrInfoReqWrap | Record; + + constructor() { + super(providerType.UDPWRAP); + } + + addMembership(_multicastAddress: string, _interfaceAddress?: string): number { + notImplemented("udp.UDP.prototype.addMembership"); + } + + addSourceSpecificMembership( + _sourceAddress: string, + _groupAddress: string, + _interfaceAddress?: string, + ): number { + notImplemented("udp.UDP.prototype.addSourceSpecificMembership"); + } + + /** + * Bind to an IPv4 address. + * @param ip The hostname to bind to. + * @param port The port to bind to + * @return An error status code. + */ + bind(ip: string, port: number, flags: number): number { + return this.#doBind(ip, port, flags, AF_INET); + } + + /** + * Bind to an IPv6 address. + * @param ip The hostname to bind to. + * @param port The port to bind to + * @return An error status code. + */ + bind6(ip: string, port: number, flags: number): number { + return this.#doBind(ip, port, flags, AF_INET6); + } + + bufferSize( + size: number, + buffer: boolean, + ctx: Record, + ): number | undefined { + let err: string | undefined; + + if (size > UDP_DGRAM_MAXSIZE) { + err = "EINVAL"; + } else if (!this.#address) { + err = isWindows ? "ENOTSOCK" : "EBADF"; + } + + if (err) { + ctx.errno = codeMap.get(err)!; + ctx.code = err; + ctx.message = errorMap.get(ctx.errno)![1]; + ctx.syscall = buffer ? "uv_recv_buffer_size" : "uv_send_buffer_size"; + + return; + } + + if (size !== 0) { + size = isLinux ? size * 2 : size; + + if (buffer) { + return (this.#recvBufferSize = size); + } + + return (this.#sendBufferSize = size); + } + + return buffer ? this.#recvBufferSize : this.#sendBufferSize; + } + + connect(ip: string, port: number): number { + return this.#doConnect(ip, port, AF_INET); + } + + connect6(ip: string, port: number): number { + return this.#doConnect(ip, port, AF_INET6); + } + + disconnect(): number { + this.#remoteAddress = undefined; + this.#remotePort = undefined; + this.#remoteFamily = undefined; + + return 0; + } + + dropMembership( + _multicastAddress: string, + _interfaceAddress?: string, + ): number { + notImplemented("udp.UDP.prototype.dropMembership"); + } + + dropSourceSpecificMembership( + _sourceAddress: string, + _groupAddress: string, + _interfaceAddress?: string, + ): number { + notImplemented("udp.UDP.prototype.dropSourceSpecificMembership"); + } + + /** + * Populates the provided object with remote address entries. + * @param peername An object to add the remote address entries to. + * @return An error status code. + */ + getpeername(peername: Record): number { + if (this.#remoteAddress === undefined) { + return codeMap.get("EBADF")!; + } + + peername.address = this.#remoteAddress; + peername.port = this.#remotePort!; + peername.family = this.#remoteFamily!; + + return 0; + } + + /** + * Populates the provided object with local address entries. + * @param sockname An object to add the local address entries to. + * @return An error status code. + */ + getsockname(sockname: Record): number { + if (this.#address === undefined) { + return codeMap.get("EBADF")!; + } + + sockname.address = this.#address; + sockname.port = this.#port!; + sockname.family = this.#family!; + + return 0; + } + + /** + * Opens a file descriptor. + * @param fd The file descriptor to open. + * @return An error status code. + */ + open(_fd: number): number { + // REF: https://github.com/denoland/deno/issues/6529 + notImplemented("udp.UDP.prototype.open"); + } + + /** + * Start receiving on the connection. + * @return An error status code. + */ + recvStart(): number { + if (!this.#receiving) { + this.#receiving = true; + this.#receive(); + } + + return 0; + } + + /** + * Stop receiving on the connection. + * @return An error status code. + */ + recvStop(): number { + this.#receiving = false; + + return 0; + } + + override ref() { + notImplemented("udp.UDP.prototype.ref"); + } + + send( + req: SendWrap, + bufs: MessageType[], + count: number, + ...args: [number, string, boolean] | [boolean] + ): number { + return this.#doSend(req, bufs, count, args, AF_INET); + } + + send6( + req: SendWrap, + bufs: MessageType[], + count: number, + ...args: [number, string, boolean] | [boolean] + ): number { + return this.#doSend(req, bufs, count, args, AF_INET6); + } + + setBroadcast(_bool: 0 | 1): number { + notImplemented("udp.UDP.prototype.setBroadcast"); + } + + setMulticastInterface(_interfaceAddress: string): number { + notImplemented("udp.UDP.prototype.setMulticastInterface"); + } + + setMulticastLoopback(_bool: 0 | 1): number { + notImplemented("udp.UDP.prototype.setMulticastLoopback"); + } + + setMulticastTTL(_ttl: number): number { + notImplemented("udp.UDP.prototype.setMulticastTTL"); + } + + setTTL(_ttl: number): number { + notImplemented("udp.UDP.prototype.setTTL"); + } + + override unref() { + notImplemented("udp.UDP.prototype.unref"); + } + + #doBind(ip: string, port: number, _flags: number, family: number): number { + // TODO(cmorten): use flags to inform socket reuse etc. + const listenOptions = { + port, + hostname: ip, + transport: "udp" as const, + }; + + let listener; + + try { + listener = DenoListenDatagram(listenOptions); + } catch (e) { + if (e instanceof Deno.errors.AddrInUse) { + return codeMap.get("EADDRINUSE")!; + } else if (e instanceof Deno.errors.AddrNotAvailable) { + return codeMap.get("EADDRNOTAVAIL")!; + } else if (e instanceof Deno.errors.PermissionDenied) { + throw e; + } + + // TODO(cmorten): map errors to appropriate error codes. + return codeMap.get("UNKNOWN")!; + } + + const address = listener.addr as Deno.NetAddr; + this.#address = address.hostname; + this.#port = address.port; + this.#family = family === AF_INET6 ? ("IPv6" as const) : ("IPv4" as const); + this.#listener = listener; + + return 0; + } + + #doConnect(ip: string, port: number, family: number): number { + this.#remoteAddress = ip; + this.#remotePort = port; + this.#remoteFamily = family === AF_INET6 + ? ("IPv6" as const) + : ("IPv4" as const); + + return 0; + } + + #doSend( + req: SendWrap, + bufs: MessageType[], + _count: number, + args: [number, string, boolean] | [boolean], + _family: number, + ): number { + let hasCallback: boolean; + + if (args.length === 3) { + this.#remotePort = args[0] as number; + this.#remoteAddress = args[1] as string; + hasCallback = args[2] as boolean; + } else { + hasCallback = args[0] as boolean; + } + + const addr: Deno.NetAddr = { + hostname: this.#remoteAddress!, + port: this.#remotePort!, + transport: "udp", + }; + + // Deno.DatagramConn.prototype.send accepts only one Uint8Array + const payload = new Uint8Array( + Buffer.concat( + bufs.map((buf) => { + if (typeof buf === "string") { + return Buffer.from(buf); + } + + return Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength); + }), + ), + ); + + (async () => { + let sent: number; + let err: number | null = null; + + try { + sent = await this.#listener!.send(payload, addr); + } catch (e) { + // TODO(cmorten): map errors to appropriate error codes. + if (e instanceof Deno.errors.BadResource) { + err = codeMap.get("EBADF")!; + } else if ( + e instanceof Error && + e.message.match(/os error (40|90|10040)/) + ) { + err = codeMap.get("EMSGSIZE")!; + } else { + err = codeMap.get("UNKNOWN")!; + } + + sent = 0; + } + + if (hasCallback) { + try { + req.oncomplete(err, sent); + } catch { + // swallow callback errors + } + } + })(); + + return 0; + } + + async #receive() { + if (!this.#receiving) { + return; + } + + const p = new Uint8Array(this.#recvBufferSize); + + let buf: Uint8Array; + let remoteAddr: Deno.NetAddr | null; + let nread: number | null; + + try { + [buf, remoteAddr] = (await this.#listener!.receive(p)) as [ + Uint8Array, + Deno.NetAddr, + ]; + + nread = buf.length; + } catch (e) { + // TODO(cmorten): map errors to appropriate error codes. + if ( + e instanceof Deno.errors.Interrupted || + e instanceof Deno.errors.BadResource + ) { + nread = 0; + } else { + nread = codeMap.get("UNKNOWN")!; + } + + buf = new Uint8Array(0); + remoteAddr = null; + } + + nread ??= 0; + + const rinfo = remoteAddr + ? { + address: remoteAddr.hostname, + port: remoteAddr.port, + family: isIP(remoteAddr.hostname) === 6 + ? ("IPv6" as const) + : ("IPv4" as const), + } + : undefined; + + try { + this.onmessage(nread, this, Buffer.from(buf), rinfo); + } catch { + // swallow callback errors. + } + + this.#receive(); + } + + /** Handle socket closure. */ + override _onClose(): number { + this.#receiving = false; + + this.#address = undefined; + this.#port = undefined; + this.#family = undefined; + + try { + this.#listener!.close(); + } catch { + // listener already closed + } + + this.#listener = undefined; + + return 0; + } +} diff --git a/ext/node/polyfills/internal_binding/url.ts b/ext/node/polyfills/internal_binding/url.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/url.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/util.ts b/ext/node/polyfills/internal_binding/util.ts new file mode 100644 index 0000000000..21d3cb3dd4 --- /dev/null +++ b/ext/node/polyfills/internal_binding/util.ts @@ -0,0 +1,126 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/util-inl.h +// - https://github.com/nodejs/node/blob/master/src/util.cc +// - https://github.com/nodejs/node/blob/master/src/util.h + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export function guessHandleType(_fd: number): string { + notImplemented("util.guessHandleType"); +} + +export const ALL_PROPERTIES = 0; +export const ONLY_WRITABLE = 1; +export const ONLY_ENUMERABLE = 2; +export const ONLY_CONFIGURABLE = 4; +export const ONLY_ENUM_WRITABLE = 6; +export const SKIP_STRINGS = 8; +export const SKIP_SYMBOLS = 16; + +/** + * Efficiently determine whether the provided property key is numeric + * (and thus could be an array indexer) or not. + * + * Always returns true for values of type `'number'`. + * + * Otherwise, only returns true for strings that consist only of positive integers. + * + * Results are cached. + */ +const isNumericLookup: Record = {}; +export function isArrayIndex(value: unknown): value is number | string { + switch (typeof value) { + case "number": + return value >= 0 && (value | 0) === value; + case "string": { + const result = isNumericLookup[value]; + if (result !== void 0) { + return result; + } + const length = value.length; + if (length === 0) { + return isNumericLookup[value] = false; + } + let ch = 0; + let i = 0; + for (; i < length; ++i) { + ch = value.charCodeAt(i); + if ( + i === 0 && ch === 0x30 && length > 1 /* must not start with 0 */ || + ch < 0x30 /* 0 */ || ch > 0x39 /* 9 */ + ) { + return isNumericLookup[value] = false; + } + } + return isNumericLookup[value] = true; + } + default: + return false; + } +} + +export function getOwnNonIndexProperties( + // deno-lint-ignore ban-types + obj: object, + filter: number, +): (string | symbol)[] { + let allProperties = [ + ...Object.getOwnPropertyNames(obj), + ...Object.getOwnPropertySymbols(obj), + ]; + + if (Array.isArray(obj)) { + allProperties = allProperties.filter((k) => !isArrayIndex(k)); + } + + if (filter === ALL_PROPERTIES) { + return allProperties; + } + + const result: (string | symbol)[] = []; + for (const key of allProperties) { + const desc = Object.getOwnPropertyDescriptor(obj, key); + if (desc === undefined) { + continue; + } + if (filter & ONLY_WRITABLE && !desc.writable) { + continue; + } + if (filter & ONLY_ENUMERABLE && !desc.enumerable) { + continue; + } + if (filter & ONLY_CONFIGURABLE && !desc.configurable) { + continue; + } + if (filter & SKIP_STRINGS && typeof key === "string") { + continue; + } + if (filter & SKIP_SYMBOLS && typeof key === "symbol") { + continue; + } + result.push(key); + } + return result; +} diff --git a/ext/node/polyfills/internal_binding/uv.ts b/ext/node/polyfills/internal_binding/uv.ts new file mode 100644 index 0000000000..4ef1b9c411 --- /dev/null +++ b/ext/node/polyfills/internal_binding/uv.ts @@ -0,0 +1,437 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +// This module ports: +// - https://github.com/nodejs/node/blob/master/src/uv.cc +// - https://github.com/nodejs/node/blob/master/deps/uv +// +// See also: http://docs.libuv.org/en/v1.x/errors.html#error-constants + +import { unreachable } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { osType } from "internal:deno_node/polyfills/_util/os.ts"; +import { uvTranslateSysError } from "internal:deno_node/polyfills/internal_binding/_libuv_winerror.ts"; + +// In Node these values are coming from libuv: +// Ref: https://github.com/libuv/libuv/blob/v1.x/include/uv/errno.h +// Ref: https://github.com/nodejs/node/blob/524123fbf064ff64bb6fcd83485cfc27db932f68/lib/internal/errors.js#L383 +// Since there is no easy way to port code from libuv and these maps are +// changing very rarely, we simply extract them from Node and store here. + +// Note +// Run the following to get the map: +// $ node -e "console.log(process.binding('uv').getErrorMap())" +// This setup automatically exports maps from both "win", "linux" & darwin: +// https://github.com/schwarzkopfb/node_errno_map + +type ErrorMapData = Array<[number, [string, string]]>; +type CodeMapData = Array<[string, number]>; + +const codeToErrorWindows: ErrorMapData = [ + [-4093, ["E2BIG", "argument list too long"]], + [-4092, ["EACCES", "permission denied"]], + [-4091, ["EADDRINUSE", "address already in use"]], + [-4090, ["EADDRNOTAVAIL", "address not available"]], + [-4089, ["EAFNOSUPPORT", "address family not supported"]], + [-4088, ["EAGAIN", "resource temporarily unavailable"]], + [-3000, ["EAI_ADDRFAMILY", "address family not supported"]], + [-3001, ["EAI_AGAIN", "temporary failure"]], + [-3002, ["EAI_BADFLAGS", "bad ai_flags value"]], + [-3013, ["EAI_BADHINTS", "invalid value for hints"]], + [-3003, ["EAI_CANCELED", "request canceled"]], + [-3004, ["EAI_FAIL", "permanent failure"]], + [-3005, ["EAI_FAMILY", "ai_family not supported"]], + [-3006, ["EAI_MEMORY", "out of memory"]], + [-3007, ["EAI_NODATA", "no address"]], + [-3008, ["EAI_NONAME", "unknown node or service"]], + [-3009, ["EAI_OVERFLOW", "argument buffer overflow"]], + [-3014, ["EAI_PROTOCOL", "resolved protocol is unknown"]], + [-3010, ["EAI_SERVICE", "service not available for socket type"]], + [-3011, ["EAI_SOCKTYPE", "socket type not supported"]], + [-4084, ["EALREADY", "connection already in progress"]], + [-4083, ["EBADF", "bad file descriptor"]], + [-4082, ["EBUSY", "resource busy or locked"]], + [-4081, ["ECANCELED", "operation canceled"]], + [-4080, ["ECHARSET", "invalid Unicode character"]], + [-4079, ["ECONNABORTED", "software caused connection abort"]], + [-4078, ["ECONNREFUSED", "connection refused"]], + [-4077, ["ECONNRESET", "connection reset by peer"]], + [-4076, ["EDESTADDRREQ", "destination address required"]], + [-4075, ["EEXIST", "file already exists"]], + [-4074, ["EFAULT", "bad address in system call argument"]], + [-4036, ["EFBIG", "file too large"]], + [-4073, ["EHOSTUNREACH", "host is unreachable"]], + [-4072, ["EINTR", "interrupted system call"]], + [-4071, ["EINVAL", "invalid argument"]], + [-4070, ["EIO", "i/o error"]], + [-4069, ["EISCONN", "socket is already connected"]], + [-4068, ["EISDIR", "illegal operation on a directory"]], + [-4067, ["ELOOP", "too many symbolic links encountered"]], + [-4066, ["EMFILE", "too many open files"]], + [-4065, ["EMSGSIZE", "message too long"]], + [-4064, ["ENAMETOOLONG", "name too long"]], + [-4063, ["ENETDOWN", "network is down"]], + [-4062, ["ENETUNREACH", "network is unreachable"]], + [-4061, ["ENFILE", "file table overflow"]], + [-4060, ["ENOBUFS", "no buffer space available"]], + [-4059, ["ENODEV", "no such device"]], + [-4058, ["ENOENT", "no such file or directory"]], + [-4057, ["ENOMEM", "not enough memory"]], + [-4056, ["ENONET", "machine is not on the network"]], + [-4035, ["ENOPROTOOPT", "protocol not available"]], + [-4055, ["ENOSPC", "no space left on device"]], + [-4054, ["ENOSYS", "function not implemented"]], + [-4053, ["ENOTCONN", "socket is not connected"]], + [-4052, ["ENOTDIR", "not a directory"]], + [-4051, ["ENOTEMPTY", "directory not empty"]], + [-4050, ["ENOTSOCK", "socket operation on non-socket"]], + [-4049, ["ENOTSUP", "operation not supported on socket"]], + [-4048, ["EPERM", "operation not permitted"]], + [-4047, ["EPIPE", "broken pipe"]], + [-4046, ["EPROTO", "protocol error"]], + [-4045, ["EPROTONOSUPPORT", "protocol not supported"]], + [-4044, ["EPROTOTYPE", "protocol wrong type for socket"]], + [-4034, ["ERANGE", "result too large"]], + [-4043, ["EROFS", "read-only file system"]], + [-4042, ["ESHUTDOWN", "cannot send after transport endpoint shutdown"]], + [-4041, ["ESPIPE", "invalid seek"]], + [-4040, ["ESRCH", "no such process"]], + [-4039, ["ETIMEDOUT", "connection timed out"]], + [-4038, ["ETXTBSY", "text file is busy"]], + [-4037, ["EXDEV", "cross-device link not permitted"]], + [-4094, ["UNKNOWN", "unknown error"]], + [-4095, ["EOF", "end of file"]], + [-4033, ["ENXIO", "no such device or address"]], + [-4032, ["EMLINK", "too many links"]], + [-4031, ["EHOSTDOWN", "host is down"]], + [-4030, ["EREMOTEIO", "remote I/O error"]], + [-4029, ["ENOTTY", "inappropriate ioctl for device"]], + [-4028, ["EFTYPE", "inappropriate file type or format"]], + [-4027, ["EILSEQ", "illegal byte sequence"]], +]; + +const errorToCodeWindows: CodeMapData = codeToErrorWindows.map(( + [status, [error]], +) => [error, status]); + +const codeToErrorDarwin: ErrorMapData = [ + [-7, ["E2BIG", "argument list too long"]], + [-13, ["EACCES", "permission denied"]], + [-48, ["EADDRINUSE", "address already in use"]], + [-49, ["EADDRNOTAVAIL", "address not available"]], + [-47, ["EAFNOSUPPORT", "address family not supported"]], + [-35, ["EAGAIN", "resource temporarily unavailable"]], + [-3000, ["EAI_ADDRFAMILY", "address family not supported"]], + [-3001, ["EAI_AGAIN", "temporary failure"]], + [-3002, ["EAI_BADFLAGS", "bad ai_flags value"]], + [-3013, ["EAI_BADHINTS", "invalid value for hints"]], + [-3003, ["EAI_CANCELED", "request canceled"]], + [-3004, ["EAI_FAIL", "permanent failure"]], + [-3005, ["EAI_FAMILY", "ai_family not supported"]], + [-3006, ["EAI_MEMORY", "out of memory"]], + [-3007, ["EAI_NODATA", "no address"]], + [-3008, ["EAI_NONAME", "unknown node or service"]], + [-3009, ["EAI_OVERFLOW", "argument buffer overflow"]], + [-3014, ["EAI_PROTOCOL", "resolved protocol is unknown"]], + [-3010, ["EAI_SERVICE", "service not available for socket type"]], + [-3011, ["EAI_SOCKTYPE", "socket type not supported"]], + [-37, ["EALREADY", "connection already in progress"]], + [-9, ["EBADF", "bad file descriptor"]], + [-16, ["EBUSY", "resource busy or locked"]], + [-89, ["ECANCELED", "operation canceled"]], + [-4080, ["ECHARSET", "invalid Unicode character"]], + [-53, ["ECONNABORTED", "software caused connection abort"]], + [-61, ["ECONNREFUSED", "connection refused"]], + [-54, ["ECONNRESET", "connection reset by peer"]], + [-39, ["EDESTADDRREQ", "destination address required"]], + [-17, ["EEXIST", "file already exists"]], + [-14, ["EFAULT", "bad address in system call argument"]], + [-27, ["EFBIG", "file too large"]], + [-65, ["EHOSTUNREACH", "host is unreachable"]], + [-4, ["EINTR", "interrupted system call"]], + [-22, ["EINVAL", "invalid argument"]], + [-5, ["EIO", "i/o error"]], + [-56, ["EISCONN", "socket is already connected"]], + [-21, ["EISDIR", "illegal operation on a directory"]], + [-62, ["ELOOP", "too many symbolic links encountered"]], + [-24, ["EMFILE", "too many open files"]], + [-40, ["EMSGSIZE", "message too long"]], + [-63, ["ENAMETOOLONG", "name too long"]], + [-50, ["ENETDOWN", "network is down"]], + [-51, ["ENETUNREACH", "network is unreachable"]], + [-23, ["ENFILE", "file table overflow"]], + [-55, ["ENOBUFS", "no buffer space available"]], + [-19, ["ENODEV", "no such device"]], + [-2, ["ENOENT", "no such file or directory"]], + [-12, ["ENOMEM", "not enough memory"]], + [-4056, ["ENONET", "machine is not on the network"]], + [-42, ["ENOPROTOOPT", "protocol not available"]], + [-28, ["ENOSPC", "no space left on device"]], + [-78, ["ENOSYS", "function not implemented"]], + [-57, ["ENOTCONN", "socket is not connected"]], + [-20, ["ENOTDIR", "not a directory"]], + [-66, ["ENOTEMPTY", "directory not empty"]], + [-38, ["ENOTSOCK", "socket operation on non-socket"]], + [-45, ["ENOTSUP", "operation not supported on socket"]], + [-1, ["EPERM", "operation not permitted"]], + [-32, ["EPIPE", "broken pipe"]], + [-100, ["EPROTO", "protocol error"]], + [-43, ["EPROTONOSUPPORT", "protocol not supported"]], + [-41, ["EPROTOTYPE", "protocol wrong type for socket"]], + [-34, ["ERANGE", "result too large"]], + [-30, ["EROFS", "read-only file system"]], + [-58, ["ESHUTDOWN", "cannot send after transport endpoint shutdown"]], + [-29, ["ESPIPE", "invalid seek"]], + [-3, ["ESRCH", "no such process"]], + [-60, ["ETIMEDOUT", "connection timed out"]], + [-26, ["ETXTBSY", "text file is busy"]], + [-18, ["EXDEV", "cross-device link not permitted"]], + [-4094, ["UNKNOWN", "unknown error"]], + [-4095, ["EOF", "end of file"]], + [-6, ["ENXIO", "no such device or address"]], + [-31, ["EMLINK", "too many links"]], + [-64, ["EHOSTDOWN", "host is down"]], + [-4030, ["EREMOTEIO", "remote I/O error"]], + [-25, ["ENOTTY", "inappropriate ioctl for device"]], + [-79, ["EFTYPE", "inappropriate file type or format"]], + [-92, ["EILSEQ", "illegal byte sequence"]], +]; + +const errorToCodeDarwin: CodeMapData = codeToErrorDarwin.map(( + [status, [code]], +) => [code, status]); + +const codeToErrorLinux: ErrorMapData = [ + [-7, ["E2BIG", "argument list too long"]], + [-13, ["EACCES", "permission denied"]], + [-98, ["EADDRINUSE", "address already in use"]], + [-99, ["EADDRNOTAVAIL", "address not available"]], + [-97, ["EAFNOSUPPORT", "address family not supported"]], + [-11, ["EAGAIN", "resource temporarily unavailable"]], + [-3000, ["EAI_ADDRFAMILY", "address family not supported"]], + [-3001, ["EAI_AGAIN", "temporary failure"]], + [-3002, ["EAI_BADFLAGS", "bad ai_flags value"]], + [-3013, ["EAI_BADHINTS", "invalid value for hints"]], + [-3003, ["EAI_CANCELED", "request canceled"]], + [-3004, ["EAI_FAIL", "permanent failure"]], + [-3005, ["EAI_FAMILY", "ai_family not supported"]], + [-3006, ["EAI_MEMORY", "out of memory"]], + [-3007, ["EAI_NODATA", "no address"]], + [-3008, ["EAI_NONAME", "unknown node or service"]], + [-3009, ["EAI_OVERFLOW", "argument buffer overflow"]], + [-3014, ["EAI_PROTOCOL", "resolved protocol is unknown"]], + [-3010, ["EAI_SERVICE", "service not available for socket type"]], + [-3011, ["EAI_SOCKTYPE", "socket type not supported"]], + [-114, ["EALREADY", "connection already in progress"]], + [-9, ["EBADF", "bad file descriptor"]], + [-16, ["EBUSY", "resource busy or locked"]], + [-125, ["ECANCELED", "operation canceled"]], + [-4080, ["ECHARSET", "invalid Unicode character"]], + [-103, ["ECONNABORTED", "software caused connection abort"]], + [-111, ["ECONNREFUSED", "connection refused"]], + [-104, ["ECONNRESET", "connection reset by peer"]], + [-89, ["EDESTADDRREQ", "destination address required"]], + [-17, ["EEXIST", "file already exists"]], + [-14, ["EFAULT", "bad address in system call argument"]], + [-27, ["EFBIG", "file too large"]], + [-113, ["EHOSTUNREACH", "host is unreachable"]], + [-4, ["EINTR", "interrupted system call"]], + [-22, ["EINVAL", "invalid argument"]], + [-5, ["EIO", "i/o error"]], + [-106, ["EISCONN", "socket is already connected"]], + [-21, ["EISDIR", "illegal operation on a directory"]], + [-40, ["ELOOP", "too many symbolic links encountered"]], + [-24, ["EMFILE", "too many open files"]], + [-90, ["EMSGSIZE", "message too long"]], + [-36, ["ENAMETOOLONG", "name too long"]], + [-100, ["ENETDOWN", "network is down"]], + [-101, ["ENETUNREACH", "network is unreachable"]], + [-23, ["ENFILE", "file table overflow"]], + [-105, ["ENOBUFS", "no buffer space available"]], + [-19, ["ENODEV", "no such device"]], + [-2, ["ENOENT", "no such file or directory"]], + [-12, ["ENOMEM", "not enough memory"]], + [-64, ["ENONET", "machine is not on the network"]], + [-92, ["ENOPROTOOPT", "protocol not available"]], + [-28, ["ENOSPC", "no space left on device"]], + [-38, ["ENOSYS", "function not implemented"]], + [-107, ["ENOTCONN", "socket is not connected"]], + [-20, ["ENOTDIR", "not a directory"]], + [-39, ["ENOTEMPTY", "directory not empty"]], + [-88, ["ENOTSOCK", "socket operation on non-socket"]], + [-95, ["ENOTSUP", "operation not supported on socket"]], + [-1, ["EPERM", "operation not permitted"]], + [-32, ["EPIPE", "broken pipe"]], + [-71, ["EPROTO", "protocol error"]], + [-93, ["EPROTONOSUPPORT", "protocol not supported"]], + [-91, ["EPROTOTYPE", "protocol wrong type for socket"]], + [-34, ["ERANGE", "result too large"]], + [-30, ["EROFS", "read-only file system"]], + [-108, ["ESHUTDOWN", "cannot send after transport endpoint shutdown"]], + [-29, ["ESPIPE", "invalid seek"]], + [-3, ["ESRCH", "no such process"]], + [-110, ["ETIMEDOUT", "connection timed out"]], + [-26, ["ETXTBSY", "text file is busy"]], + [-18, ["EXDEV", "cross-device link not permitted"]], + [-4094, ["UNKNOWN", "unknown error"]], + [-4095, ["EOF", "end of file"]], + [-6, ["ENXIO", "no such device or address"]], + [-31, ["EMLINK", "too many links"]], + [-112, ["EHOSTDOWN", "host is down"]], + [-121, ["EREMOTEIO", "remote I/O error"]], + [-25, ["ENOTTY", "inappropriate ioctl for device"]], + [-4028, ["EFTYPE", "inappropriate file type or format"]], + [-84, ["EILSEQ", "illegal byte sequence"]], +]; + +const errorToCodeLinux: CodeMapData = codeToErrorLinux.map(( + [status, [code]], +) => [code, status]); + +const codeToErrorFreebsd: ErrorMapData = [ + [-7, ["E2BIG", "argument list too long"]], + [-13, ["EACCES", "permission denied"]], + [-48, ["EADDRINUSE", "address already in use"]], + [-49, ["EADDRNOTAVAIL", "address not available"]], + [-47, ["EAFNOSUPPORT", "address family not supported"]], + [-35, ["EAGAIN", "resource temporarily unavailable"]], + [-3000, ["EAI_ADDRFAMILY", "address family not supported"]], + [-3001, ["EAI_AGAIN", "temporary failure"]], + [-3002, ["EAI_BADFLAGS", "bad ai_flags value"]], + [-3013, ["EAI_BADHINTS", "invalid value for hints"]], + [-3003, ["EAI_CANCELED", "request canceled"]], + [-3004, ["EAI_FAIL", "permanent failure"]], + [-3005, ["EAI_FAMILY", "ai_family not supported"]], + [-3006, ["EAI_MEMORY", "out of memory"]], + [-3007, ["EAI_NODATA", "no address"]], + [-3008, ["EAI_NONAME", "unknown node or service"]], + [-3009, ["EAI_OVERFLOW", "argument buffer overflow"]], + [-3014, ["EAI_PROTOCOL", "resolved protocol is unknown"]], + [-3010, ["EAI_SERVICE", "service not available for socket type"]], + [-3011, ["EAI_SOCKTYPE", "socket type not supported"]], + [-37, ["EALREADY", "connection already in progress"]], + [-9, ["EBADF", "bad file descriptor"]], + [-16, ["EBUSY", "resource busy or locked"]], + [-85, ["ECANCELED", "operation canceled"]], + [-4080, ["ECHARSET", "invalid Unicode character"]], + [-53, ["ECONNABORTED", "software caused connection abort"]], + [-61, ["ECONNREFUSED", "connection refused"]], + [-54, ["ECONNRESET", "connection reset by peer"]], + [-39, ["EDESTADDRREQ", "destination address required"]], + [-17, ["EEXIST", "file already exists"]], + [-14, ["EFAULT", "bad address in system call argument"]], + [-27, ["EFBIG", "file too large"]], + [-65, ["EHOSTUNREACH", "host is unreachable"]], + [-4, ["EINTR", "interrupted system call"]], + [-22, ["EINVAL", "invalid argument"]], + [-5, ["EIO", "i/o error"]], + [-56, ["EISCONN", "socket is already connected"]], + [-21, ["EISDIR", "illegal operation on a directory"]], + [-62, ["ELOOP", "too many symbolic links encountered"]], + [-24, ["EMFILE", "too many open files"]], + [-40, ["EMSGSIZE", "message too long"]], + [-63, ["ENAMETOOLONG", "name too long"]], + [-50, ["ENETDOWN", "network is down"]], + [-51, ["ENETUNREACH", "network is unreachable"]], + [-23, ["ENFILE", "file table overflow"]], + [-55, ["ENOBUFS", "no buffer space available"]], + [-19, ["ENODEV", "no such device"]], + [-2, ["ENOENT", "no such file or directory"]], + [-12, ["ENOMEM", "not enough memory"]], + [-4056, ["ENONET", "machine is not on the network"]], + [-42, ["ENOPROTOOPT", "protocol not available"]], + [-28, ["ENOSPC", "no space left on device"]], + [-78, ["ENOSYS", "function not implemented"]], + [-57, ["ENOTCONN", "socket is not connected"]], + [-20, ["ENOTDIR", "not a directory"]], + [-66, ["ENOTEMPTY", "directory not empty"]], + [-38, ["ENOTSOCK", "socket operation on non-socket"]], + [-45, ["ENOTSUP", "operation not supported on socket"]], + [-84, ["EOVERFLOW", "value too large for defined data type"]], + [-1, ["EPERM", "operation not permitted"]], + [-32, ["EPIPE", "broken pipe"]], + [-92, ["EPROTO", "protocol error"]], + [-43, ["EPROTONOSUPPORT", "protocol not supported"]], + [-41, ["EPROTOTYPE", "protocol wrong type for socket"]], + [-34, ["ERANGE", "result too large"]], + [-30, ["EROFS", "read-only file system"]], + [-58, ["ESHUTDOWN", "cannot send after transport endpoint shutdown"]], + [-29, ["ESPIPE", "invalid seek"]], + [-3, ["ESRCH", "no such process"]], + [-60, ["ETIMEDOUT", "connection timed out"]], + [-26, ["ETXTBSY", "text file is busy"]], + [-18, ["EXDEV", "cross-device link not permitted"]], + [-4094, ["UNKNOWN", "unknown error"]], + [-4095, ["EOF", "end of file"]], + [-6, ["ENXIO", "no such device or address"]], + [-31, ["EMLINK", "too many links"]], + [-64, ["EHOSTDOWN", "host is down"]], + [-4030, ["EREMOTEIO", "remote I/O error"]], + [-25, ["ENOTTY", "inappropriate ioctl for device"]], + [-79, ["EFTYPE", "inappropriate file type or format"]], + [-86, ["EILSEQ", "illegal byte sequence"]], + [-44, ["ESOCKTNOSUPPORT", "socket type not supported"]], +]; + +const errorToCodeFreebsd: CodeMapData = codeToErrorFreebsd.map(( + [status, [code]], +) => [code, status]); + +export const errorMap = new Map( + osType === "windows" + ? codeToErrorWindows + : osType === "darwin" + ? codeToErrorDarwin + : osType === "linux" + ? codeToErrorLinux + : osType === "freebsd" + ? codeToErrorFreebsd + : unreachable(), +); + +export const codeMap = new Map( + osType === "windows" + ? errorToCodeWindows + : osType === "darwin" + ? errorToCodeDarwin + : osType === "linux" + ? errorToCodeLinux + : osType === "freebsd" + ? errorToCodeFreebsd + : unreachable(), +); + +export function mapSysErrnoToUvErrno(sysErrno: number): number { + if (osType === "windows") { + const code = uvTranslateSysError(sysErrno); + return codeMap.get(code) ?? -sysErrno; + } else { + return -sysErrno; + } +} + +export const UV_EAI_MEMORY = codeMap.get("EAI_MEMORY")!; +export const UV_EBADF = codeMap.get("EBADF")!; +export const UV_EEXIST = codeMap.get("EEXIST"); +export const UV_EINVAL = codeMap.get("EINVAL")!; +export const UV_ENOENT = codeMap.get("ENOENT"); +export const UV_ENOTSOCK = codeMap.get("ENOTSOCK")!; +export const UV_UNKNOWN = codeMap.get("UNKNOWN")!; diff --git a/ext/node/polyfills/internal_binding/v8.ts b/ext/node/polyfills/internal_binding/v8.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/v8.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/worker.ts b/ext/node/polyfills/internal_binding/worker.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/worker.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/internal_binding/zlib.ts b/ext/node/polyfills/internal_binding/zlib.ts new file mode 100644 index 0000000000..93e2042b0d --- /dev/null +++ b/ext/node/polyfills/internal_binding/zlib.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +export {}; diff --git a/ext/node/polyfills/module.js b/ext/node/polyfills/module.js new file mode 100644 index 0000000000..2b7c20e26d --- /dev/null +++ b/ext/node/polyfills/module.js @@ -0,0 +1,20 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +const internals = globalThis.__bootstrap.internals; +const m = internals.require.Module; +export const _cache = m._cache; +export const _extensions = m._extensions; +export const _findPath = m._findPath; +export const _initPaths = m._initPaths; +export const _load = m._load; +export const _nodeModulePaths = m._nodeModulePaths; +export const _pathCache = m._pathCache; +export const _preloadModules = m._preloadModules; +export const _resolveFilename = m._resolveFilename; +export const _resolveLookupPaths = m._resolveLookupPaths; +export const builtinModules = m.builtinModules; +export const createRequire = m.createRequire; +export const globalPaths = m.globalPaths; +export const Module = m.Module; +export const wrap = m.wrap; +export default m; diff --git a/ext/node/polyfills/module_all.ts b/ext/node/polyfills/module_all.ts new file mode 100644 index 0000000000..4a5f73b0c8 --- /dev/null +++ b/ext/node/polyfills/module_all.ts @@ -0,0 +1,187 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import _httpAgent from "internal:deno_node/polyfills/_http_agent.mjs"; +import _httpOutgoing from "internal:deno_node/polyfills/_http_outgoing.ts"; +import _streamDuplex from "internal:deno_node/polyfills/internal/streams/duplex.mjs"; +import _streamPassthrough from "internal:deno_node/polyfills/internal/streams/passthrough.mjs"; +import _streamReadable from "internal:deno_node/polyfills/internal/streams/readable.mjs"; +import _streamTransform from "internal:deno_node/polyfills/internal/streams/transform.mjs"; +import _streamWritable from "internal:deno_node/polyfills/internal/streams/writable.mjs"; +import assert from "internal:deno_node/polyfills/assert.ts"; +import assertStrict from "internal:deno_node/polyfills/assert/strict.ts"; +import asyncHooks from "internal:deno_node/polyfills/async_hooks.ts"; +import buffer from "internal:deno_node/polyfills/buffer.ts"; +import childProcess from "internal:deno_node/polyfills/child_process.ts"; +import cluster from "internal:deno_node/polyfills/cluster.ts"; +import console from "internal:deno_node/polyfills/console.ts"; +import constants from "internal:deno_node/polyfills/constants.ts"; +import crypto from "internal:deno_node/polyfills/crypto.ts"; +import dgram from "internal:deno_node/polyfills/dgram.ts"; +import diagnosticsChannel from "internal:deno_node/polyfills/diagnostics_channel.ts"; +import dns from "internal:deno_node/polyfills/dns.ts"; +import dnsPromises from "internal:deno_node/polyfills/dns/promises.ts"; +import domain from "internal:deno_node/polyfills/domain.ts"; +import events from "internal:deno_node/polyfills/events.ts"; +import fs from "internal:deno_node/polyfills/fs.ts"; +import fsPromises from "internal:deno_node/polyfills/fs/promises.ts"; +import http from "internal:deno_node/polyfills/http.ts"; +import http2 from "internal:deno_node/polyfills/http2.ts"; +import https from "internal:deno_node/polyfills/https.ts"; +import inspector from "internal:deno_node/polyfills/inspector.ts"; +import internalCp from "internal:deno_node/polyfills/internal/child_process.ts"; +import internalCryptoCertificate from "internal:deno_node/polyfills/internal/crypto/certificate.ts"; +import internalCryptoCipher from "internal:deno_node/polyfills/internal/crypto/cipher.ts"; +import internalCryptoDiffiehellman from "internal:deno_node/polyfills/internal/crypto/diffiehellman.ts"; +import internalCryptoHash from "internal:deno_node/polyfills/internal/crypto/hash.ts"; +import internalCryptoHkdf from "internal:deno_node/polyfills/internal/crypto/hkdf.ts"; +import internalCryptoKeygen from "internal:deno_node/polyfills/internal/crypto/keygen.ts"; +import internalCryptoKeys from "internal:deno_node/polyfills/internal/crypto/keys.ts"; +import internalCryptoPbkdf2 from "internal:deno_node/polyfills/internal/crypto/pbkdf2.ts"; +import internalCryptoRandom from "internal:deno_node/polyfills/internal/crypto/random.ts"; +import internalCryptoScrypt from "internal:deno_node/polyfills/internal/crypto/scrypt.ts"; +import internalCryptoSig from "internal:deno_node/polyfills/internal/crypto/sig.ts"; +import internalCryptoUtil from "internal:deno_node/polyfills/internal/crypto/util.ts"; +import internalCryptoX509 from "internal:deno_node/polyfills/internal/crypto/x509.ts"; +import internalDgram from "internal:deno_node/polyfills/internal/dgram.ts"; +import internalDnsPromises from "internal:deno_node/polyfills/internal/dns/promises.ts"; +import internalErrors from "internal:deno_node/polyfills/internal/errors.ts"; +import internalEventTarget from "internal:deno_node/polyfills/internal/event_target.mjs"; +import internalFsUtils from "internal:deno_node/polyfills/internal/fs/utils.mjs"; +import internalHttp from "internal:deno_node/polyfills/internal/http.ts"; +import internalReadlineUtils from "internal:deno_node/polyfills/internal/readline/utils.mjs"; +import internalStreamsAddAbortSignal from "internal:deno_node/polyfills/internal/streams/add-abort-signal.mjs"; +import internalStreamsBufferList from "internal:deno_node/polyfills/internal/streams/buffer_list.mjs"; +import internalStreamsLazyTransform from "internal:deno_node/polyfills/internal/streams/lazy_transform.mjs"; +import internalStreamsState from "internal:deno_node/polyfills/internal/streams/state.mjs"; +import internalTestBinding from "internal:deno_node/polyfills/internal/test/binding.ts"; +import internalTimers from "internal:deno_node/polyfills/internal/timers.mjs"; +import internalUtil from "internal:deno_node/polyfills/internal/util.mjs"; +import internalUtilInspect from "internal:deno_node/polyfills/internal/util/inspect.mjs"; +import net from "internal:deno_node/polyfills/net.ts"; +import os from "internal:deno_node/polyfills/os.ts"; +import pathPosix from "internal:deno_node/polyfills/path/posix.ts"; +import pathWin32 from "internal:deno_node/polyfills/path/win32.ts"; +import path from "internal:deno_node/polyfills/path.ts"; +import perfHooks from "internal:deno_node/polyfills/perf_hooks.ts"; +import punycode from "internal:deno_node/polyfills/punycode.ts"; +import process from "internal:deno_node/polyfills/process.ts"; +import querystring from "internal:deno_node/polyfills/querystring.ts"; +import readline from "internal:deno_node/polyfills/readline.ts"; +import readlinePromises from "internal:deno_node/polyfills/readline/promises.ts"; +import repl from "internal:deno_node/polyfills/repl.ts"; +import stream from "internal:deno_node/polyfills/stream.ts"; +import streamConsumers from "internal:deno_node/polyfills/stream/consumers.mjs"; +import streamPromises from "internal:deno_node/polyfills/stream/promises.mjs"; +import streamWeb from "internal:deno_node/polyfills/stream/web.ts"; +import stringDecoder from "internal:deno_node/polyfills/string_decoder.ts"; +import sys from "internal:deno_node/polyfills/sys.ts"; +import timers from "internal:deno_node/polyfills/timers.ts"; +import timersPromises from "internal:deno_node/polyfills/timers/promises.ts"; +import tls from "internal:deno_node/polyfills/tls.ts"; +import tty from "internal:deno_node/polyfills/tty.ts"; +import url from "internal:deno_node/polyfills/url.ts"; +import utilTypes from "internal:deno_node/polyfills/util/types.ts"; +import util from "internal:deno_node/polyfills/util.ts"; +import v8 from "internal:deno_node/polyfills/v8.ts"; +import vm from "internal:deno_node/polyfills/vm.ts"; +import workerThreads from "internal:deno_node/polyfills/worker_threads.ts"; +import wasi from "internal:deno_node/polyfills/wasi.ts"; +import zlib from "internal:deno_node/polyfills/zlib.ts"; + +// Canonical mapping of supported modules +export default { + "_http_agent": _httpAgent, + "_http_outgoing": _httpOutgoing, + "_stream_duplex": _streamDuplex, + "_stream_passthrough": _streamPassthrough, + "_stream_readable": _streamReadable, + "_stream_transform": _streamTransform, + "_stream_writable": _streamWritable, + assert, + "assert/strict": assertStrict, + "async_hooks": asyncHooks, + buffer, + crypto, + console, + constants, + child_process: childProcess, + cluster, + dgram, + diagnostics_channel: diagnosticsChannel, + dns, + "dns/promises": dnsPromises, + domain, + events, + fs, + "fs/promises": fsPromises, + http, + http2, + https, + inspector, + "internal/child_process": internalCp, + "internal/crypto/certificate": internalCryptoCertificate, + "internal/crypto/cipher": internalCryptoCipher, + "internal/crypto/diffiehellman": internalCryptoDiffiehellman, + "internal/crypto/hash": internalCryptoHash, + "internal/crypto/hkdf": internalCryptoHkdf, + "internal/crypto/keygen": internalCryptoKeygen, + "internal/crypto/keys": internalCryptoKeys, + "internal/crypto/pbkdf2": internalCryptoPbkdf2, + "internal/crypto/random": internalCryptoRandom, + "internal/crypto/scrypt": internalCryptoScrypt, + "internal/crypto/sig": internalCryptoSig, + "internal/crypto/util": internalCryptoUtil, + "internal/crypto/x509": internalCryptoX509, + "internal/dgram": internalDgram, + "internal/dns/promises": internalDnsPromises, + "internal/errors": internalErrors, + "internal/event_target": internalEventTarget, + "internal/fs/utils": internalFsUtils, + "internal/http": internalHttp, + "internal/readline/utils": internalReadlineUtils, + "internal/streams/add-abort-signal": internalStreamsAddAbortSignal, + "internal/streams/buffer_list": internalStreamsBufferList, + "internal/streams/lazy_transform": internalStreamsLazyTransform, + "internal/streams/state": internalStreamsState, + "internal/test/binding": internalTestBinding, + "internal/timers": internalTimers, + "internal/util/inspect": internalUtilInspect, + "internal/util": internalUtil, + net, + os, + "path/posix": pathPosix, + "path/win32": pathWin32, + path, + perf_hooks: perfHooks, + process, + get punycode() { + process.emitWarning( + "The `punycode` module is deprecated. Please use a userland " + + "alternative instead.", + "DeprecationWarning", + "DEP0040", + ); + return punycode; + }, + querystring, + readline, + "readline/promises": readlinePromises, + repl, + stream, + "stream/consumers": streamConsumers, + "stream/promises": streamPromises, + "stream/web": streamWeb, + string_decoder: stringDecoder, + sys, + timers, + "timers/promises": timersPromises, + tls, + tty, + url, + util, + "util/types": utilTypes, + v8, + vm, + wasi, + worker_threads: workerThreads, + zlib, +} as Record; diff --git a/ext/node/polyfills/module_esm.ts b/ext/node/polyfills/module_esm.ts new file mode 100644 index 0000000000..e9cb38ff1f --- /dev/null +++ b/ext/node/polyfills/module_esm.ts @@ -0,0 +1,845 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +/** + * NOTE(bartlomieju): + * Functionality of this file is ported in Rust in `cli/compat/esm_resolver.ts`. + * Unfortunately we have no way to call ESM resolution in Rust from TypeScript code. + */ + +import { + fileURLToPath, + pathToFileURL, +} from "internal:deno_node/polyfills/url.ts"; +import { + ERR_INVALID_MODULE_SPECIFIER, + ERR_INVALID_PACKAGE_CONFIG, + ERR_INVALID_PACKAGE_TARGET, + ERR_MODULE_NOT_FOUND, + ERR_PACKAGE_IMPORT_NOT_DEFINED, + ERR_PACKAGE_PATH_NOT_EXPORTED, + NodeError, +} from "internal:deno_node/polyfills/internal/errors.ts"; + +const { hasOwn } = Object; + +export const encodedSepRegEx = /%2F|%2C/i; + +function throwInvalidSubpath( + subpath: string, + packageJSONUrl: string, + internal: boolean, + base: string, +) { + const reason = `request is not a valid subpath for the "${ + internal ? "imports" : "exports" + }" resolution of ${fileURLToPath(packageJSONUrl)}`; + throw new ERR_INVALID_MODULE_SPECIFIER( + subpath, + reason, + base && fileURLToPath(base), + ); +} + +function throwInvalidPackageTarget( + subpath: string, + // deno-lint-ignore no-explicit-any + target: any, + packageJSONUrl: string, + internal: boolean, + base: string, +) { + if (typeof target === "object" && target !== null) { + target = JSON.stringify(target, null, ""); + } else { + target = `${target}`; + } + throw new ERR_INVALID_PACKAGE_TARGET( + fileURLToPath(new URL(".", packageJSONUrl)), + subpath, + target, + internal, + base && fileURLToPath(base), + ); +} + +function throwImportNotDefined( + specifier: string, + packageJSONUrl: URL | undefined, + base: string | URL, +): TypeError & { code: string } { + throw new ERR_PACKAGE_IMPORT_NOT_DEFINED( + specifier, + packageJSONUrl && fileURLToPath(new URL(".", packageJSONUrl)), + fileURLToPath(base), + ); +} + +function throwExportsNotFound( + subpath: string, + packageJSONUrl: string, + base?: string, +): Error & { code: string } { + throw new ERR_PACKAGE_PATH_NOT_EXPORTED( + subpath, + fileURLToPath(new URL(".", packageJSONUrl)), + base && fileURLToPath(base), + ); +} + +function patternKeyCompare(a: string, b: string): number { + const aPatternIndex = a.indexOf("*"); + const bPatternIndex = b.indexOf("*"); + const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; + const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; + if (baseLenA > baseLenB) return -1; + if (baseLenB > baseLenA) return 1; + if (aPatternIndex === -1) return 1; + if (bPatternIndex === -1) return -1; + if (a.length > b.length) return -1; + if (b.length > a.length) return 1; + return 0; +} + +function fileExists(url: string | URL): boolean { + try { + const info = Deno.statSync(url); + return info.isFile; + } catch { + return false; + } +} + +function tryStatSync(path: string): { isDirectory: boolean } { + try { + const info = Deno.statSync(path); + return { isDirectory: info.isDirectory }; + } catch { + return { isDirectory: false }; + } +} + +/** + * Legacy CommonJS main resolution: + * 1. let M = pkg_url + (json main field) + * 2. TRY(M, M.js, M.json, M.node) + * 3. TRY(M/index.js, M/index.json, M/index.node) + * 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node) + * 5. NOT_FOUND + */ +function legacyMainResolve( + packageJSONUrl: URL, + packageConfig: PackageConfig, + base: string | URL, +): URL { + let guess; + if (packageConfig.main !== undefined) { + // Note: fs check redundances will be handled by Descriptor cache here. + if ( + fileExists(guess = new URL(`./${packageConfig.main}`, packageJSONUrl)) + ) { + return guess; + } else if ( + fileExists(guess = new URL(`./${packageConfig.main}.js`, packageJSONUrl)) + ) { + // pass + } else if ( + fileExists( + guess = new URL(`./${packageConfig.main}.json`, packageJSONUrl), + ) + ) { + // pass + } else if ( + fileExists( + guess = new URL(`./${packageConfig.main}.node`, packageJSONUrl), + ) + ) { + // pass + } else if ( + fileExists( + guess = new URL(`./${packageConfig.main}/index.js`, packageJSONUrl), + ) + ) { + // pass + } else if ( + fileExists( + guess = new URL(`./${packageConfig.main}/index.json`, packageJSONUrl), + ) + ) { + // pass + } else if ( + fileExists( + guess = new URL(`./${packageConfig.main}/index.node`, packageJSONUrl), + ) + ) { + // pass + } else guess = undefined; + if (guess) { + // TODO(bartlomieju): + // emitLegacyIndexDeprecation(guess, packageJSONUrl, base, + // packageConfig.main); + return guess; + } + // Fallthrough. + } + if (fileExists(guess = new URL("./index.js", packageJSONUrl))) { + // pass + } // So fs. + else if (fileExists(guess = new URL("./index.json", packageJSONUrl))) { + // pass + } else if (fileExists(guess = new URL("./index.node", packageJSONUrl))) { + // pass + } else guess = undefined; + if (guess) { + // TODO(bartlomieju): + // emitLegacyIndexDeprecation(guess, packageJSONUrl, base, packageConfig.main); + return guess; + } + // Not found. + throw new ERR_MODULE_NOT_FOUND( + fileURLToPath(new URL(".", packageJSONUrl)), + fileURLToPath(base), + ); +} + +function parsePackageName( + specifier: string, + base: string | URL, +): { packageName: string; packageSubpath: string; isScoped: boolean } { + let separatorIndex = specifier.indexOf("/"); + let validPackageName = true; + let isScoped = false; + if (specifier[0] === "@") { + isScoped = true; + if (separatorIndex === -1 || specifier.length === 0) { + validPackageName = false; + } else { + separatorIndex = specifier.indexOf("/", separatorIndex + 1); + } + } + + const packageName = separatorIndex === -1 + ? specifier + : specifier.slice(0, separatorIndex); + + // Package name cannot have leading . and cannot have percent-encoding or + // separators. + for (let i = 0; i < packageName.length; i++) { + if (packageName[i] === "%" || packageName[i] === "\\") { + validPackageName = false; + break; + } + } + + if (!validPackageName) { + throw new ERR_INVALID_MODULE_SPECIFIER( + specifier, + "is not a valid package name", + fileURLToPath(base), + ); + } + + const packageSubpath = "." + + (separatorIndex === -1 ? "" : specifier.slice(separatorIndex)); + + return { packageName, packageSubpath, isScoped }; +} + +function packageResolve( + specifier: string, + base: string, + conditions: Set, +): URL | undefined { + const { packageName, packageSubpath, isScoped } = parsePackageName( + specifier, + base, + ); + + // ResolveSelf + const packageConfig = getPackageScopeConfig(base); + if (packageConfig.exists) { + const packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); + if ( + packageConfig.name === packageName && + packageConfig.exports !== undefined && packageConfig.exports !== null + ) { + return packageExportsResolve( + packageJSONUrl.toString(), + packageSubpath, + packageConfig, + base, + conditions, + ); + } + } + + let packageJSONUrl = new URL( + "./node_modules/" + packageName + "/package.json", + base, + ); + let packageJSONPath = fileURLToPath(packageJSONUrl); + let lastPath; + do { + const stat = tryStatSync( + packageJSONPath.slice(0, packageJSONPath.length - 13), + ); + if (!stat.isDirectory) { + lastPath = packageJSONPath; + packageJSONUrl = new URL( + (isScoped ? "../../../../node_modules/" : "../../../node_modules/") + + packageName + "/package.json", + packageJSONUrl, + ); + packageJSONPath = fileURLToPath(packageJSONUrl); + continue; + } + + // Package match. + const packageConfig = getPackageConfig(packageJSONPath, specifier, base); + if (packageConfig.exports !== undefined && packageConfig.exports !== null) { + return packageExportsResolve( + packageJSONUrl.toString(), + packageSubpath, + packageConfig, + base, + conditions, + ); + } + if (packageSubpath === ".") { + return legacyMainResolve(packageJSONUrl, packageConfig, base); + } + return new URL(packageSubpath, packageJSONUrl); + // Cross-platform root check. + } while (packageJSONPath.length !== lastPath.length); + + // TODO(bartlomieju): this is false positive + // deno-lint-ignore no-unreachable + throw new ERR_MODULE_NOT_FOUND(packageName, fileURLToPath(base)); +} + +const invalidSegmentRegEx = /(^|\\|\/)(\.\.?|node_modules)(\\|\/|$)/; +const patternRegEx = /\*/g; + +function resolvePackageTargetString( + target: string, + subpath: string, + match: string, + packageJSONUrl: string, + base: string, + pattern: boolean, + internal: boolean, + conditions: Set, +): URL | undefined { + if (subpath !== "" && !pattern && target[target.length - 1] !== "/") { + throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); + } + + if (!target.startsWith("./")) { + if ( + internal && !target.startsWith("../") && + !target.startsWith("/") + ) { + let isURL = false; + try { + new URL(target); + isURL = true; + } catch { + // pass + } + if (!isURL) { + const exportTarget = pattern + ? target.replace(patternRegEx, () => subpath) + : target + subpath; + return packageResolve(exportTarget, packageJSONUrl, conditions); + } + } + throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); + } + + if (invalidSegmentRegEx.test(target.slice(2))) { + throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); + } + + const resolved = new URL(target, packageJSONUrl); + const resolvedPath = resolved.pathname; + const packagePath = new URL(".", packageJSONUrl).pathname; + + if (!resolvedPath.startsWith(packagePath)) { + throwInvalidPackageTarget(match, target, packageJSONUrl, internal, base); + } + + if (subpath === "") return resolved; + + if (invalidSegmentRegEx.test(subpath)) { + const request = pattern + ? match.replace("*", () => subpath) + : match + subpath; + throwInvalidSubpath(request, packageJSONUrl, internal, base); + } + + if (pattern) { + return new URL(resolved.href.replace(patternRegEx, () => subpath)); + } + return new URL(subpath, resolved); +} + +function isArrayIndex(key: string): boolean { + const keyNum = +key; + if (`${keyNum}` !== key) return false; + return keyNum >= 0 && keyNum < 0xFFFF_FFFF; +} + +function resolvePackageTarget( + packageJSONUrl: string, + // deno-lint-ignore no-explicit-any + target: any, + subpath: string, + packageSubpath: string, + base: string, + pattern: boolean, + internal: boolean, + conditions: Set, +): URL | undefined { + if (typeof target === "string") { + return resolvePackageTargetString( + target, + subpath, + packageSubpath, + packageJSONUrl, + base, + pattern, + internal, + conditions, + ); + } else if (Array.isArray(target)) { + if (target.length === 0) { + return undefined; + } + + let lastException; + for (let i = 0; i < target.length; i++) { + const targetItem = target[i]; + let resolved; + try { + resolved = resolvePackageTarget( + packageJSONUrl, + targetItem, + subpath, + packageSubpath, + base, + pattern, + internal, + conditions, + ); + } catch (e: unknown) { + lastException = e; + if (e instanceof NodeError && e.code === "ERR_INVALID_PACKAGE_TARGET") { + continue; + } + throw e; + } + if (resolved === undefined) { + continue; + } + if (resolved === null) { + lastException = null; + continue; + } + return resolved; + } + if (lastException === undefined || lastException === null) { + return undefined; + } + throw lastException; + } else if (typeof target === "object" && target !== null) { + const keys = Object.getOwnPropertyNames(target); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (isArrayIndex(key)) { + throw new ERR_INVALID_PACKAGE_CONFIG( + fileURLToPath(packageJSONUrl), + base, + '"exports" cannot contain numeric property keys.', + ); + } + } + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + if (key === "default" || conditions.has(key)) { + const conditionalTarget = target[key]; + const resolved = resolvePackageTarget( + packageJSONUrl, + conditionalTarget, + subpath, + packageSubpath, + base, + pattern, + internal, + conditions, + ); + if (resolved === undefined) { + continue; + } + return resolved; + } + } + return undefined; + } else if (target === null) { + return undefined; + } + throwInvalidPackageTarget( + packageSubpath, + target, + packageJSONUrl, + internal, + base, + ); +} + +export function packageExportsResolve( + packageJSONUrl: string, + packageSubpath: string, + packageConfig: PackageConfig, + base: string, + conditions: Set, + // @ts-ignore `URL` needs to be forced due to control flow +): URL { + let exports = packageConfig.exports; + if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) { + exports = { ".": exports }; + } + + if ( + hasOwn(exports, packageSubpath) && + !packageSubpath.includes("*") && + !packageSubpath.endsWith("/") + ) { + const target = exports[packageSubpath]; + const resolved = resolvePackageTarget( + packageJSONUrl, + target, + "", + packageSubpath, + base, + false, + false, + conditions, + ); + if (resolved === null || resolved === undefined) { + throwExportsNotFound(packageSubpath, packageJSONUrl, base); + } + return resolved!; + } + + let bestMatch = ""; + let bestMatchSubpath = ""; + const keys = Object.getOwnPropertyNames(exports); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const patternIndex = key.indexOf("*"); + if ( + patternIndex !== -1 && + packageSubpath.startsWith(key.slice(0, patternIndex)) + ) { + // When this reaches EOL, this can throw at the top of the whole function: + // + // if (StringPrototypeEndsWith(packageSubpath, '/')) + // throwInvalidSubpath(packageSubpath) + // + // To match "imports" and the spec. + if (packageSubpath.endsWith("/")) { + // TODO(@bartlomieju): + // emitTrailingSlashPatternDeprecation( + // packageSubpath, + // packageJSONUrl, + // base, + // ); + } + const patternTrailer = key.slice(patternIndex + 1); + if ( + packageSubpath.length >= key.length && + packageSubpath.endsWith(patternTrailer) && + patternKeyCompare(bestMatch, key) === 1 && + key.lastIndexOf("*") === patternIndex + ) { + bestMatch = key; + bestMatchSubpath = packageSubpath.slice( + patternIndex, + packageSubpath.length - patternTrailer.length, + ); + } + } + } + + if (bestMatch) { + const target = exports[bestMatch]; + const resolved = resolvePackageTarget( + packageJSONUrl, + target, + bestMatchSubpath, + bestMatch, + base, + true, + false, + conditions, + ); + if (resolved === null || resolved === undefined) { + throwExportsNotFound(packageSubpath, packageJSONUrl, base); + } + return resolved!; + } + + throwExportsNotFound(packageSubpath, packageJSONUrl, base); +} + +export interface PackageConfig { + pjsonPath: string; + exists: boolean; + name?: string; + main?: string; + // deno-lint-ignore no-explicit-any + exports?: any; + // deno-lint-ignore no-explicit-any + imports?: any; + type?: string; +} + +const packageJSONCache = new Map(); /* string -> PackageConfig */ + +function getPackageConfig( + path: string, + specifier: string | URL, + base?: string | URL, +): PackageConfig { + const existing = packageJSONCache.get(path); + if (existing !== undefined) { + return existing; + } + + let source: string | undefined; + try { + source = new TextDecoder().decode( + Deno.readFileSync(path), + ); + } catch { + // pass + } + + if (source === undefined) { + const packageConfig = { + pjsonPath: path, + exists: false, + main: undefined, + name: undefined, + type: "none", + exports: undefined, + imports: undefined, + }; + packageJSONCache.set(path, packageConfig); + return packageConfig; + } + + let packageJSON; + try { + packageJSON = JSON.parse(source); + } catch (error) { + throw new ERR_INVALID_PACKAGE_CONFIG( + path, + (base ? `"${specifier}" from ` : "") + fileURLToPath(base || specifier), + // @ts-ignore there's no assertion for type and `error` is thus `unknown` + error.message, + ); + } + + let { imports, main, name, type } = packageJSON; + const { exports } = packageJSON; + if (typeof imports !== "object" || imports === null) imports = undefined; + if (typeof main !== "string") main = undefined; + if (typeof name !== "string") name = undefined; + // Ignore unknown types for forwards compatibility + if (type !== "module" && type !== "commonjs") type = "none"; + + const packageConfig = { + pjsonPath: path, + exists: true, + main, + name, + type, + exports, + imports, + }; + packageJSONCache.set(path, packageConfig); + return packageConfig; +} + +function getPackageScopeConfig(resolved: URL | string): PackageConfig { + let packageJSONUrl = new URL("./package.json", resolved); + while (true) { + const packageJSONPath = packageJSONUrl.pathname; + if (packageJSONPath.endsWith("node_modules/package.json")) { + break; + } + const packageConfig = getPackageConfig( + fileURLToPath(packageJSONUrl), + resolved, + ); + if (packageConfig.exists) return packageConfig; + + const lastPackageJSONUrl = packageJSONUrl; + packageJSONUrl = new URL("../package.json", packageJSONUrl); + + // Terminates at root where ../package.json equals ../../package.json + // (can't just check "/package.json" for Windows support). + if (packageJSONUrl.pathname === lastPackageJSONUrl.pathname) break; + } + const packageJSONPath = fileURLToPath(packageJSONUrl); + const packageConfig = { + pjsonPath: packageJSONPath, + exists: false, + main: undefined, + name: undefined, + type: "none", + exports: undefined, + imports: undefined, + }; + packageJSONCache.set(packageJSONPath, packageConfig); + return packageConfig; +} + +export function packageImportsResolve( + name: string, + base: string, + conditions: Set, + // @ts-ignore `URL` needs to be forced due to control flow +): URL { + if ( + name === "#" || name.startsWith("#/") || + name.startsWith("/") + ) { + const reason = "is not a valid internal imports specifier name"; + throw new ERR_INVALID_MODULE_SPECIFIER(name, reason, fileURLToPath(base)); + } + let packageJSONUrl; + const packageConfig = getPackageScopeConfig(base); + if (packageConfig.exists) { + packageJSONUrl = pathToFileURL(packageConfig.pjsonPath); + const imports = packageConfig.imports; + if (imports) { + if ( + hasOwn(imports, name) && + !name.includes("*") + ) { + const resolved = resolvePackageTarget( + packageJSONUrl.toString(), + imports[name], + "", + name, + base, + false, + true, + conditions, + ); + if (resolved !== null && resolved !== undefined) { + return resolved; + } + } else { + let bestMatch = ""; + let bestMatchSubpath = ""; + const keys = Object.getOwnPropertyNames(imports); + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const patternIndex = key.indexOf("*"); + if ( + patternIndex !== -1 && + name.startsWith( + key.slice(0, patternIndex), + ) + ) { + const patternTrailer = key.slice(patternIndex + 1); + if ( + name.length >= key.length && + name.endsWith(patternTrailer) && + patternKeyCompare(bestMatch, key) === 1 && + key.lastIndexOf("*") === patternIndex + ) { + bestMatch = key; + bestMatchSubpath = name.slice( + patternIndex, + name.length - patternTrailer.length, + ); + } + } + } + + if (bestMatch) { + const target = imports[bestMatch]; + const resolved = resolvePackageTarget( + packageJSONUrl.toString(), + target, + bestMatchSubpath, + bestMatch, + base, + true, + true, + conditions, + ); + if (resolved !== null && resolved !== undefined) { + return resolved; + } + } + } + } + } + throwImportNotDefined(name, packageJSONUrl, base); +} + +function isConditionalExportsMainSugar( + // deno-lint-ignore no-explicit-any + exports: any, + packageJSONUrl: string, + base: string, +): boolean { + if (typeof exports === "string" || Array.isArray(exports)) return true; + if (typeof exports !== "object" || exports === null) return false; + + const keys = Object.getOwnPropertyNames(exports); + let isConditionalSugar = false; + let i = 0; + for (let j = 0; j < keys.length; j++) { + const key = keys[j]; + const curIsConditionalSugar = key === "" || key[0] !== "."; + if (i++ === 0) { + isConditionalSugar = curIsConditionalSugar; + } else if (isConditionalSugar !== curIsConditionalSugar) { + const message = + "\"exports\" cannot contain some keys starting with '.' and some not." + + " The exports object must either be an object of package subpath keys" + + " or an object of main entry condition name keys only."; + throw new ERR_INVALID_PACKAGE_CONFIG( + fileURLToPath(packageJSONUrl), + base, + message, + ); + } + } + return isConditionalSugar; +} diff --git a/ext/node/polyfills/net.ts b/ext/node/polyfills/net.ts new file mode 100644 index 0000000000..e5f157f099 --- /dev/null +++ b/ext/node/polyfills/net.ts @@ -0,0 +1,2483 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { + isIP, + isIPv4, + isIPv6, + normalizedArgsSymbol, +} from "internal:deno_node/polyfills/internal/net.ts"; +import { Duplex } from "internal:deno_node/polyfills/stream.ts"; +import { + asyncIdSymbol, + defaultTriggerAsyncIdScope, + newAsyncId, + ownerSymbol, +} from "internal:deno_node/polyfills/internal/async_hooks.ts"; +import { + ERR_INVALID_ADDRESS_FAMILY, + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_FD_TYPE, + ERR_INVALID_IP_ADDRESS, + ERR_MISSING_ARGS, + ERR_SERVER_ALREADY_LISTEN, + ERR_SERVER_NOT_RUNNING, + ERR_SOCKET_CLOSED, + errnoException, + exceptionWithHostPort, + genericNodeError, + uvExceptionWithHostPort, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import type { ErrnoException } from "internal:deno_node/polyfills/internal/errors.ts"; +import { Encodings } from "internal:deno_node/polyfills/_utils.ts"; +import { isUint8Array } from "internal:deno_node/polyfills/internal/util/types.ts"; +import { + kAfterAsyncWrite, + kBuffer, + kBufferCb, + kBufferGen, + kHandle, + kUpdateTimer, + onStreamRead, + setStreamTimeout, + writeGeneric, + writevGeneric, +} from "internal:deno_node/polyfills/internal/stream_base_commons.ts"; +import { kTimeout } from "internal:deno_node/polyfills/internal/timers.mjs"; +import { nextTick } from "internal:deno_node/polyfills/_next_tick.ts"; +import { + DTRACE_NET_SERVER_CONNECTION, + DTRACE_NET_STREAM_END, +} from "internal:deno_node/polyfills/internal/dtrace.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import type { LookupOneOptions } from "internal:deno_node/polyfills/internal/dns/utils.ts"; +import { + validateAbortSignal, + validateFunction, + validateInt32, + validateNumber, + validatePort, + validateString, +} from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + constants as TCPConstants, + TCP, + TCPConnectWrap, +} from "internal:deno_node/polyfills/internal_binding/tcp_wrap.ts"; +import { + constants as PipeConstants, + Pipe, + PipeConnectWrap, +} from "internal:deno_node/polyfills/internal_binding/pipe_wrap.ts"; +import { ShutdownWrap } from "internal:deno_node/polyfills/internal_binding/stream_wrap.ts"; +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; +import { + ADDRCONFIG, + lookup as dnsLookup, +} from "internal:deno_node/polyfills/dns.ts"; +import { codeMap } from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import { guessHandleType } from "internal:deno_node/polyfills/internal_binding/util.ts"; +import { debuglog } from "internal:deno_node/polyfills/internal/util/debuglog.ts"; +import type { DuplexOptions } from "internal:deno_node/polyfills/_stream.d.ts"; +import type { BufferEncoding } from "internal:deno_node/polyfills/_global.d.ts"; +import type { Abortable } from "internal:deno_node/polyfills/_events.d.ts"; +import { channel } from "internal:deno_node/polyfills/diagnostics_channel.ts"; + +let debug = debuglog("net", (fn) => { + debug = fn; +}); + +const kLastWriteQueueSize = Symbol("lastWriteQueueSize"); +const kSetNoDelay = Symbol("kSetNoDelay"); +const kBytesRead = Symbol("kBytesRead"); +const kBytesWritten = Symbol("kBytesWritten"); + +const DEFAULT_IPV4_ADDR = "0.0.0.0"; +const DEFAULT_IPV6_ADDR = "::"; + +type Handle = TCP | Pipe; + +interface HandleOptions { + pauseOnCreate?: boolean; + manualStart?: boolean; + handle?: Handle; +} + +interface OnReadOptions { + buffer: Uint8Array | (() => Uint8Array); + /** + * This function is called for every chunk of incoming data. + * + * Two arguments are passed to it: the number of bytes written to buffer and + * a reference to buffer. + * + * Return `false` from this function to implicitly `pause()` the socket. + */ + callback(bytesWritten: number, buf: Uint8Array): boolean; +} + +interface ConnectOptions { + /** + * If specified, incoming data is stored in a single buffer and passed to the + * supplied callback when data arrives on the socket. + * + * Note: this will cause the streaming functionality to not provide any data, + * however events like `"error"`, `"end"`, and `"close"` will still be + * emitted as normal and methods like `pause()` and `resume()` will also + * behave as expected. + */ + onread?: OnReadOptions; +} + +interface SocketOptions extends ConnectOptions, HandleOptions, DuplexOptions { + /** + * If specified, wrap around an existing socket with the given file + * descriptor, otherwise a new socket will be created. + */ + fd?: number; + /** + * If set to `false`, then the socket will automatically end the writable + * side when the readable side ends. See `net.createServer()` and the `"end"` + * event for details. Default: `false`. + */ + allowHalfOpen?: boolean; + /** + * Allow reads on the socket when an fd is passed, otherwise ignored. + * Default: `false`. + */ + readable?: boolean; + /** + * Allow writes on the socket when an fd is passed, otherwise ignored. + * Default: `false`. + */ + writable?: boolean; + /** An Abort signal that may be used to destroy the socket. */ + signal?: AbortSignal; +} + +interface TcpNetConnectOptions extends TcpSocketConnectOptions, SocketOptions { + timeout?: number; +} + +interface IpcNetConnectOptions extends IpcSocketConnectOptions, SocketOptions { + timeout?: number; +} + +type NetConnectOptions = TcpNetConnectOptions | IpcNetConnectOptions; + +interface AddressInfo { + address: string; + family?: string; + port: number; +} + +type LookupFunction = ( + hostname: string, + options: LookupOneOptions, + callback: ( + err: ErrnoException | null, + address: string, + family: number, + ) => void, +) => void; + +interface TcpSocketConnectOptions extends ConnectOptions { + port: number; + host?: string; + localAddress?: string; + localPort?: number; + hints?: number; + family?: number; + lookup?: LookupFunction; +} + +interface IpcSocketConnectOptions extends ConnectOptions { + path: string; +} + +type SocketConnectOptions = TcpSocketConnectOptions | IpcSocketConnectOptions; + +function _getNewAsyncId(handle?: Handle): number { + return !handle || typeof handle.getAsyncId !== "function" + ? newAsyncId() + : handle.getAsyncId(); +} + +interface NormalizedArgs { + 0: Partial; + 1: ConnectionListener | null; + [normalizedArgsSymbol]?: boolean; +} + +const _noop = (_arrayBuffer: Uint8Array, _nread: number): undefined => { + return; +}; + +const netClientSocketChannel = channel("net.client.socket"); +const netServerSocketChannel = channel("net.server.socket"); + +function _toNumber(x: unknown): number | false { + return (x = Number(x)) >= 0 ? (x as number) : false; +} + +function _isPipeName(s: unknown): s is string { + return typeof s === "string" && _toNumber(s) === false; +} + +function _createHandle(fd: number, isServer: boolean): Handle { + validateInt32(fd, "fd", 0); + + const type = guessHandleType(fd); + + if (type === "PIPE") { + return new Pipe(isServer ? PipeConstants.SERVER : PipeConstants.SOCKET); + } + + if (type === "TCP") { + return new TCP(isServer ? TCPConstants.SERVER : TCPConstants.SOCKET); + } + + throw new ERR_INVALID_FD_TYPE(type); +} + +// Returns an array [options, cb], where options is an object, +// cb is either a function or null. +// Used to normalize arguments of `Socket.prototype.connect()` and +// `Server.prototype.listen()`. Possible combinations of parameters: +// - (options[...][, cb]) +// - (path[...][, cb]) +// - ([port][, host][...][, cb]) +// For `Socket.prototype.connect()`, the [...] part is ignored +// For `Server.prototype.listen()`, the [...] part is [, backlog] +// but will not be handled here (handled in listen()) +export function _normalizeArgs(args: unknown[]): NormalizedArgs { + let arr: NormalizedArgs; + + if (args.length === 0) { + arr = [{}, null]; + arr[normalizedArgsSymbol] = true; + + return arr; + } + + const arg0 = args[0] as Partial | number | string; + let options: Partial = {}; + + if (typeof arg0 === "object" && arg0 !== null) { + // (options[...][, cb]) + options = arg0; + } else if (_isPipeName(arg0)) { + // (path[...][, cb]) + (options as IpcSocketConnectOptions).path = arg0; + } else { + // ([port][, host][...][, cb]) + (options as TcpSocketConnectOptions).port = arg0; + + if (args.length > 1 && typeof args[1] === "string") { + (options as TcpSocketConnectOptions).host = args[1]; + } + } + + const cb = args[args.length - 1]; + + if (!_isConnectionListener(cb)) { + arr = [options, null]; + } else { + arr = [options, cb]; + } + + arr[normalizedArgsSymbol] = true; + + return arr; +} + +function _isTCPConnectWrap( + req: TCPConnectWrap | PipeConnectWrap, +): req is TCPConnectWrap { + return "localAddress" in req && "localPort" in req; +} + +function _afterConnect( + status: number, + // deno-lint-ignore no-explicit-any + handle: any, + req: PipeConnectWrap | TCPConnectWrap, + readable: boolean, + writable: boolean, +) { + let socket = handle[ownerSymbol]; + + if (socket.constructor.name === "ReusedHandle") { + socket = socket.handle; + } + + // Callback may come after call to destroy + if (socket.destroyed) { + return; + } + + debug("afterConnect"); + + assert(socket.connecting); + + socket.connecting = false; + socket._sockname = null; + + if (status === 0) { + if (socket.readable && !readable) { + socket.push(null); + socket.read(); + } + + if (socket.writable && !writable) { + socket.end(); + } + + socket._unrefTimer(); + + socket.emit("connect"); + socket.emit("ready"); + + // Start the first read, or get an immediate EOF. + // this doesn't actually consume any bytes, because len=0. + if (readable && !socket.isPaused()) { + socket.read(0); + } + } else { + socket.connecting = false; + let details; + + if (_isTCPConnectWrap(req)) { + details = req.localAddress + ":" + req.localPort; + } + + const ex = exceptionWithHostPort( + status, + "connect", + req.address, + (req as TCPConnectWrap).port, + details, + ); + + if (_isTCPConnectWrap(req)) { + ex.localAddress = req.localAddress; + ex.localPort = req.localPort; + } + + socket.destroy(ex); + } +} + +function _checkBindError(err: number, port: number, handle: TCP) { + // EADDRINUSE may not be reported until we call `listen()` or `connect()`. + // To complicate matters, a failed `bind()` followed by `listen()` or `connect()` + // will implicitly bind to a random port. Ergo, check that the socket is + // bound to the expected port before calling `listen()` or `connect()`. + if (err === 0 && port > 0 && handle.getsockname) { + const out: AddressInfo | Record = {}; + err = handle.getsockname(out); + + if (err === 0 && port !== out.port) { + err = codeMap.get("EADDRINUSE")!; + } + } + + return err; +} + +function _isPipe( + options: Partial, +): options is IpcSocketConnectOptions { + return "path" in options && !!options.path; +} + +function _connectErrorNT(socket: Socket, err: Error) { + socket.destroy(err); +} + +function _internalConnect( + socket: Socket, + address: string, + port: number, + addressType: number, + localAddress: string, + localPort: number, + flags: number, +) { + assert(socket.connecting); + + let err; + + if (localAddress || localPort) { + if (addressType === 4) { + localAddress = localAddress || DEFAULT_IPV4_ADDR; + err = (socket._handle as TCP).bind(localAddress, localPort); + } else { + // addressType === 6 + localAddress = localAddress || DEFAULT_IPV6_ADDR; + err = (socket._handle as TCP).bind6(localAddress, localPort, flags); + } + + debug( + "binding to localAddress: %s and localPort: %d (addressType: %d)", + localAddress, + localPort, + addressType, + ); + + err = _checkBindError(err, localPort, socket._handle as TCP); + + if (err) { + const ex = exceptionWithHostPort(err, "bind", localAddress, localPort); + socket.destroy(ex); + + return; + } + } + + if (addressType === 6 || addressType === 4) { + const req = new TCPConnectWrap(); + req.oncomplete = _afterConnect; + req.address = address; + req.port = port; + req.localAddress = localAddress; + req.localPort = localPort; + + if (addressType === 4) { + err = (socket._handle as TCP).connect(req, address, port); + } else { + err = (socket._handle as TCP).connect6(req, address, port); + } + } else { + const req = new PipeConnectWrap(); + req.oncomplete = _afterConnect; + req.address = address; + + err = (socket._handle as Pipe).connect(req, address); + } + + if (err) { + let details = ""; + + const sockname = socket._getsockname(); + + if (sockname) { + details = `${sockname.address}:${sockname.port}`; + } + + const ex = exceptionWithHostPort(err, "connect", address, port, details); + socket.destroy(ex); + } +} + +// Provide a better error message when we call end() as a result +// of the other side sending a FIN. The standard "write after end" +// is overly vague, and makes it seem like the user's code is to blame. +function _writeAfterFIN( + this: Socket, + // deno-lint-ignore no-explicit-any + chunk: any, + encoding?: + | BufferEncoding + | null + | ((error: Error | null | undefined) => void), + cb?: (error: Error | null | undefined) => void, +): boolean { + if (!this.writableEnded) { + return Duplex.prototype.write.call( + this, + chunk, + encoding as BufferEncoding | null, + // @ts-expect-error Using `call` seem to be interfering with the overload for write + cb, + ); + } + + if (typeof encoding === "function") { + cb = encoding; + encoding = null; + } + + const err = genericNodeError( + "This socket has been ended by the other party", + { code: "EPIPE" }, + ); + + if (typeof cb === "function") { + defaultTriggerAsyncIdScope(this[asyncIdSymbol], nextTick, cb, err); + } + + if (this._server) { + nextTick(() => this.destroy(err)); + } else { + this.destroy(err); + } + + return false; +} + +function _tryReadStart(socket: Socket) { + // Not already reading, start the flow. + debug("Socket._handle.readStart"); + socket._handle!.reading = true; + const err = socket._handle!.readStart(); + + if (err) { + socket.destroy(errnoException(err, "read")); + } +} + +// Called when the "end" event is emitted. +function _onReadableStreamEnd(this: Socket) { + if (!this.allowHalfOpen) { + this.write = _writeAfterFIN; + } +} + +// Called when creating new Socket, or when re-using a closed Socket +function _initSocketHandle(socket: Socket) { + socket._undestroy(); + socket._sockname = undefined; + + // Handle creation may be deferred to bind() or connect() time. + if (socket._handle) { + // deno-lint-ignore no-explicit-any + (socket._handle as any)[ownerSymbol] = socket; + socket._handle.onread = onStreamRead; + socket[asyncIdSymbol] = _getNewAsyncId(socket._handle); + + let userBuf = socket[kBuffer]; + + if (userBuf) { + const bufGen = socket[kBufferGen]; + + if (bufGen !== null) { + userBuf = bufGen(); + + if (!isUint8Array(userBuf)) { + return; + } + + socket[kBuffer] = userBuf; + } + + socket._handle.useUserBuffer(userBuf); + } + } +} + +function _lookupAndConnect( + self: Socket, + options: TcpSocketConnectOptions, +) { + const { localAddress, localPort } = options; + const host = options.host || "localhost"; + let { port } = options; + + if (localAddress && !isIP(localAddress)) { + throw new ERR_INVALID_IP_ADDRESS(localAddress); + } + + if (localPort) { + validateNumber(localPort, "options.localPort"); + } + + if (typeof port !== "undefined") { + if (typeof port !== "number" && typeof port !== "string") { + throw new ERR_INVALID_ARG_TYPE( + "options.port", + ["number", "string"], + port, + ); + } + + validatePort(port); + } + + port |= 0; + + // If host is an IP, skip performing a lookup + const addressType = isIP(host); + if (addressType) { + defaultTriggerAsyncIdScope(self[asyncIdSymbol], nextTick, () => { + if (self.connecting) { + defaultTriggerAsyncIdScope( + self[asyncIdSymbol], + _internalConnect, + self, + host, + port, + addressType, + localAddress, + localPort, + ); + } + }); + + return; + } + + if (options.lookup !== undefined) { + validateFunction(options.lookup, "options.lookup"); + } + + const dnsOpts = { + family: options.family, + hints: options.hints || 0, + }; + + if ( + !isWindows && + dnsOpts.family !== 4 && + dnsOpts.family !== 6 && + dnsOpts.hints === 0 + ) { + dnsOpts.hints = ADDRCONFIG; + } + + debug("connect: find host", host); + debug("connect: dns options", dnsOpts); + self._host = host; + const lookup = options.lookup || dnsLookup; + + defaultTriggerAsyncIdScope(self[asyncIdSymbol], function () { + lookup( + host, + dnsOpts, + function emitLookup( + err: ErrnoException | null, + ip: string, + addressType: number, + ) { + self.emit("lookup", err, ip, addressType, host); + + // It's possible we were destroyed while looking this up. + // XXX it would be great if we could cancel the promise returned by + // the look up. + if (!self.connecting) { + return; + } + + if (err) { + // net.createConnection() creates a net.Socket object and immediately + // calls net.Socket.connect() on it (that's us). There are no event + // listeners registered yet so defer the error event to the next tick. + nextTick(_connectErrorNT, self, err); + } else if (!isIP(ip)) { + err = new ERR_INVALID_IP_ADDRESS(ip); + + nextTick(_connectErrorNT, self, err); + } else if (addressType !== 4 && addressType !== 6) { + err = new ERR_INVALID_ADDRESS_FAMILY( + `${addressType}`, + options.host!, + options.port, + ); + + nextTick(_connectErrorNT, self, err); + } else { + self._unrefTimer(); + + defaultTriggerAsyncIdScope( + self[asyncIdSymbol], + _internalConnect, + self, + ip, + port, + addressType, + localAddress, + localPort, + ); + } + }, + ); + }); +} + +function _afterShutdown(this: ShutdownWrap) { + // deno-lint-ignore no-explicit-any + const self: any = this.handle[ownerSymbol]; + + debug("afterShutdown destroyed=%j", self.destroyed, self._readableState); + + this.callback(); +} + +function _emitCloseNT(s: Socket | Server) { + debug("SERVER: emit close"); + s.emit("close"); +} + +/** + * This class is an abstraction of a TCP socket or a streaming `IPC` endpoint + * (uses named pipes on Windows, and Unix domain sockets otherwise). It is also + * an `EventEmitter`. + * + * A `net.Socket` can be created by the user and used directly to interact with + * a server. For example, it is returned by `createConnection`, + * so the user can use it to talk to the server. + * + * It can also be created by Node.js and passed to the user when a connection + * is received. For example, it is passed to the listeners of a `"connection"` event emitted on a `Server`, so the user can use + * it to interact with the client. + */ +export class Socket extends Duplex { + // Problem with this is that users can supply their own handle, that may not + // have `handle.getAsyncId()`. In this case an `[asyncIdSymbol]` should + // probably be supplied by `async_hooks`. + [asyncIdSymbol] = -1; + + [kHandle]: Handle | null = null; + [kSetNoDelay] = false; + [kLastWriteQueueSize] = 0; + // deno-lint-ignore no-explicit-any + [kTimeout]: any = null; + [kBuffer]: Uint8Array | boolean | null = null; + [kBufferCb]: OnReadOptions["callback"] | null = null; + [kBufferGen]: (() => Uint8Array) | null = null; + + // Used after `.destroy()` + [kBytesRead] = 0; + [kBytesWritten] = 0; + + // Reserved properties + server = null; + // deno-lint-ignore no-explicit-any + _server: any = null; + + _peername?: AddressInfo | Record; + _sockname?: AddressInfo | Record; + _pendingData: Uint8Array | string | null = null; + _pendingEncoding = ""; + _host: string | null = null; + // deno-lint-ignore no-explicit-any + _parent: any = null; + + constructor(options: SocketOptions | number) { + if (typeof options === "number") { + // Legacy interface. + options = { fd: options }; + } else { + options = { ...options }; + } + + // Default to *not* allowing half open sockets. + options.allowHalfOpen = Boolean(options.allowHalfOpen); + // For backwards compat do not emit close on destroy. + options.emitClose = false; + options.autoDestroy = true; + // Handle strings directly. + options.decodeStrings = false; + + super(options); + + if (options.handle) { + this._handle = options.handle; + this[asyncIdSymbol] = _getNewAsyncId(this._handle); + } else if (options.fd !== undefined) { + // REF: https://github.com/denoland/deno/issues/6529 + notImplemented("net.Socket.prototype.constructor with fd option"); + } + + const onread = options.onread; + + if ( + onread !== null && + typeof onread === "object" && + (isUint8Array(onread.buffer) || typeof onread.buffer === "function") && + typeof onread.callback === "function" + ) { + if (typeof onread.buffer === "function") { + this[kBuffer] = true; + this[kBufferGen] = onread.buffer; + } else { + this[kBuffer] = onread.buffer; + } + + this[kBufferCb] = onread.callback; + } + + this.on("end", _onReadableStreamEnd); + + _initSocketHandle(this); + + // If we have a handle, then start the flow of data into the + // buffer. If not, then this will happen when we connect. + if (this._handle && options.readable !== false) { + if (options.pauseOnCreate) { + // Stop the handle from reading and pause the stream + this._handle.reading = false; + this._handle.readStop(); + // @ts-expect-error This property shouldn't be modified + this.readableFlowing = false; + } else if (!options.manualStart) { + this.read(0); + } + } + } + + /** + * Initiate a connection on a given socket. + * + * Possible signatures: + * + * - `socket.connect(options[, connectListener])` + * - `socket.connect(path[, connectListener])` for `IPC` connections. + * - `socket.connect(port[, host][, connectListener])` for TCP connections. + * - Returns: `net.Socket` The socket itself. + * + * This function is asynchronous. When the connection is established, the `"connect"` event will be emitted. If there is a problem connecting, + * instead of a `"connect"` event, an `"error"` event will be emitted with + * the error passed to the `"error"` listener. + * The last parameter `connectListener`, if supplied, will be added as a listener + * for the `"connect"` event **once**. + * + * This function should only be used for reconnecting a socket after `"close"` has been emitted or otherwise it may lead to undefined + * behavior. + */ + connect( + options: SocketConnectOptions | NormalizedArgs, + connectionListener?: ConnectionListener, + ): this; + connect( + port: number, + host: string, + connectionListener?: ConnectionListener, + ): this; + connect(port: number, connectionListener?: ConnectionListener): this; + connect(path: string, connectionListener?: ConnectionListener): this; + connect(...args: unknown[]): this { + let normalized: NormalizedArgs; + + // If passed an array, it's treated as an array of arguments that have + // already been normalized (so we don't normalize more than once). This has + // been solved before in https://github.com/nodejs/node/pull/12342, but was + // reverted as it had unintended side effects. + if ( + Array.isArray(args[0]) && + (args[0] as unknown as NormalizedArgs)[normalizedArgsSymbol] + ) { + normalized = args[0] as unknown as NormalizedArgs; + } else { + normalized = _normalizeArgs(args); + } + + const options = normalized[0]; + const cb = normalized[1]; + + // `options.port === null` will be checked later. + if ( + (options as TcpSocketConnectOptions).port === undefined && + (options as IpcSocketConnectOptions).path == null + ) { + throw new ERR_MISSING_ARGS(["options", "port", "path"]); + } + + if (this.write !== Socket.prototype.write) { + this.write = Socket.prototype.write; + } + + if (this.destroyed) { + this._handle = null; + this._peername = undefined; + this._sockname = undefined; + } + + const { path } = options as IpcNetConnectOptions; + const pipe = _isPipe(options); + debug("pipe", pipe, path); + + if (!this._handle) { + this._handle = pipe + ? new Pipe(PipeConstants.SOCKET) + : new TCP(TCPConstants.SOCKET); + + _initSocketHandle(this); + } + + if (cb !== null) { + this.once("connect", cb); + } + + this._unrefTimer(); + + this.connecting = true; + + if (pipe) { + validateString(path, "options.path"); + defaultTriggerAsyncIdScope( + this[asyncIdSymbol], + _internalConnect, + this, + path, + ); + } else { + _lookupAndConnect(this, options as TcpSocketConnectOptions); + } + + return this; + } + + /** + * Pauses the reading of data. That is, `"data"` events will not be emitted. + * Useful to throttle back an upload. + * + * @return The socket itself. + */ + override pause(): this { + if ( + this[kBuffer] && + !this.connecting && + this._handle && + this._handle.reading + ) { + this._handle.reading = false; + + if (!this.destroyed) { + const err = this._handle.readStop(); + + if (err) { + this.destroy(errnoException(err, "read")); + } + } + } + + return Duplex.prototype.pause.call(this) as unknown as this; + } + + /** + * Resumes reading after a call to `socket.pause()`. + * + * @return The socket itself. + */ + override resume(): this { + if ( + this[kBuffer] && + !this.connecting && + this._handle && + !this._handle.reading + ) { + _tryReadStart(this); + } + + return Duplex.prototype.resume.call(this) as this; + } + + /** + * Sets the socket to timeout after `timeout` milliseconds of inactivity on + * the socket. By default `net.Socket` do not have a timeout. + * + * When an idle timeout is triggered the socket will receive a `"timeout"` event but the connection will not be severed. The user must manually call `socket.end()` or `socket.destroy()` to + * end the connection. + * + * If `timeout` is `0`, then the existing idle timeout is disabled. + * + * The optional `callback` parameter will be added as a one-time listener for the `"timeout"` event. + * @return The socket itself. + */ + setTimeout = setStreamTimeout; + + /** + * Enable/disable the use of Nagle's algorithm. + * + * When a TCP connection is created, it will have Nagle's algorithm enabled. + * + * Nagle's algorithm delays data before it is sent via the network. It attempts + * to optimize throughput at the expense of latency. + * + * Passing `true` for `noDelay` or not passing an argument will disable Nagle's + * algorithm for the socket. Passing `false` for `noDelay` will enable Nagle's + * algorithm. + * + * @param noDelay + * @return The socket itself. + */ + setNoDelay(noDelay?: boolean): this { + if (!this._handle) { + this.once( + "connect", + noDelay ? this.setNoDelay : () => this.setNoDelay(noDelay), + ); + + return this; + } + + // Backwards compatibility: assume true when `noDelay` is omitted + const newValue = noDelay === undefined ? true : !!noDelay; + + if ( + "setNoDelay" in this._handle && + this._handle.setNoDelay && + newValue !== this[kSetNoDelay] + ) { + this[kSetNoDelay] = newValue; + this._handle.setNoDelay(newValue); + } + + return this; + } + + /** + * Enable/disable keep-alive functionality, and optionally set the initial + * delay before the first keepalive probe is sent on an idle socket. + * + * Set `initialDelay` (in milliseconds) to set the delay between the last + * data packet received and the first keepalive probe. Setting `0` for`initialDelay` will leave the value unchanged from the default + * (or previous) setting. + * + * Enabling the keep-alive functionality will set the following socket options: + * + * - `SO_KEEPALIVE=1` + * - `TCP_KEEPIDLE=initialDelay` + * - `TCP_KEEPCNT=10` + * - `TCP_KEEPINTVL=1` + * + * @param enable + * @param initialDelay + * @return The socket itself. + */ + setKeepAlive(enable: boolean, initialDelay?: number): this { + if (!this._handle) { + this.once("connect", () => this.setKeepAlive(enable, initialDelay)); + + return this; + } + + if ("setKeepAlive" in this._handle) { + this._handle.setKeepAlive(enable, ~~(initialDelay! / 1000)); + } + + return this; + } + + /** + * Returns the bound `address`, the address `family` name and `port` of the + * socket as reported by the operating system:`{ port: 12346, family: "IPv4", address: "127.0.0.1" }` + */ + address(): AddressInfo | Record { + return this._getsockname(); + } + + /** + * Calling `unref()` on a socket will allow the program to exit if this is the only + * active socket in the event system. If the socket is already `unref`ed calling`unref()` again will have no effect. + * + * @return The socket itself. + */ + unref(): this { + if (!this._handle) { + this.once("connect", this.unref); + + return this; + } + + if (typeof this._handle.unref === "function") { + this._handle.unref(); + } + + return this; + } + + /** + * Opposite of `unref()`, calling `ref()` on a previously `unref`ed socket will_not_ let the program exit if it's the only socket left (the default behavior). + * If the socket is `ref`ed calling `ref` again will have no effect. + * + * @return The socket itself. + */ + ref(): this { + if (!this._handle) { + this.once("connect", this.ref); + + return this; + } + + if (typeof this._handle.ref === "function") { + this._handle.ref(); + } + + return this; + } + + /** + * This property shows the number of characters buffered for writing. The buffer + * may contain strings whose length after encoding is not yet known. So this number + * is only an approximation of the number of bytes in the buffer. + * + * `net.Socket` has the property that `socket.write()` always works. This is to + * help users get up and running quickly. The computer cannot always keep up + * with the amount of data that is written to a socket. The network connection + * simply might be too slow. Node.js will internally queue up the data written to a + * socket and send it out over the wire when it is possible. + * + * The consequence of this internal buffering is that memory may grow. + * Users who experience large or growing `bufferSize` should attempt to + * "throttle" the data flows in their program with `socket.pause()` and `socket.resume()`. + * + * @deprecated Use `writableLength` instead. + */ + get bufferSize(): number { + if (this._handle) { + return this.writableLength; + } + + return 0; + } + + /** + * The amount of received bytes. + */ + get bytesRead(): number { + return this._handle ? this._handle.bytesRead : this[kBytesRead]; + } + + /** + * The amount of bytes sent. + */ + get bytesWritten(): number | undefined { + let bytes = this._bytesDispatched; + const data = this._pendingData; + const encoding = this._pendingEncoding; + const writableBuffer = this.writableBuffer; + + if (!writableBuffer) { + return undefined; + } + + for (const el of writableBuffer) { + bytes += el!.chunk instanceof Buffer + ? el!.chunk.length + : Buffer.byteLength(el!.chunk, el!.encoding); + } + + if (Array.isArray(data)) { + // Was a writev, iterate over chunks to get total length + for (let i = 0; i < data.length; i++) { + const chunk = data[i]; + + // deno-lint-ignore no-explicit-any + if ((data as any).allBuffers || chunk instanceof Buffer) { + bytes += chunk.length; + } else { + bytes += Buffer.byteLength(chunk.chunk, chunk.encoding); + } + } + } else if (data) { + // Writes are either a string or a Buffer. + if (typeof data !== "string") { + bytes += (data as Buffer).length; + } else { + bytes += Buffer.byteLength(data, encoding); + } + } + + return bytes; + } + + /** + * If `true`,`socket.connect(options[, connectListener])` was + * called and has not yet finished. It will stay `true` until the socket becomes + * connected, then it is set to `false` and the `"connect"` event is emitted. Note + * that the `socket.connect(options[, connectListener])` callback is a listener for the `"connect"` event. + */ + connecting = false; + + /** + * The string representation of the local IP address the remote client is + * connecting on. For example, in a server listening on `"0.0.0.0"`, if a client + * connects on `"192.168.1.1"`, the value of `socket.localAddress` would be`"192.168.1.1"`. + */ + get localAddress(): string { + return this._getsockname().address; + } + + /** + * The numeric representation of the local port. For example, `80` or `21`. + */ + get localPort(): number { + return this._getsockname().port; + } + + /** + * The string representation of the local IP family. `"IPv4"` or `"IPv6"`. + */ + get localFamily(): string | undefined { + return this._getsockname().family; + } + + /** + * The string representation of the remote IP address. For example,`"74.125.127.100"` or `"2001:4860:a005::68"`. Value may be `undefined` if + * the socket is destroyed (for example, if the client disconnected). + */ + get remoteAddress(): string | undefined { + return this._getpeername().address; + } + + /** + * The string representation of the remote IP family. `"IPv4"` or `"IPv6"`. + */ + get remoteFamily(): string | undefined { + const { family } = this._getpeername(); + + return family ? `IPv${family}` : family; + } + + /** + * The numeric representation of the remote port. For example, `80` or `21`. + */ + get remotePort(): number | undefined { + return this._getpeername().port; + } + + get pending(): boolean { + return !this._handle || this.connecting; + } + + get readyState(): string { + if (this.connecting) { + return "opening"; + } else if (this.readable && this.writable) { + return "open"; + } else if (this.readable && !this.writable) { + return "readOnly"; + } else if (!this.readable && this.writable) { + return "writeOnly"; + } + return "closed"; + } + + /** + * Half-closes the socket. i.e., it sends a FIN packet. It is possible the + * server will still send some data. + * + * See `writable.end()` for further details. + * + * @param encoding Only used when data is `string`. + * @param cb Optional callback for when the socket is finished. + * @return The socket itself. + */ + override end(cb?: () => void): this; + override end(buffer: Uint8Array | string, cb?: () => void): this; + override end( + data: Uint8Array | string, + encoding?: Encodings, + cb?: () => void, + ): this; + override end( + data?: Uint8Array | string | (() => void), + encoding?: Encodings | (() => void), + cb?: () => void, + ): this { + Duplex.prototype.end.call(this, data, encoding as Encodings, cb); + DTRACE_NET_STREAM_END(this); + + return this; + } + + /** + * @param size Optional argument to specify how much data to read. + */ + override read( + size?: number, + ): string | Uint8Array | Buffer | null | undefined { + if ( + this[kBuffer] && + !this.connecting && + this._handle && + !this._handle.reading + ) { + _tryReadStart(this); + } + + return Duplex.prototype.read.call(this, size); + } + + destroySoon() { + if (this.writable) { + this.end(); + } + + if (this.writableFinished) { + this.destroy(); + } else { + this.once("finish", this.destroy); + } + } + + _unrefTimer() { + // deno-lint-ignore no-this-alias + for (let s = this; s !== null; s = s._parent) { + if (s[kTimeout]) { + s[kTimeout].refresh(); + } + } + } + + // The user has called .end(), and all the bytes have been + // sent out to the other side. + // deno-lint-ignore no-explicit-any + override _final(cb: any): any { + // If still connecting - defer handling `_final` until 'connect' will happen + if (this.pending) { + debug("_final: not yet connected"); + return this.once("connect", () => this._final(cb)); + } + + if (!this._handle) { + return cb(); + } + + debug("_final: not ended, call shutdown()"); + + const req = new ShutdownWrap(); + req.oncomplete = _afterShutdown; + req.handle = this._handle; + req.callback = cb; + const err = this._handle.shutdown(req); + + if (err === 1 || err === codeMap.get("ENOTCONN")) { + // synchronous finish + return cb(); + } else if (err !== 0) { + return cb(errnoException(err, "shutdown")); + } + } + + _onTimeout() { + const handle = this._handle; + const lastWriteQueueSize = this[kLastWriteQueueSize]; + + if (lastWriteQueueSize > 0 && handle) { + // `lastWriteQueueSize !== writeQueueSize` means there is + // an active write in progress, so we suppress the timeout. + const { writeQueueSize } = handle; + + if (lastWriteQueueSize !== writeQueueSize) { + this[kLastWriteQueueSize] = writeQueueSize; + this._unrefTimer(); + + return; + } + } + + debug("_onTimeout"); + this.emit("timeout"); + } + + override _read(size?: number) { + debug("_read"); + if (this.connecting || !this._handle) { + debug("_read wait for connection"); + this.once("connect", () => this._read(size)); + } else if (!this._handle.reading) { + _tryReadStart(this); + } + } + + override _destroy(exception: Error | null, cb: (err: Error | null) => void) { + debug("destroy"); + this.connecting = false; + + // deno-lint-ignore no-this-alias + for (let s = this; s !== null; s = s._parent) { + clearTimeout(s[kTimeout]); + } + + debug("close"); + if (this._handle) { + debug("close handle"); + const isException = exception ? true : false; + // `bytesRead` and `kBytesWritten` should be accessible after `.destroy()` + this[kBytesRead] = this._handle.bytesRead; + this[kBytesWritten] = this._handle.bytesWritten; + + this._handle.close(() => { + this._handle!.onread = _noop; + this._handle = null; + this._sockname = undefined; + + debug("emit close"); + this.emit("close", isException); + }); + cb(exception); + } else { + cb(exception); + nextTick(_emitCloseNT, this); + } + + if (this._server) { + debug("has server"); + this._server._connections--; + + if (this._server._emitCloseIfDrained) { + this._server._emitCloseIfDrained(); + } + } + } + + _getpeername(): AddressInfo | Record { + if (!this._handle || !("getpeername" in this._handle) || this.connecting) { + return this._peername || {}; + } else if (!this._peername) { + this._peername = {}; + this._handle.getpeername(this._peername); + } + + return this._peername; + } + + _getsockname(): AddressInfo | Record { + if (!this._handle || !("getsockname" in this._handle)) { + return {}; + } else if (!this._sockname) { + this._sockname = {}; + this._handle.getsockname(this._sockname); + } + + return this._sockname; + } + + _writeGeneric( + writev: boolean, + // deno-lint-ignore no-explicit-any + data: any, + encoding: string, + cb: (error?: Error | null) => void, + ) { + // If we are still connecting, then buffer this for later. + // The Writable logic will buffer up any more writes while + // waiting for this one to be done. + if (this.connecting) { + this._pendingData = data; + this._pendingEncoding = encoding; + this.once("connect", function connect(this: Socket) { + this._writeGeneric(writev, data, encoding, cb); + }); + + return; + } + + this._pendingData = null; + this._pendingEncoding = ""; + + if (!this._handle) { + cb(new ERR_SOCKET_CLOSED()); + + return false; + } + + this._unrefTimer(); + + let req; + + if (writev) { + req = writevGeneric(this, data, cb); + } else { + req = writeGeneric(this, data, encoding, cb); + } + if (req.async) { + this[kLastWriteQueueSize] = req.bytes; + } + } + + // @ts-ignore Duplex defining as a property when want a method. + _writev( + // deno-lint-ignore no-explicit-any + chunks: Array<{ chunk: any; encoding: string }>, + cb: (error?: Error | null) => void, + ) { + this._writeGeneric(true, chunks, "", cb); + } + + override _write( + // deno-lint-ignore no-explicit-any + data: any, + encoding: string, + cb: (error?: Error | null) => void, + ) { + this._writeGeneric(false, data, encoding, cb); + } + + [kAfterAsyncWrite]() { + this[kLastWriteQueueSize] = 0; + } + + get [kUpdateTimer]() { + return this._unrefTimer; + } + + get _connecting(): boolean { + return this.connecting; + } + + // Legacy alias. Having this is probably being overly cautious, but it doesn't + // really hurt anyone either. This can probably be removed safely if desired. + get _bytesDispatched(): number { + return this._handle ? this._handle.bytesWritten : this[kBytesWritten]; + } + + get _handle(): Handle | null { + return this[kHandle]; + } + + set _handle(v: Handle | null) { + this[kHandle] = v; + } +} + +export const Stream = Socket; + +// Target API: +// +// let s = net.connect({port: 80, host: 'google.com'}, function() { +// ... +// }); +// +// There are various forms: +// +// connect(options, [cb]) +// connect(port, [host], [cb]) +// connect(path, [cb]); +// +export function connect( + options: NetConnectOptions, + connectionListener?: () => void, +): Socket; +export function connect( + port: number, + host?: string, + connectionListener?: () => void, +): Socket; +export function connect(path: string, connectionListener?: () => void): Socket; +export function connect(...args: unknown[]) { + const normalized = _normalizeArgs(args); + const options = normalized[0] as Partial; + debug("createConnection", normalized); + const socket = new Socket(options); + + if (netClientSocketChannel.hasSubscribers) { + netClientSocketChannel.publish({ + socket, + }); + } + + if (options.timeout) { + socket.setTimeout(options.timeout); + } + + return socket.connect(normalized); +} + +export const createConnection = connect; + +export interface ListenOptions extends Abortable { + fd?: number; + port?: number | undefined; + host?: string | undefined; + backlog?: number | undefined; + path?: string | undefined; + exclusive?: boolean | undefined; + readableAll?: boolean | undefined; + writableAll?: boolean | undefined; + /** + * Default: `false` + */ + ipv6Only?: boolean | undefined; +} + +type ConnectionListener = (socket: Socket) => void; + +interface ServerOptions { + /** + * Indicates whether half-opened TCP connections are allowed. + * Default: false + */ + allowHalfOpen?: boolean | undefined; + /** + * Indicates whether the socket should be paused on incoming connections. + * Default: false + */ + pauseOnConnect?: boolean | undefined; +} + +function _isServerSocketOptions( + options: unknown, +): options is null | undefined | ServerOptions { + return ( + options === null || + typeof options === "undefined" || + typeof options === "object" + ); +} + +function _isConnectionListener( + connectionListener: unknown, +): connectionListener is ConnectionListener { + return typeof connectionListener === "function"; +} + +function _getFlags(ipv6Only?: boolean): number { + return ipv6Only === true ? TCPConstants.UV_TCP_IPV6ONLY : 0; +} + +function _listenInCluster( + server: Server, + address: string | null, + port: number | null, + addressType: number | null, + backlog: number, + fd?: number | null, + exclusive?: boolean, + flags?: number, +) { + exclusive = !!exclusive; + + // TODO(cmorten): here we deviate somewhat from the Node implementation which + // makes use of the https://nodejs.org/api/cluster.html module to run servers + // across a "cluster" of Node processes to take advantage of multi-core + // systems. + // + // Though Deno has has a Worker capability from which we could simulate this, + // for now we assert that we are _always_ on the primary process. + const isPrimary = true; + + if (isPrimary || exclusive) { + // Will create a new handle + // _listen2 sets up the listened handle, it is still named like this + // to avoid breaking code that wraps this method + server._listen2(address, port, addressType, backlog, fd, flags); + + return; + } +} + +function _lookupAndListen( + server: Server, + port: number, + address: string, + backlog: number, + exclusive: boolean, + flags: number, +) { + dnsLookup(address, function doListen(err, ip, addressType) { + if (err) { + server.emit("error", err); + } else { + addressType = ip ? addressType : 4; + + _listenInCluster( + server, + ip, + port, + addressType, + backlog, + null, + exclusive, + flags, + ); + } + }); +} + +function _addAbortSignalOption(server: Server, options: ListenOptions) { + if (options?.signal === undefined) { + return; + } + + validateAbortSignal(options.signal, "options.signal"); + + const { signal } = options; + + const onAborted = () => { + server.close(); + }; + + if (signal.aborted) { + nextTick(onAborted); + } else { + signal.addEventListener("abort", onAborted); + server.once("close", () => signal.removeEventListener("abort", onAborted)); + } +} + +// Returns handle if it can be created, or error code if it can't +export function _createServerHandle( + address: string | null, + port: number | null, + addressType: number | null, + fd?: number | null, + flags?: number, +): Handle | number { + let err = 0; + // Assign handle in listen, and clean up if bind or listen fails + let handle; + let isTCP = false; + + if (typeof fd === "number" && fd >= 0) { + try { + handle = _createHandle(fd, true); + } catch (e) { + // Not a fd we can listen on. This will trigger an error. + debug("listen invalid fd=%d:", fd, (e as Error).message); + + return codeMap.get("EINVAL")!; + } + + err = handle.open(fd); + + if (err) { + return err; + } + + assert(!address && !port); + } else if (port === -1 && addressType === -1) { + handle = new Pipe(PipeConstants.SERVER); + + if (isWindows) { + const instances = Number.parseInt( + Deno.env.get("NODE_PENDING_PIPE_INSTANCES") ?? "", + ); + + if (!Number.isNaN(instances)) { + handle.setPendingInstances!(instances); + } + } + } else { + handle = new TCP(TCPConstants.SERVER); + isTCP = true; + } + + if (address || port || isTCP) { + debug("bind to", address || "any"); + + if (!address) { + // TODO(@bartlomieju): differs from Node which tries to bind to IPv6 first when no + // address is provided. + // + // Forcing IPv4 as a workaround for Deno not aligning with Node on + // implicit binding on Windows. + // + // REF: https://github.com/denoland/deno/issues/10762 + + // Try binding to ipv6 first + // err = (handle as TCP).bind6(DEFAULT_IPV6_ADDR, port ?? 0, flags ?? 0); + + // if (err) { + // handle.close(); + + // Fallback to ipv4 + return _createServerHandle(DEFAULT_IPV4_ADDR, port, 4, null, flags); + // } + } else if (addressType === 6) { + err = (handle as TCP).bind6(address, port ?? 0, flags ?? 0); + } else { + err = (handle as TCP).bind(address, port ?? 0); + } + } + + if (err) { + handle.close(); + + return err; + } + + return handle; +} + +function _emitErrorNT(server: Server, err: Error) { + server.emit("error", err); +} + +function _emitListeningNT(server: Server) { + // Ensure handle hasn't closed + if (server._handle) { + server.emit("listening"); + } +} + +// deno-lint-ignore no-explicit-any +function _onconnection(this: any, err: number, clientHandle?: Handle) { + // deno-lint-ignore no-this-alias + const handle = this; + const self = handle[ownerSymbol]; + + debug("onconnection"); + + if (err) { + self.emit("error", errnoException(err, "accept")); + + return; + } + + if (self.maxConnections && self._connections >= self.maxConnections) { + clientHandle!.close(); + + return; + } + + const socket = new Socket({ + handle: clientHandle, + allowHalfOpen: self.allowHalfOpen, + pauseOnCreate: self.pauseOnConnect, + readable: true, + writable: true, + }); + + // TODO(@bartlomieju): implement noDelay and setKeepAlive + + self._connections++; + socket.server = self; + socket._server = self; + + DTRACE_NET_SERVER_CONNECTION(socket); + self.emit("connection", socket); + + if (netServerSocketChannel.hasSubscribers) { + netServerSocketChannel.publish({ + socket, + }); + } +} + +function _setupListenHandle( + this: Server, + address: string | null, + port: number | null, + addressType: number | null, + backlog: number, + fd?: number | null, + flags?: number, +) { + debug("setupListenHandle", address, port, addressType, backlog, fd); + + // If there is not yet a handle, we need to create one and bind. + // In the case of a server sent via IPC, we don't need to do this. + if (this._handle) { + debug("setupListenHandle: have a handle already"); + } else { + debug("setupListenHandle: create a handle"); + + let rval = null; + + // Try to bind to the unspecified IPv6 address, see if IPv6 is available + if (!address && typeof fd !== "number") { + // TODO(@bartlomieju): differs from Node which tries to bind to IPv6 first + // when no address is provided. + // + // Forcing IPv4 as a workaround for Deno not aligning with Node on + // implicit binding on Windows. + // + // REF: https://github.com/denoland/deno/issues/10762 + // rval = _createServerHandle(DEFAULT_IPV6_ADDR, port, 6, fd, flags); + + // if (typeof rval === "number") { + // rval = null; + address = DEFAULT_IPV4_ADDR; + addressType = 4; + // } else { + // address = DEFAULT_IPV6_ADDR; + // addressType = 6; + // } + } + + if (rval === null) { + rval = _createServerHandle(address, port, addressType, fd, flags); + } + + if (typeof rval === "number") { + const error = uvExceptionWithHostPort(rval, "listen", address, port); + nextTick(_emitErrorNT, this, error); + + return; + } + + this._handle = rval; + } + + this[asyncIdSymbol] = _getNewAsyncId(this._handle); + this._handle.onconnection = _onconnection; + this._handle[ownerSymbol] = this; + + // Use a backlog of 512 entries. We pass 511 to the listen() call because + // the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1); + // which will thus give us a backlog of 512 entries. + const err = this._handle.listen(backlog || 511); + + if (err) { + const ex = uvExceptionWithHostPort(err, "listen", address, port); + this._handle.close(); + this._handle = null; + + defaultTriggerAsyncIdScope( + this[asyncIdSymbol], + nextTick, + _emitErrorNT, + this, + ex, + ); + + return; + } + + // Generate connection key, this should be unique to the connection + this._connectionKey = addressType + ":" + address + ":" + port; + + // Unref the handle if the server was unref'ed prior to listening + if (this._unref) { + this.unref(); + } + + defaultTriggerAsyncIdScope( + this[asyncIdSymbol], + nextTick, + _emitListeningNT, + this, + ); +} + +/** This class is used to create a TCP or IPC server. */ +export class Server extends EventEmitter { + [asyncIdSymbol] = -1; + + allowHalfOpen = false; + pauseOnConnect = false; + + // deno-lint-ignore no-explicit-any + _handle: any = null; + _connections = 0; + _usingWorkers = false; + // deno-lint-ignore no-explicit-any + _workers: any[] = []; + _unref = false; + _pipeName?: string; + _connectionKey?: string; + + /** + * `net.Server` is an `EventEmitter` with the following events: + * + * - `"close"` - Emitted when the server closes. If connections exist, this + * event is not emitted until all connections are ended. + * - `"connection"` - Emitted when a new connection is made. `socket` is an + * instance of `net.Socket`. + * - `"error"` - Emitted when an error occurs. Unlike `net.Socket`, the + * `"close"` event will not be emitted directly following this event unless + * `server.close()` is manually called. See the example in discussion of + * `server.listen()`. + * - `"listening"` - Emitted when the server has been bound after calling + * `server.listen()`. + */ + constructor(connectionListener?: ConnectionListener); + constructor(options?: ServerOptions, connectionListener?: ConnectionListener); + constructor( + options?: ServerOptions | ConnectionListener, + connectionListener?: ConnectionListener, + ) { + super(); + + if (_isConnectionListener(options)) { + this.on("connection", options); + } else if (_isServerSocketOptions(options)) { + this.allowHalfOpen = options?.allowHalfOpen || false; + this.pauseOnConnect = !!options?.pauseOnConnect; + + if (_isConnectionListener(connectionListener)) { + this.on("connection", connectionListener); + } + } else { + throw new ERR_INVALID_ARG_TYPE("options", "Object", options); + } + } + + /** + * Start a server listening for connections. A `net.Server` can be a TCP or + * an `IPC` server depending on what it listens to. + * + * Possible signatures: + * + * - `server.listen(handle[, backlog][, callback])` + * - `server.listen(options[, callback])` + * - `server.listen(path[, backlog][, callback])` for `IPC` servers + * - `server.listen([port[, host[, backlog]]][, callback])` for TCP servers + * + * This function is asynchronous. When the server starts listening, the `'listening'` event will be emitted. The last parameter `callback`will be added as a listener for the `'listening'` + * event. + * + * All `listen()` methods can take a `backlog` parameter to specify the maximum + * length of the queue of pending connections. The actual length will be determined + * by the OS through sysctl settings such as `tcp_max_syn_backlog` and `somaxconn` on Linux. The default value of this parameter is 511 (not 512). + * + * All `Socket` are set to `SO_REUSEADDR` (see [`socket(7)`](https://man7.org/linux/man-pages/man7/socket.7.html) for + * details). + * + * The `server.listen()` method can be called again if and only if there was an + * error during the first `server.listen()` call or `server.close()` has been + * called. Otherwise, an `ERR_SERVER_ALREADY_LISTEN` error will be thrown. + * + * One of the most common errors raised when listening is `EADDRINUSE`. + * This happens when another server is already listening on the requested`port`/`path`/`handle`. One way to handle this would be to retry + * after a certain amount of time: + */ + listen( + port?: number, + hostname?: string, + backlog?: number, + listeningListener?: () => void, + ): this; + listen( + port?: number, + hostname?: string, + listeningListener?: () => void, + ): this; + listen(port?: number, backlog?: number, listeningListener?: () => void): this; + listen(port?: number, listeningListener?: () => void): this; + listen(path: string, backlog?: number, listeningListener?: () => void): this; + listen(path: string, listeningListener?: () => void): this; + listen(options: ListenOptions, listeningListener?: () => void): this; + // deno-lint-ignore no-explicit-any + listen(handle: any, backlog?: number, listeningListener?: () => void): this; + // deno-lint-ignore no-explicit-any + listen(handle: any, listeningListener?: () => void): this; + listen(...args: unknown[]): this { + const normalized = _normalizeArgs(args); + let options = normalized[0] as Partial; + const cb = normalized[1]; + + if (this._handle) { + throw new ERR_SERVER_ALREADY_LISTEN(); + } + + if (cb !== null) { + this.once("listening", cb); + } + + const backlogFromArgs: number = + // (handle, backlog) or (path, backlog) or (port, backlog) + _toNumber(args.length > 1 && args[1]) || + (_toNumber(args.length > 2 && args[2]) as number); // (port, host, backlog) + + // deno-lint-ignore no-explicit-any + options = (options as any)._handle || (options as any).handle || options; + const flags = _getFlags(options.ipv6Only); + + // (handle[, backlog][, cb]) where handle is an object with a handle + if (options instanceof TCP) { + this._handle = options; + this[asyncIdSymbol] = this._handle.getAsyncId(); + + _listenInCluster(this, null, -1, -1, backlogFromArgs); + + return this; + } + + _addAbortSignalOption(this, options); + + // (handle[, backlog][, cb]) where handle is an object with a fd + if (typeof options.fd === "number" && options.fd >= 0) { + _listenInCluster(this, null, null, null, backlogFromArgs, options.fd); + + return this; + } + + // ([port][, host][, backlog][, cb]) where port is omitted, + // that is, listen(), listen(null), listen(cb), or listen(null, cb) + // or (options[, cb]) where options.port is explicitly set as undefined or + // null, bind to an arbitrary unused port + if ( + args.length === 0 || + typeof args[0] === "function" || + (typeof options.port === "undefined" && "port" in options) || + options.port === null + ) { + options.port = 0; + } + + // ([port][, host][, backlog][, cb]) where port is specified + // or (options[, cb]) where options.port is specified + // or if options.port is normalized as 0 before + let backlog; + + if (typeof options.port === "number" || typeof options.port === "string") { + validatePort(options.port, "options.port"); + backlog = options.backlog || backlogFromArgs; + + // start TCP server listening on host:port + if (options.host) { + _lookupAndListen( + this, + options.port | 0, + options.host, + backlog, + !!options.exclusive, + flags, + ); + } else { + // Undefined host, listens on unspecified address + // Default addressType 4 will be used to search for primary server + _listenInCluster( + this, + null, + options.port | 0, + 4, + backlog, + undefined, + options.exclusive, + ); + } + + return this; + } + + // (path[, backlog][, cb]) or (options[, cb]) + // where path or options.path is a UNIX domain socket or Windows pipe + if (options.path && _isPipeName(options.path)) { + const pipeName = (this._pipeName = options.path); + backlog = options.backlog || backlogFromArgs; + + _listenInCluster( + this, + pipeName, + -1, + -1, + backlog, + undefined, + options.exclusive, + ); + + if (!this._handle) { + // Failed and an error shall be emitted in the next tick. + // Therefore, we directly return. + return this; + } + + let mode = 0; + + if (options.readableAll === true) { + mode |= PipeConstants.UV_READABLE; + } + + if (options.writableAll === true) { + mode |= PipeConstants.UV_WRITABLE; + } + + if (mode !== 0) { + const err = this._handle.fchmod(mode); + + if (err) { + this._handle.close(); + this._handle = null; + + throw errnoException(err, "uv_pipe_chmod"); + } + } + + return this; + } + + if (!("port" in options || "path" in options)) { + throw new ERR_INVALID_ARG_VALUE( + "options", + options, + 'must have the property "port" or "path"', + ); + } + + throw new ERR_INVALID_ARG_VALUE("options", options); + } + + /** + * Stops the server from accepting new connections and keeps existing + * connections. This function is asynchronous, the server is finally closed + * when all connections are ended and the server emits a `"close"` event. + * The optional `callback` will be called once the `"close"` event occurs. Unlike + * that event, it will be called with an `Error` as its only argument if the server + * was not open when it was closed. + * + * @param cb Called when the server is closed. + */ + close(cb?: (err?: Error) => void): this { + if (typeof cb === "function") { + if (!this._handle) { + this.once("close", function close() { + cb(new ERR_SERVER_NOT_RUNNING()); + }); + } else { + this.once("close", cb); + } + } + + if (this._handle) { + (this._handle as TCP).close(); + this._handle = null; + } + + if (this._usingWorkers) { + let left = this._workers.length; + const onWorkerClose = () => { + if (--left !== 0) { + return; + } + + this._connections = 0; + this._emitCloseIfDrained(); + }; + + // Increment connections to be sure that, even if all sockets will be closed + // during polling of workers, `close` event will be emitted only once. + this._connections++; + + // Poll workers + for (let n = 0; n < this._workers.length; n++) { + this._workers[n].close(onWorkerClose); + } + } else { + this._emitCloseIfDrained(); + } + + return this; + } + + /** + * Returns the bound `address`, the address `family` name, and `port` of the server + * as reported by the operating system if listening on an IP socket + * (useful to find which port was assigned when getting an OS-assigned address):`{ port: 12346, family: "IPv4", address: "127.0.0.1" }`. + * + * For a server listening on a pipe or Unix domain socket, the name is returned + * as a string. + * + * `server.address()` returns `null` before the `"listening"` event has been + * emitted or after calling `server.close()`. + */ + address(): AddressInfo | string | null { + if (this._handle && this._handle.getsockname) { + const out = {}; + const err = this._handle.getsockname(out); + + if (err) { + throw errnoException(err, "address"); + } + + return out as AddressInfo; + } else if (this._pipeName) { + return this._pipeName; + } + + return null; + } + + /** + * Asynchronously get the number of concurrent connections on the server. Works + * when sockets were sent to forks. + * + * Callback should take two arguments `err` and `count`. + */ + getConnections(cb: (err: Error | null, count: number) => void): this { + // deno-lint-ignore no-this-alias + const server = this; + + function end(err: Error | null, connections?: number) { + defaultTriggerAsyncIdScope( + server[asyncIdSymbol], + nextTick, + cb, + err, + connections, + ); + } + + if (!this._usingWorkers) { + end(null, this._connections); + + return this; + } + + // Poll workers + let left = this._workers.length; + let total = this._connections; + + function oncount(err: Error, count: number) { + if (err) { + left = -1; + + return end(err); + } + + total += count; + + if (--left === 0) { + return end(null, total); + } + } + + for (let n = 0; n < this._workers.length; n++) { + this._workers[n].getConnections(oncount); + } + + return this; + } + + /** + * Calling `unref()` on a server will allow the program to exit if this is the only + * active server in the event system. If the server is already `unref`ed calling `unref()` again will have no effect. + */ + unref(): this { + this._unref = true; + + if (this._handle) { + this._handle.unref(); + } + + return this; + } + + /** + * Opposite of `unref()`, calling `ref()` on a previously `unref`ed server will _not_ let the program exit if it's the only server left (the default behavior). + * If the server is `ref`ed calling `ref()` again will have no effect. + */ + ref(): this { + this._unref = false; + + if (this._handle) { + this._handle.ref(); + } + + return this; + } + + /** + * Indicates whether or not the server is listening for connections. + */ + get listening(): boolean { + return !!this._handle; + } + + _listen2 = _setupListenHandle; + + _emitCloseIfDrained() { + debug("SERVER _emitCloseIfDrained"); + if (this._handle || this._connections) { + debug( + `SERVER handle? ${!!this._handle} connections? ${this._connections}`, + ); + return; + } + + // We use setTimeout instead of nextTick here to avoid EADDRINUSE error + // when the same port listened immediately after the 'close' event. + // ref: https://github.com/denoland/deno_std/issues/2788 + defaultTriggerAsyncIdScope( + this[asyncIdSymbol], + setTimeout, + _emitCloseNT, + 0, + this, + ); + } + + _setupWorker(socketList: EventEmitter) { + this._usingWorkers = true; + this._workers.push(socketList); + + // deno-lint-ignore no-explicit-any + socketList.once("exit", (socketList: any) => { + const index = this._workers.indexOf(socketList); + this._workers.splice(index, 1); + }); + } + + [EventEmitter.captureRejectionSymbol]( + err: Error, + event: string, + sock: Socket, + ) { + switch (event) { + case "connection": { + sock.destroy(err); + break; + } + default: { + this.emit("error", err); + } + } + } +} + +/** + * Creates a new TCP or IPC server. + * + * Accepts an `options` object with properties `allowHalfOpen` (default `false`) + * and `pauseOnConnect` (default `false`). + * + * If `allowHalfOpen` is set to `false`, then the socket will + * automatically end the writable side when the readable side ends. + * + * If `allowHalfOpen` is set to `true`, when the other end of the socket + * signals the end of transmission, the server will only send back the end of + * transmission when `socket.end()` is explicitly called. For example, in the + * context of TCP, when a FIN packed is received, a FIN packed is sent back + * only when `socket.end()` is explicitly called. Until then the connection is + * half-closed (non-readable but still writable). See `"end"` event and RFC 1122 + * (section 4.2.2.13) for more information. + * + * `pauseOnConnect` indicates whether the socket should be paused on incoming + * connections. + * + * If `pauseOnConnect` is set to `true`, then the socket associated with each + * incoming connection will be paused, and no data will be read from its + * handle. This allows connections to be passed between processes without any + * data being read by the original process. To begin reading data from a paused + * socket, call `socket.resume()`. + * + * The server can be a TCP server or an IPC server, depending on what it + * `listen()` to. + * + * Here is an example of an TCP echo server which listens for connections on + * port 8124: + * + * @param options Socket options. + * @param connectionListener Automatically set as a listener for the `"connection"` event. + * @return A `net.Server`. + */ +export function createServer( + options?: ServerOptions, + connectionListener?: ConnectionListener, +): Server { + return new Server(options, connectionListener); +} + +export { isIP, isIPv4, isIPv6 }; + +export default { + _createServerHandle, + _normalizeArgs, + isIP, + isIPv4, + isIPv6, + connect, + createConnection, + createServer, + Server, + Socket, + Stream, +}; diff --git a/ext/node/polyfills/os.ts b/ext/node/polyfills/os.ts new file mode 100644 index 0000000000..94ca944c8f --- /dev/null +++ b/ext/node/polyfills/os.ts @@ -0,0 +1,349 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { validateIntegerRange } from "internal:deno_node/polyfills/_utils.ts"; +import process from "internal:deno_node/polyfills/process.ts"; +import { isWindows, osType } from "internal:deno_node/polyfills/_util/os.ts"; +import { os } from "internal:deno_node/polyfills/internal_binding/constants.ts"; + +export const constants = os; + +const SEE_GITHUB_ISSUE = "See https://github.com/denoland/deno_std/issues/1436"; + +// @ts-ignore Deno[Deno.internal] is used on purpose here +const DenoOsUptime = Deno[Deno.internal]?.nodeUnstable?.osUptime || + Deno.osUptime; + +interface CPUTimes { + /** The number of milliseconds the CPU has spent in user mode */ + user: number; + + /** The number of milliseconds the CPU has spent in nice mode */ + nice: number; + + /** The number of milliseconds the CPU has spent in sys mode */ + sys: number; + + /** The number of milliseconds the CPU has spent in idle mode */ + idle: number; + + /** The number of milliseconds the CPU has spent in irq mode */ + irq: number; +} + +interface CPUCoreInfo { + model: string; + + /** in MHz */ + speed: number; + + times: CPUTimes; +} + +interface NetworkAddress { + /** The assigned IPv4 or IPv6 address */ + address: string; + + /** The IPv4 or IPv6 network mask */ + netmask: string; + + family: "IPv4" | "IPv6"; + + /** The MAC address of the network interface */ + mac: string; + + /** true if the network interface is a loopback or similar interface that is not remotely accessible; otherwise false */ + internal: boolean; + + /** The numeric IPv6 scope ID (only specified when family is IPv6) */ + scopeid?: number; + + /** The assigned IPv4 or IPv6 address with the routing prefix in CIDR notation. If the netmask is invalid, this property is set to null. */ + cidr: string; +} + +interface NetworkInterfaces { + [key: string]: NetworkAddress[]; +} + +export interface UserInfoOptions { + encoding: string; +} + +interface UserInfo { + username: string; + uid: number; + gid: number; + shell: string; + homedir: string; +} + +export function arch(): string { + return process.arch; +} + +// deno-lint-ignore no-explicit-any +(arch as any)[Symbol.toPrimitive] = (): string => process.arch; +// deno-lint-ignore no-explicit-any +(endianness as any)[Symbol.toPrimitive] = (): string => endianness(); +// deno-lint-ignore no-explicit-any +(freemem as any)[Symbol.toPrimitive] = (): number => freemem(); +// deno-lint-ignore no-explicit-any +(homedir as any)[Symbol.toPrimitive] = (): string | null => homedir(); +// deno-lint-ignore no-explicit-any +(hostname as any)[Symbol.toPrimitive] = (): string | null => hostname(); +// deno-lint-ignore no-explicit-any +(platform as any)[Symbol.toPrimitive] = (): string => platform(); +// deno-lint-ignore no-explicit-any +(release as any)[Symbol.toPrimitive] = (): string => release(); +// deno-lint-ignore no-explicit-any +(version as any)[Symbol.toPrimitive] = (): string => version(); +// deno-lint-ignore no-explicit-any +(totalmem as any)[Symbol.toPrimitive] = (): number => totalmem(); +// deno-lint-ignore no-explicit-any +(type as any)[Symbol.toPrimitive] = (): string => type(); +// deno-lint-ignore no-explicit-any +(uptime as any)[Symbol.toPrimitive] = (): number => uptime(); + +export function cpus(): CPUCoreInfo[] { + return Array.from(Array(navigator.hardwareConcurrency)).map(() => { + return { + model: "", + speed: 0, + times: { + user: 0, + nice: 0, + sys: 0, + idle: 0, + irq: 0, + }, + }; + }); +} + +/** + * Returns a string identifying the endianness of the CPU for which the Deno + * binary was compiled. Possible values are 'BE' for big endian and 'LE' for + * little endian. + */ +export function endianness(): "BE" | "LE" { + // Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView#Endianness + const buffer = new ArrayBuffer(2); + new DataView(buffer).setInt16(0, 256, true /* littleEndian */); + // Int16Array uses the platform's endianness. + return new Int16Array(buffer)[0] === 256 ? "LE" : "BE"; +} + +/** Return free memory amount */ +export function freemem(): number { + return Deno.systemMemoryInfo().free; +} + +/** Not yet implemented */ +export function getPriority(pid = 0): number { + validateIntegerRange(pid, "pid"); + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the string path of the current user's home directory. */ +export function homedir(): string | null { + // Note: Node/libuv calls getpwuid() / GetUserProfileDirectory() when the + // environment variable isn't set but that's the (very uncommon) fallback + // path. IMO, it's okay to punt on that for now. + switch (osType) { + case "windows": + return Deno.env.get("USERPROFILE") || null; + case "linux": + case "darwin": + case "freebsd": + return Deno.env.get("HOME") || null; + default: + throw Error("unreachable"); + } +} + +/** Returns the host name of the operating system as a string. */ +export function hostname(): string { + return Deno.hostname(); +} + +/** Returns an array containing the 1, 5, and 15 minute load averages */ +export function loadavg(): number[] { + if (isWindows) { + return [0, 0, 0]; + } + return Deno.loadavg(); +} + +/** Returns an object containing network interfaces that have been assigned a network address. + * Each key on the returned object identifies a network interface. The associated value is an array of objects that each describe an assigned network address. */ +export function networkInterfaces(): NetworkInterfaces { + const interfaces: NetworkInterfaces = {}; + for ( + const { name, address, netmask, family, mac, scopeid, cidr } of Deno + .networkInterfaces() + ) { + const addresses = interfaces[name] ||= []; + const networkAddress: NetworkAddress = { + address, + netmask, + family, + mac, + internal: (family === "IPv4" && isIPv4LoopbackAddr(address)) || + (family === "IPv6" && isIPv6LoopbackAddr(address)), + cidr, + }; + if (family === "IPv6") { + networkAddress.scopeid = scopeid!; + } + addresses.push(networkAddress); + } + return interfaces; +} + +function isIPv4LoopbackAddr(addr: string) { + return addr.startsWith("127"); +} + +function isIPv6LoopbackAddr(addr: string) { + return addr === "::1" || addr === "fe80::1"; +} + +/** Returns the a string identifying the operating system platform. The value is set at compile time. Possible values are 'darwin', 'linux', and 'win32'. */ +export function platform(): string { + return process.platform; +} + +/** Returns the operating system as a string */ +export function release(): string { + return Deno.osRelease(); +} + +/** Returns a string identifying the kernel version */ +export function version(): string { + // TODO(kt3k): Temporarily uses Deno.osRelease(). + // Revisit this if this implementation is insufficient for any npm module + return Deno.osRelease(); +} + +/** Not yet implemented */ +export function setPriority(pid: number, priority?: number) { + /* The node API has the 'pid' as the first parameter and as optional. + This makes for a problematic implementation in Typescript. */ + if (priority === undefined) { + priority = pid; + pid = 0; + } + validateIntegerRange(pid, "pid"); + validateIntegerRange(priority, "priority", -20, 19); + + notImplemented(SEE_GITHUB_ISSUE); +} + +/** Returns the operating system's default directory for temporary files as a string. */ +export function tmpdir(): string | null { + /* This follows the node js implementation, but has a few + differences: + * On windows, if none of the environment variables are defined, + we return null. + * On unix we use a plain Deno.env.get, instead of safeGetenv, + which special cases setuid binaries. + * Node removes a single trailing / or \, we remove all. + */ + if (isWindows) { + const temp = Deno.env.get("TEMP") || Deno.env.get("TMP"); + if (temp) { + return temp.replace(/(? */ +export const CHAR_LEFT_CURLY_BRACKET = 123; /* { */ +export const CHAR_RIGHT_CURLY_BRACKET = 125; /* } */ +export const CHAR_HYPHEN_MINUS = 45; /* - */ +export const CHAR_PLUS = 43; /* + */ +export const CHAR_DOUBLE_QUOTE = 34; /* " */ +export const CHAR_SINGLE_QUOTE = 39; /* ' */ +export const CHAR_PERCENT = 37; /* % */ +export const CHAR_SEMICOLON = 59; /* ; */ +export const CHAR_CIRCUMFLEX_ACCENT = 94; /* ^ */ +export const CHAR_GRAVE_ACCENT = 96; /* ` */ +export const CHAR_AT = 64; /* @ */ +export const CHAR_AMPERSAND = 38; /* & */ +export const CHAR_EQUAL = 61; /* = */ + +// Digits +export const CHAR_0 = 48; /* 0 */ +export const CHAR_9 = 57; /* 9 */ diff --git a/ext/node/polyfills/path/_interface.ts b/ext/node/polyfills/path/_interface.ts new file mode 100644 index 0000000000..46d6d00f8e --- /dev/null +++ b/ext/node/polyfills/path/_interface.ts @@ -0,0 +1,29 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +/** + * A parsed path object generated by path.parse() or consumed by path.format(). + */ +export interface ParsedPath { + /** + * The root of the path such as '/' or 'c:\' + */ + root: string; + /** + * The full directory path such as '/home/user/dir' or 'c:\path\dir' + */ + dir: string; + /** + * The file name including extension (if any) such as 'index.html' + */ + base: string; + /** + * The file extension (if any) such as '.html' + */ + ext: string; + /** + * The file name without extension (if any) such as 'index' + */ + name: string; +} + +export type FormatInputPathObject = Partial; diff --git a/ext/node/polyfills/path/_util.ts b/ext/node/polyfills/path/_util.ts new file mode 100644 index 0000000000..ccc12abc9b --- /dev/null +++ b/ext/node/polyfills/path/_util.ts @@ -0,0 +1,131 @@ +// Copyright the Browserify authors. MIT License. +// Ported from https://github.com/browserify/path-browserify/ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import type { FormatInputPathObject } from "internal:deno_node/polyfills/path/_interface.ts"; +import { + CHAR_BACKWARD_SLASH, + CHAR_DOT, + CHAR_FORWARD_SLASH, + CHAR_LOWERCASE_A, + CHAR_LOWERCASE_Z, + CHAR_UPPERCASE_A, + CHAR_UPPERCASE_Z, +} from "internal:deno_node/polyfills/path/_constants.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; + +export function assertPath(path: string) { + if (typeof path !== "string") { + throw new ERR_INVALID_ARG_TYPE("path", ["string"], path); + } +} + +export function isPosixPathSeparator(code: number): boolean { + return code === CHAR_FORWARD_SLASH; +} + +export function isPathSeparator(code: number): boolean { + return isPosixPathSeparator(code) || code === CHAR_BACKWARD_SLASH; +} + +export function isWindowsDeviceRoot(code: number): boolean { + return ( + (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) || + (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) + ); +} + +// Resolves . and .. elements in a path with directory names +export function normalizeString( + path: string, + allowAboveRoot: boolean, + separator: string, + isPathSeparator: (code: number) => boolean, +): string { + let res = ""; + let lastSegmentLength = 0; + let lastSlash = -1; + let dots = 0; + let code: number | undefined; + for (let i = 0, len = path.length; i <= len; ++i) { + if (i < len) code = path.charCodeAt(i); + else if (isPathSeparator(code!)) break; + else code = CHAR_FORWARD_SLASH; + + if (isPathSeparator(code!)) { + if (lastSlash === i - 1 || dots === 1) { + // NOOP + } else if (lastSlash !== i - 1 && dots === 2) { + if ( + res.length < 2 || + lastSegmentLength !== 2 || + res.charCodeAt(res.length - 1) !== CHAR_DOT || + res.charCodeAt(res.length - 2) !== CHAR_DOT + ) { + if (res.length > 2) { + const lastSlashIndex = res.lastIndexOf(separator); + if (lastSlashIndex === -1) { + res = ""; + lastSegmentLength = 0; + } else { + res = res.slice(0, lastSlashIndex); + lastSegmentLength = res.length - 1 - res.lastIndexOf(separator); + } + lastSlash = i; + dots = 0; + continue; + } else if (res.length === 2 || res.length === 1) { + res = ""; + lastSegmentLength = 0; + lastSlash = i; + dots = 0; + continue; + } + } + if (allowAboveRoot) { + if (res.length > 0) res += `${separator}..`; + else res = ".."; + lastSegmentLength = 2; + } + } else { + if (res.length > 0) res += separator + path.slice(lastSlash + 1, i); + else res = path.slice(lastSlash + 1, i); + lastSegmentLength = i - lastSlash - 1; + } + lastSlash = i; + dots = 0; + } else if (code === CHAR_DOT && dots !== -1) { + ++dots; + } else { + dots = -1; + } + } + return res; +} + +export function _format( + sep: string, + pathObject: FormatInputPathObject, +): string { + const dir: string | undefined = pathObject.dir || pathObject.root; + const base: string = pathObject.base || + (pathObject.name || "") + (pathObject.ext || ""); + if (!dir) return base; + if (dir === pathObject.root) return dir + base; + return dir + sep + base; +} + +const WHITESPACE_ENCODINGS: Record = { + "\u0009": "%09", + "\u000A": "%0A", + "\u000B": "%0B", + "\u000C": "%0C", + "\u000D": "%0D", + "\u0020": "%20", +}; + +export function encodeWhitespace(string: string): string { + return string.replaceAll(/[\s]/g, (c) => { + return WHITESPACE_ENCODINGS[c] ?? c; + }); +} diff --git a/ext/node/polyfills/path/common.ts b/ext/node/polyfills/path/common.ts new file mode 100644 index 0000000000..e4efe7cc4a --- /dev/null +++ b/ext/node/polyfills/path/common.ts @@ -0,0 +1,39 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// This module is browser compatible. + +import { SEP } from "internal:deno_node/polyfills/path/separator.ts"; + +/** Determines the common path from a set of paths, using an optional separator, + * which defaults to the OS default separator. + * + * ```ts + * const p = common([ + * "./deno/std/path/mod.ts", + * "./deno/std/fs/mod.ts", + * ]); + * console.log(p); // "./deno/std/" + * ``` + */ +export function common(paths: string[], sep = SEP): string { + const [first = "", ...remaining] = paths; + if (first === "" || remaining.length === 0) { + return first.substring(0, first.lastIndexOf(sep) + 1); + } + const parts = first.split(sep); + + let endOfPrefix = parts.length; + for (const path of remaining) { + const compare = path.split(sep); + for (let i = 0; i < endOfPrefix; i++) { + if (compare[i] !== parts[i]) { + endOfPrefix = i; + } + } + + if (endOfPrefix === 0) { + return ""; + } + } + const prefix = parts.slice(0, endOfPrefix).join(sep); + return prefix.endsWith(sep) ? prefix : `${prefix}${sep}`; +} diff --git a/ext/node/polyfills/path/glob.ts b/ext/node/polyfills/path/glob.ts new file mode 100644 index 0000000000..c0da29b9f0 --- /dev/null +++ b/ext/node/polyfills/path/glob.ts @@ -0,0 +1,420 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { isWindows, osType } from "internal:deno_node/polyfills/_util/os.ts"; +import { + SEP, + SEP_PATTERN, +} from "internal:deno_node/polyfills/path/separator.ts"; +import * as _win32 from "internal:deno_node/polyfills/path/win32.ts"; +import * as _posix from "internal:deno_node/polyfills/path/posix.ts"; +import type { OSType } from "internal:deno_node/polyfills/_util/os.ts"; + +const path = isWindows ? _win32 : _posix; +const { join, normalize } = path; + +export interface GlobOptions { + /** Extended glob syntax. + * See https://www.linuxjournal.com/content/bash-extended-globbing. + * + * @default {true} + */ + extended?: boolean; + /** Globstar syntax. + * See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. + * If false, `**` is treated like `*`. + * + * @default {true} + */ + globstar?: boolean; + /** Whether globstar should be case-insensitive. */ + caseInsensitive?: boolean; + /** Operating system. Defaults to the native OS. */ + os?: OSType; +} + +export type GlobToRegExpOptions = GlobOptions; + +const regExpEscapeChars = [ + "!", + "$", + "(", + ")", + "*", + "+", + ".", + "=", + "?", + "[", + "\\", + "^", + "{", + "|", +]; +const rangeEscapeChars = ["-", "\\", "]"]; + +/** Convert a glob string to a regular expression. + * + * Tries to match bash glob expansion as closely as possible. + * + * Basic glob syntax: + * - `*` - Matches everything without leaving the path segment. + * - `?` - Matches any single character. + * - `{foo,bar}` - Matches `foo` or `bar`. + * - `[abcd]` - Matches `a`, `b`, `c` or `d`. + * - `[a-d]` - Matches `a`, `b`, `c` or `d`. + * - `[!abcd]` - Matches any single character besides `a`, `b`, `c` or `d`. + * - `[[::]]` - Matches any character belonging to ``. + * - `[[:alnum:]]` - Matches any digit or letter. + * - `[[:digit:]abc]` - Matches any digit, `a`, `b` or `c`. + * - See https://facelessuser.github.io/wcmatch/glob/#posix-character-classes + * for a complete list of supported character classes. + * - `\` - Escapes the next character for an `os` other than `"windows"`. + * - \` - Escapes the next character for `os` set to `"windows"`. + * - `/` - Path separator. + * - `\` - Additional path separator only for `os` set to `"windows"`. + * + * Extended syntax: + * - Requires `{ extended: true }`. + * - `?(foo|bar)` - Matches 0 or 1 instance of `{foo,bar}`. + * - `@(foo|bar)` - Matches 1 instance of `{foo,bar}`. They behave the same. + * - `*(foo|bar)` - Matches _n_ instances of `{foo,bar}`. + * - `+(foo|bar)` - Matches _n > 0_ instances of `{foo,bar}`. + * - `!(foo|bar)` - Matches anything other than `{foo,bar}`. + * - See https://www.linuxjournal.com/content/bash-extended-globbing. + * + * Globstar syntax: + * - Requires `{ globstar: true }`. + * - `**` - Matches any number of any path segments. + * - Must comprise its entire path segment in the provided glob. + * - See https://www.linuxjournal.com/content/globstar-new-bash-globbing-option. + * + * Note the following properties: + * - The generated `RegExp` is anchored at both start and end. + * - Repeating and trailing separators are tolerated. Trailing separators in the + * provided glob have no meaning and are discarded. + * - Absolute globs will only match absolute paths, etc. + * - Empty globs will match nothing. + * - Any special glob syntax must be contained to one path segment. For example, + * `?(foo|bar/baz)` is invalid. The separator will take precedence and the + * first segment ends with an unclosed group. + * - If a path segment ends with unclosed groups or a dangling escape prefix, a + * parse error has occurred. Every character for that segment is taken + * literally in this event. + * + * Limitations: + * - A negative group like `!(foo|bar)` will wrongly be converted to a negative + * look-ahead followed by a wildcard. This means that `!(foo).js` will wrongly + * fail to match `foobar.js`, even though `foobar` is not `foo`. Effectively, + * `!(foo|bar)` is treated like `!(@(foo|bar)*)`. This will work correctly if + * the group occurs not nested at the end of the segment. */ +export function globToRegExp( + glob: string, + { + extended = true, + globstar: globstarOption = true, + os = osType, + caseInsensitive = false, + }: GlobToRegExpOptions = {}, +): RegExp { + if (glob == "") { + return /(?!)/; + } + + const sep = os == "windows" ? "(?:\\\\|/)+" : "/+"; + const sepMaybe = os == "windows" ? "(?:\\\\|/)*" : "/*"; + const seps = os == "windows" ? ["\\", "/"] : ["/"]; + const globstar = os == "windows" + ? "(?:[^\\\\/]*(?:\\\\|/|$)+)*" + : "(?:[^/]*(?:/|$)+)*"; + const wildcard = os == "windows" ? "[^\\\\/]*" : "[^/]*"; + const escapePrefix = os == "windows" ? "`" : "\\"; + + // Remove trailing separators. + let newLength = glob.length; + for (; newLength > 1 && seps.includes(glob[newLength - 1]); newLength--); + glob = glob.slice(0, newLength); + + let regExpString = ""; + + // Terminates correctly. Trust that `j` is incremented every iteration. + for (let j = 0; j < glob.length;) { + let segment = ""; + const groupStack: string[] = []; + let inRange = false; + let inEscape = false; + let endsWithSep = false; + let i = j; + + // Terminates with `i` at the non-inclusive end of the current segment. + for (; i < glob.length && !seps.includes(glob[i]); i++) { + if (inEscape) { + inEscape = false; + const escapeChars = inRange ? rangeEscapeChars : regExpEscapeChars; + segment += escapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; + continue; + } + + if (glob[i] == escapePrefix) { + inEscape = true; + continue; + } + + if (glob[i] == "[") { + if (!inRange) { + inRange = true; + segment += "["; + if (glob[i + 1] == "!") { + i++; + segment += "^"; + } else if (glob[i + 1] == "^") { + i++; + segment += "\\^"; + } + continue; + } else if (glob[i + 1] == ":") { + let k = i + 1; + let value = ""; + while (glob[k + 1] != null && glob[k + 1] != ":") { + value += glob[k + 1]; + k++; + } + if (glob[k + 1] == ":" && glob[k + 2] == "]") { + i = k + 2; + if (value == "alnum") segment += "\\dA-Za-z"; + else if (value == "alpha") segment += "A-Za-z"; + else if (value == "ascii") segment += "\x00-\x7F"; + else if (value == "blank") segment += "\t "; + else if (value == "cntrl") segment += "\x00-\x1F\x7F"; + else if (value == "digit") segment += "\\d"; + else if (value == "graph") segment += "\x21-\x7E"; + else if (value == "lower") segment += "a-z"; + else if (value == "print") segment += "\x20-\x7E"; + else if (value == "punct") { + segment += "!\"#$%&'()*+,\\-./:;<=>?@[\\\\\\]^_‘{|}~"; + } else if (value == "space") segment += "\\s\v"; + else if (value == "upper") segment += "A-Z"; + else if (value == "word") segment += "\\w"; + else if (value == "xdigit") segment += "\\dA-Fa-f"; + continue; + } + } + } + + if (glob[i] == "]" && inRange) { + inRange = false; + segment += "]"; + continue; + } + + if (inRange) { + if (glob[i] == "\\") { + segment += `\\\\`; + } else { + segment += glob[i]; + } + continue; + } + + if ( + glob[i] == ")" && groupStack.length > 0 && + groupStack[groupStack.length - 1] != "BRACE" + ) { + segment += ")"; + const type = groupStack.pop()!; + if (type == "!") { + segment += wildcard; + } else if (type != "@") { + segment += type; + } + continue; + } + + if ( + glob[i] == "|" && groupStack.length > 0 && + groupStack[groupStack.length - 1] != "BRACE" + ) { + segment += "|"; + continue; + } + + if (glob[i] == "+" && extended && glob[i + 1] == "(") { + i++; + groupStack.push("+"); + segment += "(?:"; + continue; + } + + if (glob[i] == "@" && extended && glob[i + 1] == "(") { + i++; + groupStack.push("@"); + segment += "(?:"; + continue; + } + + if (glob[i] == "?") { + if (extended && glob[i + 1] == "(") { + i++; + groupStack.push("?"); + segment += "(?:"; + } else { + segment += "."; + } + continue; + } + + if (glob[i] == "!" && extended && glob[i + 1] == "(") { + i++; + groupStack.push("!"); + segment += "(?!"; + continue; + } + + if (glob[i] == "{") { + groupStack.push("BRACE"); + segment += "(?:"; + continue; + } + + if (glob[i] == "}" && groupStack[groupStack.length - 1] == "BRACE") { + groupStack.pop(); + segment += ")"; + continue; + } + + if (glob[i] == "," && groupStack[groupStack.length - 1] == "BRACE") { + segment += "|"; + continue; + } + + if (glob[i] == "*") { + if (extended && glob[i + 1] == "(") { + i++; + groupStack.push("*"); + segment += "(?:"; + } else { + const prevChar = glob[i - 1]; + let numStars = 1; + while (glob[i + 1] == "*") { + i++; + numStars++; + } + const nextChar = glob[i + 1]; + if ( + globstarOption && numStars == 2 && + [...seps, undefined].includes(prevChar) && + [...seps, undefined].includes(nextChar) + ) { + segment += globstar; + endsWithSep = true; + } else { + segment += wildcard; + } + } + continue; + } + + segment += regExpEscapeChars.includes(glob[i]) ? `\\${glob[i]}` : glob[i]; + } + + // Check for unclosed groups or a dangling backslash. + if (groupStack.length > 0 || inRange || inEscape) { + // Parse failure. Take all characters from this segment literally. + segment = ""; + for (const c of glob.slice(j, i)) { + segment += regExpEscapeChars.includes(c) ? `\\${c}` : c; + endsWithSep = false; + } + } + + regExpString += segment; + if (!endsWithSep) { + regExpString += i < glob.length ? sep : sepMaybe; + endsWithSep = true; + } + + // Terminates with `i` at the start of the next segment. + while (seps.includes(glob[i])) i++; + + // Check that the next value of `j` is indeed higher than the current value. + if (!(i > j)) { + throw new Error("Assertion failure: i > j (potential infinite loop)"); + } + j = i; + } + + regExpString = `^${regExpString}$`; + return new RegExp(regExpString, caseInsensitive ? "i" : ""); +} + +/** Test whether the given string is a glob */ +export function isGlob(str: string): boolean { + const chars: Record = { "{": "}", "(": ")", "[": "]" }; + const regex = + /\\(.)|(^!|\*|\?|[\].+)]\?|\[[^\\\]]+\]|\{[^\\}]+\}|\(\?[:!=][^\\)]+\)|\([^|]+\|[^\\)]+\))/; + + if (str === "") { + return false; + } + + let match: RegExpExecArray | null; + + while ((match = regex.exec(str))) { + if (match[2]) return true; + let idx = match.index + match[0].length; + + // if an open bracket/brace/paren is escaped, + // set the index to the next closing character + const open = match[1]; + const close = open ? chars[open] : null; + if (open && close) { + const n = str.indexOf(close, idx); + if (n !== -1) { + idx = n + 1; + } + } + + str = str.slice(idx); + } + + return false; +} + +/** Like normalize(), but doesn't collapse "**\/.." when `globstar` is true. */ +export function normalizeGlob( + glob: string, + { globstar = false }: GlobOptions = {}, +): string { + if (glob.match(/\0/g)) { + throw new Error(`Glob contains invalid characters: "${glob}"`); + } + if (!globstar) { + return normalize(glob); + } + const s = SEP_PATTERN.source; + const badParentPattern = new RegExp( + `(?<=(${s}|^)\\*\\*${s})\\.\\.(?=${s}|$)`, + "g", + ); + return normalize(glob.replace(badParentPattern, "\0")).replace(/\0/g, ".."); +} + +/** Like join(), but doesn't collapse "**\/.." when `globstar` is true. */ +export function joinGlobs( + globs: string[], + { extended = true, globstar = false }: GlobOptions = {}, +): string { + if (!globstar || globs.length == 0) { + return join(...globs); + } + if (globs.length === 0) return "."; + let joined: string | undefined; + for (const glob of globs) { + const path = glob; + if (path.length > 0) { + if (!joined) joined = path; + else joined += `${SEP}${path}`; + } + } + if (!joined) return "."; + return normalizeGlob(joined, { extended, globstar }); +} diff --git a/ext/node/polyfills/path/mod.ts b/ext/node/polyfills/path/mod.ts new file mode 100644 index 0000000000..4b4de056be --- /dev/null +++ b/ext/node/polyfills/path/mod.ts @@ -0,0 +1,37 @@ +// Copyright the Browserify authors. MIT License. +// Ported mostly from https://github.com/browserify/path-browserify/ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; +import _win32 from "internal:deno_node/polyfills/path/win32.ts"; +import _posix from "internal:deno_node/polyfills/path/posix.ts"; + +const path = isWindows ? _win32 : _posix; + +export const win32 = _win32; +export const posix = _posix; +export const { + basename, + delimiter, + dirname, + extname, + format, + fromFileUrl, + isAbsolute, + join, + normalize, + parse, + relative, + resolve, + sep, + toFileUrl, + toNamespacedPath, +} = path; + +export * from "internal:deno_node/polyfills/path/common.ts"; +export { + SEP, + SEP_PATTERN, +} from "internal:deno_node/polyfills/path/separator.ts"; +export * from "internal:deno_node/polyfills/path/_interface.ts"; +export * from "internal:deno_node/polyfills/path/glob.ts"; diff --git a/ext/node/polyfills/path/posix.ts b/ext/node/polyfills/path/posix.ts new file mode 100644 index 0000000000..8ebf646290 --- /dev/null +++ b/ext/node/polyfills/path/posix.ts @@ -0,0 +1,536 @@ +// Copyright the Browserify authors. MIT License. +// Ported from https://github.com/browserify/path-browserify/ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import type { + FormatInputPathObject, + ParsedPath, +} from "internal:deno_node/polyfills/path/_interface.ts"; +import { + CHAR_DOT, + CHAR_FORWARD_SLASH, +} from "internal:deno_node/polyfills/path/_constants.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; + +import { + _format, + assertPath, + encodeWhitespace, + isPosixPathSeparator, + normalizeString, +} from "internal:deno_node/polyfills/path/_util.ts"; + +export const sep = "/"; +export const delimiter = ":"; + +// path.resolve([from ...], to) +/** + * Resolves `pathSegments` into an absolute path. + * @param pathSegments an array of path segments + */ +export function resolve(...pathSegments: string[]): string { + let resolvedPath = ""; + let resolvedAbsolute = false; + + for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + let path: string; + + if (i >= 0) path = pathSegments[i]; + else { + // deno-lint-ignore no-explicit-any + const { Deno } = globalThis as any; + if (typeof Deno?.cwd !== "function") { + throw new TypeError("Resolved a relative path without a CWD."); + } + path = Deno.cwd(); + } + + assertPath(path); + + // Skip empty entries + if (path.length === 0) { + continue; + } + + resolvedPath = `${path}/${resolvedPath}`; + resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeString( + resolvedPath, + !resolvedAbsolute, + "/", + isPosixPathSeparator, + ); + + if (resolvedAbsolute) { + if (resolvedPath.length > 0) return `/${resolvedPath}`; + else return "/"; + } else if (resolvedPath.length > 0) return resolvedPath; + else return "."; +} + +/** + * Normalize the `path`, resolving `'..'` and `'.'` segments. + * @param path to be normalized + */ +export function normalize(path: string): string { + assertPath(path); + + if (path.length === 0) return "."; + + const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + const trailingSeparator = + path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH; + + // Normalize the path + path = normalizeString(path, !isAbsolute, "/", isPosixPathSeparator); + + if (path.length === 0 && !isAbsolute) path = "."; + if (path.length > 0 && trailingSeparator) path += "/"; + + if (isAbsolute) return `/${path}`; + return path; +} + +/** + * Verifies whether provided path is absolute + * @param path to be verified as absolute + */ +export function isAbsolute(path: string): boolean { + assertPath(path); + return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH; +} + +/** + * Join all given a sequence of `paths`,then normalizes the resulting path. + * @param paths to be joined and normalized + */ +export function join(...paths: string[]): string { + if (paths.length === 0) return "."; + let joined: string | undefined; + for (let i = 0, len = paths.length; i < len; ++i) { + const path = paths[i]; + assertPath(path); + if (path.length > 0) { + if (!joined) joined = path; + else joined += `/${path}`; + } + } + if (!joined) return "."; + return normalize(joined); +} + +/** + * Return the relative path from `from` to `to` based on current working directory. + * @param from path in current working directory + * @param to path in current working directory + */ +export function relative(from: string, to: string): string { + assertPath(from); + assertPath(to); + + if (from === to) return ""; + + from = resolve(from); + to = resolve(to); + + if (from === to) return ""; + + // Trim any leading backslashes + let fromStart = 1; + const fromEnd = from.length; + for (; fromStart < fromEnd; ++fromStart) { + if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break; + } + const fromLen = fromEnd - fromStart; + + // Trim any leading backslashes + let toStart = 1; + const toEnd = to.length; + for (; toStart < toEnd; ++toStart) { + if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) break; + } + const toLen = toEnd - toStart; + + // Compare paths to find the longest common path from root + const length = fromLen < toLen ? fromLen : toLen; + let lastCommonSep = -1; + let i = 0; + for (; i <= length; ++i) { + if (i === length) { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='/foo/bar'; to='/foo/bar/baz' + return to.slice(toStart + i + 1); + } else if (i === 0) { + // We get here if `from` is the root + // For example: from='/'; to='/foo' + return to.slice(toStart + i); + } + } else if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='/foo/bar/baz'; to='/foo/bar' + lastCommonSep = i; + } else if (i === 0) { + // We get here if `to` is the root. + // For example: from='/foo'; to='/' + lastCommonSep = 0; + } + } + break; + } + const fromCode = from.charCodeAt(fromStart + i); + const toCode = to.charCodeAt(toStart + i); + if (fromCode !== toCode) break; + else if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i; + } + + let out = ""; + // Generate the relative path based on the path difference between `to` + // and `from` + for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { + if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { + if (out.length === 0) out += ".."; + else out += "/.."; + } + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts + if (out.length > 0) return out + to.slice(toStart + lastCommonSep); + else { + toStart += lastCommonSep; + if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) ++toStart; + return to.slice(toStart); + } +} + +/** + * Resolves path to a namespace path + * @param path to resolve to namespace + */ +export function toNamespacedPath(path: string): string { + // Non-op on posix systems + return path; +} + +/** + * Return the directory name of a `path`. + * @param path to determine name for + */ +export function dirname(path: string): string { + assertPath(path); + if (path.length === 0) return "."; + const hasRoot = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + let end = -1; + let matchedSlash = true; + for (let i = path.length - 1; i >= 1; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } + + if (end === -1) return hasRoot ? "/" : "."; + if (hasRoot && end === 1) return "//"; + return path.slice(0, end); +} + +/** + * Return the last portion of a `path`. Trailing directory separators are ignored. + * @param path to process + * @param ext of path directory + */ +export function basename(path: string, ext = ""): string { + if (ext !== undefined && typeof ext !== "string") { + throw new ERR_INVALID_ARG_TYPE("ext", ["string"], ext); + } + assertPath(path); + + let start = 0; + let end = -1; + let matchedSlash = true; + let i: number; + + if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { + if (ext.length === path.length && ext === path) return ""; + let extIdx = ext.length - 1; + let firstNonSlashEnd = -1; + for (i = path.length - 1; i >= 0; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd === -1) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx >= 0) { + // Try to match the explicit extension + if (code === ext.charCodeAt(extIdx)) { + if (--extIdx === -1) { + // We matched the extension, so mark this as the end of our path + // component + end = i; + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = -1; + end = firstNonSlashEnd; + } + } + } + } + + if (start === end) end = firstNonSlashEnd; + else if (end === -1) end = path.length; + return path.slice(start, end); + } else { + for (i = path.length - 1; i >= 0; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + if (end === -1) return ""; + return path.slice(start, end); + } +} + +/** + * Return the extension of the `path`. + * @param path with extension + */ +export function extname(path: string): string { + assertPath(path); + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + for (let i = path.length - 1; i >= 0; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) startDot = i; + else if (preDotState !== 1) preDotState = 1; + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if ( + startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) + ) { + return ""; + } + return path.slice(startDot, end); +} + +/** + * Generate a path from `FormatInputPathObject` object. + * @param pathObject with path + */ +export function format(pathObject: FormatInputPathObject): string { + if (pathObject === null || typeof pathObject !== "object") { + throw new ERR_INVALID_ARG_TYPE("pathObject", ["Object"], pathObject); + } + return _format("/", pathObject); +} + +/** + * Return a `ParsedPath` object of the `path`. + * @param path to process + */ +export function parse(path: string): ParsedPath { + assertPath(path); + + const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; + if (path.length === 0) return ret; + const isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; + let start: number; + if (isAbsolute) { + ret.root = "/"; + start = 1; + } else { + start = 0; + } + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + let i = path.length - 1; + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + + // Get non-dir info + for (; i >= start; --i) { + const code = path.charCodeAt(i); + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) startDot = i; + else if (preDotState !== 1) preDotState = 1; + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if ( + startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) + ) { + if (end !== -1) { + if (startPart === 0 && isAbsolute) { + ret.base = ret.name = path.slice(1, end); + } else { + ret.base = ret.name = path.slice(startPart, end); + } + } + } else { + if (startPart === 0 && isAbsolute) { + ret.name = path.slice(1, startDot); + ret.base = path.slice(1, end); + } else { + ret.name = path.slice(startPart, startDot); + ret.base = path.slice(startPart, end); + } + ret.ext = path.slice(startDot, end); + } + + if (startPart > 0) ret.dir = path.slice(0, startPart - 1); + else if (isAbsolute) ret.dir = "/"; + + return ret; +} + +/** + * Converts a file URL to a path string. + * + * ```ts + * fromFileUrl("file:///home/foo"); // "/home/foo" + * ``` + * @param url of a file URL + */ +export function fromFileUrl(url: string | URL): string { + url = url instanceof URL ? url : new URL(url); + if (url.protocol != "file:") { + throw new TypeError("Must be a file URL."); + } + return decodeURIComponent( + url.pathname.replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), + ); +} + +/** + * Converts a path string to a file URL. + * + * ```ts + * toFileUrl("/home/foo"); // new URL("file:///home/foo") + * ``` + * @param path to convert to file URL + */ +export function toFileUrl(path: string): URL { + if (!isAbsolute(path)) { + throw new TypeError("Must be an absolute path."); + } + const url = new URL("file:///"); + url.pathname = encodeWhitespace( + path.replace(/%/g, "%25").replace(/\\/g, "%5C"), + ); + return url; +} + +export default { + basename, + delimiter, + dirname, + extname, + format, + fromFileUrl, + isAbsolute, + join, + normalize, + parse, + relative, + resolve, + sep, + toFileUrl, + toNamespacedPath, +}; diff --git a/ext/node/polyfills/path/separator.ts b/ext/node/polyfills/path/separator.ts new file mode 100644 index 0000000000..2cfde31d02 --- /dev/null +++ b/ext/node/polyfills/path/separator.ts @@ -0,0 +1,6 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { isWindows } from "internal:deno_node/polyfills/_util/os.ts"; + +export const SEP = isWindows ? "\\" : "/"; +export const SEP_PATTERN = isWindows ? /[\\/]+/ : /\/+/; diff --git a/ext/node/polyfills/path/win32.ts b/ext/node/polyfills/path/win32.ts new file mode 100644 index 0000000000..4b30e34304 --- /dev/null +++ b/ext/node/polyfills/path/win32.ts @@ -0,0 +1,1025 @@ +// Copyright the Browserify authors. MIT License. +// Ported from https://github.com/browserify/path-browserify/ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import type { + FormatInputPathObject, + ParsedPath, +} from "internal:deno_node/polyfills/path/_interface.ts"; +import { + CHAR_BACKWARD_SLASH, + CHAR_COLON, + CHAR_DOT, + CHAR_QUESTION_MARK, +} from "internal:deno_node/polyfills/path/_constants.ts"; +import { ERR_INVALID_ARG_TYPE } from "internal:deno_node/polyfills/internal/errors.ts"; + +import { + _format, + assertPath, + encodeWhitespace, + isPathSeparator, + isWindowsDeviceRoot, + normalizeString, +} from "internal:deno_node/polyfills/path/_util.ts"; +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; + +export const sep = "\\"; +export const delimiter = ";"; + +/** + * Resolves path segments into a `path` + * @param pathSegments to process to path + */ +export function resolve(...pathSegments: string[]): string { + let resolvedDevice = ""; + let resolvedTail = ""; + let resolvedAbsolute = false; + + for (let i = pathSegments.length - 1; i >= -1; i--) { + let path: string; + // deno-lint-ignore no-explicit-any + const { Deno } = globalThis as any; + if (i >= 0) { + path = pathSegments[i]; + } else if (!resolvedDevice) { + if (typeof Deno?.cwd !== "function") { + throw new TypeError("Resolved a drive-letter-less path without a CWD."); + } + path = Deno.cwd(); + } else { + if ( + typeof Deno?.env?.get !== "function" || typeof Deno?.cwd !== "function" + ) { + throw new TypeError("Resolved a relative path without a CWD."); + } + path = Deno.cwd(); + + // Verify that a cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + if ( + path === undefined || + path.slice(0, 3).toLowerCase() !== `${resolvedDevice.toLowerCase()}\\` + ) { + path = `${resolvedDevice}\\`; + } + } + + assertPath(path); + + const len = path.length; + + // Skip empty entries + if (len === 0) continue; + + let rootEnd = 0; + let device = ""; + let isAbsolute = false; + const code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + // If we started with a separator, we know we at least have an + // absolute path of some kind (UNC or otherwise) + isAbsolute = true; + + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + const firstPart = path.slice(last, j); + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j === len) { + // We matched a UNC root only + device = `\\\\${firstPart}\\${path.slice(last)}`; + rootEnd = j; + } else if (j !== last) { + // We matched a UNC root with leftovers + + device = `\\\\${firstPart}\\${path.slice(last, j)}`; + rootEnd = j; + } + } + } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + device = path.slice(0, 2); + rootEnd = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) { + // Treat separator following drive name as an absolute path + // indicator + isAbsolute = true; + rootEnd = 3; + } + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator + rootEnd = 1; + isAbsolute = true; + } + + if ( + device.length > 0 && + resolvedDevice.length > 0 && + device.toLowerCase() !== resolvedDevice.toLowerCase() + ) { + // This path points to another device so it is not applicable + continue; + } + + if (resolvedDevice.length === 0 && device.length > 0) { + resolvedDevice = device; + } + if (!resolvedAbsolute) { + resolvedTail = `${path.slice(rootEnd)}\\${resolvedTail}`; + resolvedAbsolute = isAbsolute; + } + + if (resolvedAbsolute && resolvedDevice.length > 0) break; + } + + // At this point the path should be resolved to a full absolute path, + // but handle relative paths to be safe (might happen when process.cwd() + // fails) + + // Normalize the tail path + resolvedTail = normalizeString( + resolvedTail, + !resolvedAbsolute, + "\\", + isPathSeparator, + ); + + return resolvedDevice + (resolvedAbsolute ? "\\" : "") + resolvedTail || "."; +} + +/** + * Normalizes a `path` + * @param path to normalize + */ +export function normalize(path: string): string { + assertPath(path); + const len = path.length; + if (len === 0) return "."; + let rootEnd = 0; + let device: string | undefined; + let isAbsolute = false; + const code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + // If we started with a separator, we know we at least have an absolute + // path of some kind (UNC or otherwise) + isAbsolute = true; + + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + const firstPart = path.slice(last, j); + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j === len) { + // We matched a UNC root only + // Return the normalized version of the UNC root since there + // is nothing left to process + + return `\\\\${firstPart}\\${path.slice(last)}\\`; + } else if (j !== last) { + // We matched a UNC root with leftovers + + device = `\\\\${firstPart}\\${path.slice(last, j)}`; + rootEnd = j; + } + } + } + } else { + rootEnd = 1; + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + device = path.slice(0, 2); + rootEnd = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) { + // Treat separator following drive name as an absolute path + // indicator + isAbsolute = true; + rootEnd = 3; + } + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator, exit early to avoid unnecessary + // work + return "\\"; + } + + let tail: string; + if (rootEnd < len) { + tail = normalizeString( + path.slice(rootEnd), + !isAbsolute, + "\\", + isPathSeparator, + ); + } else { + tail = ""; + } + if (tail.length === 0 && !isAbsolute) tail = "."; + if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) { + tail += "\\"; + } + if (device === undefined) { + if (isAbsolute) { + if (tail.length > 0) return `\\${tail}`; + else return "\\"; + } else if (tail.length > 0) { + return tail; + } else { + return ""; + } + } else if (isAbsolute) { + if (tail.length > 0) return `${device}\\${tail}`; + else return `${device}\\`; + } else if (tail.length > 0) { + return device + tail; + } else { + return device; + } +} + +/** + * Verifies whether path is absolute + * @param path to verify + */ +export function isAbsolute(path: string): boolean { + assertPath(path); + const len = path.length; + if (len === 0) return false; + + const code = path.charCodeAt(0); + if (isPathSeparator(code)) { + return true; + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (len > 2 && path.charCodeAt(1) === CHAR_COLON) { + if (isPathSeparator(path.charCodeAt(2))) return true; + } + } + return false; +} + +/** + * Join all given a sequence of `paths`,then normalizes the resulting path. + * @param paths to be joined and normalized + */ +export function join(...paths: string[]): string { + const pathsCount = paths.length; + if (pathsCount === 0) return "."; + + let joined: string | undefined; + let firstPart: string | null = null; + for (let i = 0; i < pathsCount; ++i) { + const path = paths[i]; + assertPath(path); + if (path.length > 0) { + if (joined === undefined) joined = firstPart = path; + else joined += `\\${path}`; + } + } + + if (joined === undefined) return "."; + + // Make sure that the joined path doesn't start with two slashes, because + // normalize() will mistake it for an UNC path then. + // + // This step is skipped when it is very clear that the user actually + // intended to point at an UNC path. This is assumed when the first + // non-empty string arguments starts with exactly two slashes followed by + // at least one more non-slash character. + // + // Note that for normalize() to treat a path as an UNC path it needs to + // have at least 2 components, so we don't filter for that here. + // This means that the user can use join to construct UNC paths from + // a server name and a share name; for example: + // path.join('//server', 'share') -> '\\\\server\\share\\') + let needsReplace = true; + let slashCount = 0; + assert(firstPart != null); + if (isPathSeparator(firstPart.charCodeAt(0))) { + ++slashCount; + const firstLen = firstPart.length; + if (firstLen > 1) { + if (isPathSeparator(firstPart.charCodeAt(1))) { + ++slashCount; + if (firstLen > 2) { + if (isPathSeparator(firstPart.charCodeAt(2))) ++slashCount; + else { + // We matched a UNC path in the first part + needsReplace = false; + } + } + } + } + } + if (needsReplace) { + // Find any more consecutive slashes we need to replace + for (; slashCount < joined.length; ++slashCount) { + if (!isPathSeparator(joined.charCodeAt(slashCount))) break; + } + + // Replace the slashes if needed + if (slashCount >= 2) joined = `\\${joined.slice(slashCount)}`; + } + + return normalize(joined); +} + +/** + * It will solve the relative path from `from` to `to`, for instance: + * from = 'C:\\orandea\\test\\aaa' + * to = 'C:\\orandea\\impl\\bbb' + * The output of the function should be: '..\\..\\impl\\bbb' + * @param from relative path + * @param to relative path + */ +export function relative(from: string, to: string): string { + assertPath(from); + assertPath(to); + + if (from === to) return ""; + + const fromOrig = resolve(from); + const toOrig = resolve(to); + + if (fromOrig === toOrig) return ""; + + from = fromOrig.toLowerCase(); + to = toOrig.toLowerCase(); + + if (from === to) return ""; + + // Trim any leading backslashes + let fromStart = 0; + let fromEnd = from.length; + for (; fromStart < fromEnd; ++fromStart) { + if (from.charCodeAt(fromStart) !== CHAR_BACKWARD_SLASH) break; + } + // Trim trailing backslashes (applicable to UNC paths only) + for (; fromEnd - 1 > fromStart; --fromEnd) { + if (from.charCodeAt(fromEnd - 1) !== CHAR_BACKWARD_SLASH) break; + } + const fromLen = fromEnd - fromStart; + + // Trim any leading backslashes + let toStart = 0; + let toEnd = to.length; + for (; toStart < toEnd; ++toStart) { + if (to.charCodeAt(toStart) !== CHAR_BACKWARD_SLASH) break; + } + // Trim trailing backslashes (applicable to UNC paths only) + for (; toEnd - 1 > toStart; --toEnd) { + if (to.charCodeAt(toEnd - 1) !== CHAR_BACKWARD_SLASH) break; + } + const toLen = toEnd - toStart; + + // Compare paths to find the longest common path from root + const length = fromLen < toLen ? fromLen : toLen; + let lastCommonSep = -1; + let i = 0; + for (; i <= length; ++i) { + if (i === length) { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_BACKWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='C:\\foo\\bar'; to='C:\\foo\\bar\\baz' + return toOrig.slice(toStart + i + 1); + } else if (i === 2) { + // We get here if `from` is the device root. + // For example: from='C:\\'; to='C:\\foo' + return toOrig.slice(toStart + i); + } + } + if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_BACKWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='C:\\foo\\bar'; to='C:\\foo' + lastCommonSep = i; + } else if (i === 2) { + // We get here if `to` is the device root. + // For example: from='C:\\foo\\bar'; to='C:\\' + lastCommonSep = 3; + } + } + break; + } + const fromCode = from.charCodeAt(fromStart + i); + const toCode = to.charCodeAt(toStart + i); + if (fromCode !== toCode) break; + else if (fromCode === CHAR_BACKWARD_SLASH) lastCommonSep = i; + } + + // We found a mismatch before the first common path separator was seen, so + // return the original `to`. + if (i !== length && lastCommonSep === -1) { + return toOrig; + } + + let out = ""; + if (lastCommonSep === -1) lastCommonSep = 0; + // Generate the relative path based on the path difference between `to` and + // `from` + for (i = fromStart + lastCommonSep + 1; i <= fromEnd; ++i) { + if (i === fromEnd || from.charCodeAt(i) === CHAR_BACKWARD_SLASH) { + if (out.length === 0) out += ".."; + else out += "\\.."; + } + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts + if (out.length > 0) { + return out + toOrig.slice(toStart + lastCommonSep, toEnd); + } else { + toStart += lastCommonSep; + if (toOrig.charCodeAt(toStart) === CHAR_BACKWARD_SLASH) ++toStart; + return toOrig.slice(toStart, toEnd); + } +} + +/** + * Resolves path to a namespace path + * @param path to resolve to namespace + */ +export function toNamespacedPath(path: string): string { + // Note: this will *probably* throw somewhere. + if (typeof path !== "string") return path; + if (path.length === 0) return ""; + + const resolvedPath = resolve(path); + + if (resolvedPath.length >= 3) { + if (resolvedPath.charCodeAt(0) === CHAR_BACKWARD_SLASH) { + // Possible UNC root + + if (resolvedPath.charCodeAt(1) === CHAR_BACKWARD_SLASH) { + const code = resolvedPath.charCodeAt(2); + if (code !== CHAR_QUESTION_MARK && code !== CHAR_DOT) { + // Matched non-long UNC root, convert the path to a long UNC path + return `\\\\?\\UNC\\${resolvedPath.slice(2)}`; + } + } + } else if (isWindowsDeviceRoot(resolvedPath.charCodeAt(0))) { + // Possible device root + + if ( + resolvedPath.charCodeAt(1) === CHAR_COLON && + resolvedPath.charCodeAt(2) === CHAR_BACKWARD_SLASH + ) { + // Matched device root, convert the path to a long UNC path + return `\\\\?\\${resolvedPath}`; + } + } + } + + return path; +} + +/** + * Return the directory name of a `path`. + * @param path to determine name for + */ +export function dirname(path: string): string { + assertPath(path); + const len = path.length; + if (len === 0) return "."; + let rootEnd = -1; + let end = -1; + let matchedSlash = true; + let offset = 0; + const code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + rootEnd = offset = 1; + + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j === len) { + // We matched a UNC root only + return path; + } + if (j !== last) { + // We matched a UNC root with leftovers + + // Offset by 1 to include the separator after the UNC root to + // treat it as a "normal root" on top of a (UNC) root + rootEnd = offset = j + 1; + } + } + } + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + rootEnd = offset = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) rootEnd = offset = 3; + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator, exit early to avoid + // unnecessary work + return path; + } + + for (let i = len - 1; i >= offset; --i) { + if (isPathSeparator(path.charCodeAt(i))) { + if (!matchedSlash) { + end = i; + break; + } + } else { + // We saw the first non-path separator + matchedSlash = false; + } + } + + if (end === -1) { + if (rootEnd === -1) return "."; + else end = rootEnd; + } + return path.slice(0, end); +} + +/** + * Return the last portion of a `path`. Trailing directory separators are ignored. + * @param path to process + * @param ext of path directory + */ +export function basename(path: string, ext = ""): string { + if (ext !== undefined && typeof ext !== "string") { + throw new ERR_INVALID_ARG_TYPE("ext", ["string"], ext); + } + + assertPath(path); + + let start = 0; + let end = -1; + let matchedSlash = true; + let i: number; + + // Check for a drive letter prefix so as not to mistake the following + // path separator as an extra separator at the end of the path that can be + // disregarded + if (path.length >= 2) { + const drive = path.charCodeAt(0); + if (isWindowsDeviceRoot(drive)) { + if (path.charCodeAt(1) === CHAR_COLON) start = 2; + } + } + + if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { + if (ext.length === path.length && ext === path) return ""; + let extIdx = ext.length - 1; + let firstNonSlashEnd = -1; + for (i = path.length - 1; i >= start; --i) { + const code = path.charCodeAt(i); + if (isPathSeparator(code)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else { + if (firstNonSlashEnd === -1) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false; + firstNonSlashEnd = i + 1; + } + if (extIdx >= 0) { + // Try to match the explicit extension + if (code === ext.charCodeAt(extIdx)) { + if (--extIdx === -1) { + // We matched the extension, so mark this as the end of our path + // component + end = i; + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = -1; + end = firstNonSlashEnd; + } + } + } + } + + if (start === end) end = firstNonSlashEnd; + else if (end === -1) end = path.length; + return path.slice(start, end); + } else { + for (i = path.length - 1; i >= start; --i) { + if (isPathSeparator(path.charCodeAt(i))) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1; + break; + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false; + end = i + 1; + } + } + + if (end === -1) return ""; + return path.slice(start, end); + } +} + +/** + * Return the extension of the `path`. + * @param path with extension + */ +export function extname(path: string): string { + assertPath(path); + let start = 0; + let startDot = -1; + let startPart = 0; + let end = -1; + let matchedSlash = true; + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + + // Check for a drive letter prefix so as not to mistake the following + // path separator as an extra separator at the end of the path that can be + // disregarded + + if ( + path.length >= 2 && + path.charCodeAt(1) === CHAR_COLON && + isWindowsDeviceRoot(path.charCodeAt(0)) + ) { + start = startPart = 2; + } + + for (let i = path.length - 1; i >= start; --i) { + const code = path.charCodeAt(i); + if (isPathSeparator(code)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) startDot = i; + else if (preDotState !== 1) preDotState = 1; + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if ( + startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) + ) { + return ""; + } + return path.slice(startDot, end); +} + +/** + * Generate a path from `FormatInputPathObject` object. + * @param pathObject with path + */ +export function format(pathObject: FormatInputPathObject): string { + if (pathObject === null || typeof pathObject !== "object") { + throw new ERR_INVALID_ARG_TYPE("pathObject", ["Object"], pathObject); + } + return _format("\\", pathObject); +} + +/** + * Return a `ParsedPath` object of the `path`. + * @param path to process + */ +export function parse(path: string): ParsedPath { + assertPath(path); + + const ret: ParsedPath = { root: "", dir: "", base: "", ext: "", name: "" }; + + const len = path.length; + if (len === 0) return ret; + + let rootEnd = 0; + let code = path.charCodeAt(0); + + // Try to match a root + if (len > 1) { + if (isPathSeparator(code)) { + // Possible UNC root + + rootEnd = 1; + if (isPathSeparator(path.charCodeAt(1))) { + // Matched double path separator at beginning + let j = 2; + let last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more path separators + for (; j < len; ++j) { + if (!isPathSeparator(path.charCodeAt(j))) break; + } + if (j < len && j !== last) { + // Matched! + last = j; + // Match 1 or more non-path separators + for (; j < len; ++j) { + if (isPathSeparator(path.charCodeAt(j))) break; + } + if (j === len) { + // We matched a UNC root only + + rootEnd = j; + } else if (j !== last) { + // We matched a UNC root with leftovers + + rootEnd = j + 1; + } + } + } + } + } else if (isWindowsDeviceRoot(code)) { + // Possible device root + + if (path.charCodeAt(1) === CHAR_COLON) { + rootEnd = 2; + if (len > 2) { + if (isPathSeparator(path.charCodeAt(2))) { + if (len === 3) { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + rootEnd = 3; + } + } else { + // `path` contains just a drive root, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + } + } + } else if (isPathSeparator(code)) { + // `path` contains just a path separator, exit early to avoid + // unnecessary work + ret.root = ret.dir = path; + return ret; + } + + if (rootEnd > 0) ret.root = path.slice(0, rootEnd); + + let startDot = -1; + let startPart = rootEnd; + let end = -1; + let matchedSlash = true; + let i = path.length - 1; + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + let preDotState = 0; + + // Get non-dir info + for (; i >= rootEnd; --i) { + code = path.charCodeAt(i); + if (isPathSeparator(code)) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1; + break; + } + continue; + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false; + end = i + 1; + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) startDot = i; + else if (preDotState !== 1) preDotState = 1; + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1; + } + } + + if ( + startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) + ) { + if (end !== -1) { + ret.base = ret.name = path.slice(startPart, end); + } + } else { + ret.name = path.slice(startPart, startDot); + ret.base = path.slice(startPart, end); + ret.ext = path.slice(startDot, end); + } + + // If the directory is the root, use the entire root as the `dir` including + // the trailing slash if any (`C:\abc` -> `C:\`). Otherwise, strip out the + // trailing slash (`C:\abc\def` -> `C:\abc`). + if (startPart > 0 && startPart !== rootEnd) { + ret.dir = path.slice(0, startPart - 1); + } else ret.dir = ret.root; + + return ret; +} + +/** + * Converts a file URL to a path string. + * + * ```ts + * fromFileUrl("file:///home/foo"); // "\\home\\foo" + * fromFileUrl("file:///C:/Users/foo"); // "C:\\Users\\foo" + * fromFileUrl("file://localhost/home/foo"); // "\\\\localhost\\home\\foo" + * ``` + * @param url of a file URL + */ +export function fromFileUrl(url: string | URL): string { + url = url instanceof URL ? url : new URL(url); + if (url.protocol != "file:") { + throw new TypeError("Must be a file URL."); + } + let path = decodeURIComponent( + url.pathname.replace(/\//g, "\\").replace(/%(?![0-9A-Fa-f]{2})/g, "%25"), + ).replace(/^\\*([A-Za-z]:)(\\|$)/, "$1\\"); + if (url.hostname != "") { + // Note: The `URL` implementation guarantees that the drive letter and + // hostname are mutually exclusive. Otherwise it would not have been valid + // to append the hostname and path like this. + path = `\\\\${url.hostname}${path}`; + } + return path; +} + +/** + * Converts a path string to a file URL. + * + * ```ts + * toFileUrl("\\home\\foo"); // new URL("file:///home/foo") + * toFileUrl("C:\\Users\\foo"); // new URL("file:///C:/Users/foo") + * toFileUrl("\\\\127.0.0.1\\home\\foo"); // new URL("file://127.0.0.1/home/foo") + * ``` + * @param path to convert to file URL + */ +export function toFileUrl(path: string): URL { + if (!isAbsolute(path)) { + throw new TypeError("Must be an absolute path."); + } + const [, hostname, pathname] = path.match( + /^(?:[/\\]{2}([^/\\]+)(?=[/\\](?:[^/\\]|$)))?(.*)/, + )!; + const url = new URL("file:///"); + url.pathname = encodeWhitespace(pathname.replace(/%/g, "%25")); + if (hostname != null && hostname != "localhost") { + url.hostname = hostname; + if (!url.hostname) { + throw new TypeError("Invalid hostname."); + } + } + return url; +} + +export default { + basename, + delimiter, + dirname, + extname, + format, + fromFileUrl, + isAbsolute, + join, + normalize, + parse, + relative, + resolve, + sep, + toFileUrl, + toNamespacedPath, +}; diff --git a/ext/node/polyfills/perf_hooks.ts b/ext/node/polyfills/perf_hooks.ts new file mode 100644 index 0000000000..3888ec2c54 --- /dev/null +++ b/ext/node/polyfills/perf_hooks.ts @@ -0,0 +1,86 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { + performance as shimPerformance, + PerformanceEntry, +} from "internal:deno_web/15_performance.js"; + +// FIXME(bartlomieju) +const PerformanceObserver = undefined; +const constants = {}; + +const performance: + & Omit< + Performance, + "clearMeasures" | "getEntries" | "getEntriesByName" | "getEntriesByType" + > + & { + // deno-lint-ignore no-explicit-any + eventLoopUtilization: any; + nodeTiming: Record; + // deno-lint-ignore no-explicit-any + timerify: any; + // deno-lint-ignore no-explicit-any + timeOrigin: any; + } = { + clearMarks: (markName: string) => shimPerformance.clearMarks(markName), + eventLoopUtilization: () => + notImplemented("eventLoopUtilization from performance"), + mark: (markName: string) => shimPerformance.mark(markName), + measure: ( + measureName: string, + startMark?: string | PerformanceMeasureOptions, + endMark?: string, + ): PerformanceMeasure => { + if (endMark) { + return shimPerformance.measure( + measureName, + startMark as string, + endMark, + ); + } else { + return shimPerformance.measure( + measureName, + startMark as PerformanceMeasureOptions, + ); + } + }, + nodeTiming: {}, + now: () => shimPerformance.now(), + timerify: () => notImplemented("timerify from performance"), + // deno-lint-ignore no-explicit-any + timeOrigin: (shimPerformance as any).timeOrigin, + // @ts-ignore waiting on update in `deno`, but currently this is + // a circular dependency + toJSON: () => shimPerformance.toJSON(), + addEventListener: ( + ...args: Parameters + ) => shimPerformance.addEventListener(...args), + removeEventListener: ( + ...args: Parameters + ) => shimPerformance.removeEventListener(...args), + dispatchEvent: ( + ...args: Parameters + ) => shimPerformance.dispatchEvent(...args), + }; + +const monitorEventLoopDelay = () => + notImplemented( + "monitorEventLoopDelay from performance", + ); + +export default { + performance, + PerformanceObserver, + PerformanceEntry, + monitorEventLoopDelay, + constants, +}; + +export { + constants, + monitorEventLoopDelay, + performance, + PerformanceEntry, + PerformanceObserver, +}; diff --git a/ext/node/polyfills/process.ts b/ext/node/polyfills/process.ts new file mode 100644 index 0000000000..9e7c29be74 --- /dev/null +++ b/ext/node/polyfills/process.ts @@ -0,0 +1,734 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent, Inc. and Node.js contributors. All rights reserved. MIT license. + +const internals = globalThis.__bootstrap.internals; +import { + notImplemented, + warnNotImplemented, +} from "internal:deno_node/polyfills/_utils.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + ERR_INVALID_ARG_TYPE, + ERR_UNKNOWN_SIGNAL, + errnoException, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { getOptionValue } from "internal:deno_node/polyfills/internal/options.ts"; +import { assert } from "internal:deno_node/polyfills/_util/asserts.ts"; +import { fromFileUrl, join } from "internal:deno_node/polyfills/path.ts"; +import { + arch as arch_, + chdir, + cwd, + env, + nextTick as _nextTick, + version, + versions, +} from "internal:deno_node/polyfills/_process/process.ts"; +import { _exiting } from "internal:deno_node/polyfills/_process/exiting.ts"; +export { _nextTick as nextTick, chdir, cwd, env, version, versions }; +import { + stderr as stderr_, + stdin as stdin_, + stdout as stdout_, +} from "internal:deno_node/polyfills/_process/streams.mjs"; +import { core } from "internal:deno_node/polyfills/_core.ts"; +import { processTicksAndRejections } from "internal:deno_node/polyfills/_next_tick.ts"; + +// TODO(kt3k): This should be set at start up time +export let arch = ""; + +// TODO(kt3k): This should be set at start up time +export let platform = ""; + +// TODO(kt3k): This should be set at start up time +export let pid = 0; + +// TODO(kt3k): Give better types to stdio objects +// deno-lint-ignore no-explicit-any +const stderr = stderr_ as any; +// deno-lint-ignore no-explicit-any +const stdin = stdin_ as any; +// deno-lint-ignore no-explicit-any +const stdout = stdout_ as any; + +export { stderr, stdin, stdout }; +import { getBinding } from "internal:deno_node/polyfills/internal_binding/mod.ts"; +import * as constants from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import * as uv from "internal:deno_node/polyfills/internal_binding/uv.ts"; +import type { BindingName } from "internal:deno_node/polyfills/internal_binding/mod.ts"; +import { buildAllowedFlags } from "internal:deno_node/polyfills/internal/process/per_thread.mjs"; + +// @ts-ignore Deno[Deno.internal] is used on purpose here +const DenoCommand = Deno[Deno.internal]?.nodeUnstable?.Command || + Deno.Command; + +const notImplementedEvents = [ + "disconnect", + "message", + "multipleResolves", + "rejectionHandled", + "worker", +]; + +export const argv = []; + +// Overwrites the 1st item with getter. +Object.defineProperty(argv, "0", { get: Deno.execPath }); +// Overwrites the 2st item with getter. +Object.defineProperty(argv, "1", { + get: () => { + if (Deno.mainModule.startsWith("file:")) { + return fromFileUrl(Deno.mainModule); + } else { + return join(Deno.cwd(), "$deno$node.js"); + } + }, +}); + +// TODO(kt3k): Set the rest of args at start up time instead of defining +// random number of getters. +for (let i = 0; i < 30; i++) { + const j = i; + Object.defineProperty(argv, j + 2, { get: () => Deno.args[j] }); +} + +/** https://nodejs.org/api/process.html#process_process_exit_code */ +export const exit = (code?: number | string) => { + if (code || code === 0) { + if (typeof code === "string") { + const parsedCode = parseInt(code); + process.exitCode = isNaN(parsedCode) ? undefined : parsedCode; + } else { + process.exitCode = code; + } + } + + if (!process._exiting) { + process._exiting = true; + // FIXME(bartlomieju): this is wrong, we won't be using syscall to exit + // and thus the `unload` event will not be emitted to properly trigger "emit" + // event on `process`. + process.emit("exit", process.exitCode || 0); + } + + Deno.exit(process.exitCode || 0); +}; + +function addReadOnlyProcessAlias( + name: string, + option: string, + enumerable = true, +) { + const value = getOptionValue(option); + + if (value) { + Object.defineProperty(process, name, { + writable: false, + configurable: true, + enumerable, + value, + }); + } +} + +function createWarningObject( + warning: string, + type: string, + code?: string, + // deno-lint-ignore ban-types + ctor?: Function, + detail?: string, +): Error { + assert(typeof warning === "string"); + + // deno-lint-ignore no-explicit-any + const warningErr: any = new Error(warning); + warningErr.name = String(type || "Warning"); + + if (code !== undefined) { + warningErr.code = code; + } + if (detail !== undefined) { + warningErr.detail = detail; + } + + // @ts-ignore this function is not available in lib.dom.d.ts + Error.captureStackTrace(warningErr, ctor || process.emitWarning); + + return warningErr; +} + +function doEmitWarning(warning: Error) { + process.emit("warning", warning); +} + +/** https://nodejs.org/api/process.html#process_process_emitwarning_warning_options */ +export function emitWarning( + warning: string | Error, + type: + // deno-lint-ignore ban-types + | { type: string; detail: string; code: string; ctor: Function } + | string + | null, + code?: string, + // deno-lint-ignore ban-types + ctor?: Function, +) { + let detail; + + if (type !== null && typeof type === "object" && !Array.isArray(type)) { + ctor = type.ctor; + code = type.code; + + if (typeof type.detail === "string") { + detail = type.detail; + } + + type = type.type || "Warning"; + } else if (typeof type === "function") { + ctor = type; + code = undefined; + type = "Warning"; + } + + if (type !== undefined) { + validateString(type, "type"); + } + + if (typeof code === "function") { + ctor = code; + code = undefined; + } else if (code !== undefined) { + validateString(code, "code"); + } + + if (typeof warning === "string") { + warning = createWarningObject(warning, type as string, code, ctor, detail); + } else if (!(warning instanceof Error)) { + throw new ERR_INVALID_ARG_TYPE("warning", ["Error", "string"], warning); + } + + if (warning.name === "DeprecationWarning") { + // deno-lint-ignore no-explicit-any + if ((process as any).noDeprecation) { + return; + } + + // deno-lint-ignore no-explicit-any + if ((process as any).throwDeprecation) { + // Delay throwing the error to guarantee that all former warnings were + // properly logged. + return process.nextTick(() => { + throw warning; + }); + } + } + + process.nextTick(doEmitWarning, warning); +} + +export function hrtime(time?: [number, number]): [number, number] { + const milli = performance.now(); + const sec = Math.floor(milli / 1000); + const nano = Math.floor(milli * 1_000_000 - sec * 1_000_000_000); + if (!time) { + return [sec, nano]; + } + const [prevSec, prevNano] = time; + return [sec - prevSec, nano - prevNano]; +} + +hrtime.bigint = function (): bigint { + const [sec, nano] = hrtime(); + return BigInt(sec) * 1_000_000_000n + BigInt(nano); +}; + +export function memoryUsage(): { + rss: number; + heapTotal: number; + heapUsed: number; + external: number; + arrayBuffers: number; +} { + return { + ...Deno.memoryUsage(), + arrayBuffers: 0, + }; +} + +memoryUsage.rss = function (): number { + return memoryUsage().rss; +}; + +// Returns a negative error code than can be recognized by errnoException +function _kill(pid: number, sig: number): number { + let errCode; + + if (sig === 0) { + let status; + if (Deno.build.os === "windows") { + status = (new DenoCommand("powershell.exe", { + args: ["Get-Process", "-pid", pid], + })).outputSync(); + } else { + status = (new DenoCommand("kill", { + args: ["-0", pid], + })).outputSync(); + } + + if (!status.success) { + errCode = uv.codeMap.get("ESRCH"); + } + } else { + // Reverse search the shortname based on the numeric code + const maybeSignal = Object.entries(constants.os.signals).find(( + [_, numericCode], + ) => numericCode === sig); + + if (!maybeSignal) { + errCode = uv.codeMap.get("EINVAL"); + } else { + try { + Deno.kill(pid, maybeSignal[0] as Deno.Signal); + } catch (e) { + if (e instanceof TypeError) { + throw notImplemented(maybeSignal[0]); + } + + throw e; + } + } + } + + if (!errCode) { + return 0; + } else { + return errCode; + } +} + +export function kill(pid: number, sig: string | number = "SIGTERM") { + if (pid != (pid | 0)) { + throw new ERR_INVALID_ARG_TYPE("pid", "number", pid); + } + + let err; + if (typeof sig === "number") { + err = process._kill(pid, sig); + } else { + if (sig in constants.os.signals) { + // @ts-ignore Index previously checked + err = process._kill(pid, constants.os.signals[sig]); + } else { + throw new ERR_UNKNOWN_SIGNAL(sig); + } + } + + if (err) { + throw errnoException(err, "kill"); + } + + return true; +} + +// deno-lint-ignore no-explicit-any +function uncaughtExceptionHandler(err: any, origin: string) { + // The origin parameter can be 'unhandledRejection' or 'uncaughtException' + // depending on how the uncaught exception was created. In Node.js, + // exceptions thrown from the top level of a CommonJS module are reported as + // 'uncaughtException', while exceptions thrown from the top level of an ESM + // module are reported as 'unhandledRejection'. Deno does not have a true + // CommonJS implementation, so all exceptions thrown from the top level are + // reported as 'uncaughtException'. + process.emit("uncaughtExceptionMonitor", err, origin); + process.emit("uncaughtException", err, origin); +} + +let execPath: string | null = null; + +class Process extends EventEmitter { + constructor() { + super(); + } + + /** https://nodejs.org/api/process.html#process_process_arch */ + get arch() { + if (!arch) { + arch = arch_(); + } + return arch; + } + + /** + * https://nodejs.org/api/process.html#process_process_argv + * Read permissions are required in order to get the executable route + */ + argv = argv; + + /** https://nodejs.org/api/process.html#process_process_chdir_directory */ + chdir = chdir; + + /** https://nodejs.org/api/process.html#processconfig */ + config = { + target_defaults: {}, + variables: {}, + }; + + /** https://nodejs.org/api/process.html#process_process_cwd */ + cwd = cwd; + + /** + * https://nodejs.org/api/process.html#process_process_env + * Requires env permissions + */ + env = env; + + /** https://nodejs.org/api/process.html#process_process_execargv */ + execArgv: string[] = []; + + /** https://nodejs.org/api/process.html#process_process_exit_code */ + exit = exit; + + _exiting = _exiting; + + /** https://nodejs.org/api/process.html#processexitcode_1 */ + exitCode: undefined | number = undefined; + + // Typed as any to avoid importing "module" module for types + // deno-lint-ignore no-explicit-any + mainModule: any = undefined; + + /** https://nodejs.org/api/process.html#process_process_nexttick_callback_args */ + nextTick = _nextTick; + + /** https://nodejs.org/api/process.html#process_process_events */ + override on(event: "exit", listener: (code: number) => void): this; + override on( + event: typeof notImplementedEvents[number], + // deno-lint-ignore ban-types + listener: Function, + ): this; + // deno-lint-ignore no-explicit-any + override on(event: string, listener: (...args: any[]) => void): this { + if (notImplementedEvents.includes(event)) { + warnNotImplemented(`process.on("${event}")`); + super.on(event, listener); + } else if (event.startsWith("SIG")) { + if (event === "SIGBREAK" && Deno.build.os !== "windows") { + // Ignores SIGBREAK if the platform is not windows. + } else if (event === "SIGTERM" && Deno.build.os === "windows") { + // Ignores SIGTERM on windows. + } else { + Deno.addSignalListener(event as Deno.Signal, listener); + } + } else { + super.on(event, listener); + } + + return this; + } + + override off(event: "exit", listener: (code: number) => void): this; + override off( + event: typeof notImplementedEvents[number], + // deno-lint-ignore ban-types + listener: Function, + ): this; + // deno-lint-ignore no-explicit-any + override off(event: string, listener: (...args: any[]) => void): this { + if (notImplementedEvents.includes(event)) { + warnNotImplemented(`process.off("${event}")`); + super.off(event, listener); + } else if (event.startsWith("SIG")) { + if (event === "SIGBREAK" && Deno.build.os !== "windows") { + // Ignores SIGBREAK if the platform is not windows. + } else if (event === "SIGTERM" && Deno.build.os === "windows") { + // Ignores SIGTERM on windows. + } else { + Deno.removeSignalListener(event as Deno.Signal, listener); + } + } else { + super.off(event, listener); + } + + return this; + } + + // deno-lint-ignore no-explicit-any + override emit(event: string, ...args: any[]): boolean { + if (event.startsWith("SIG")) { + if (event === "SIGBREAK" && Deno.build.os !== "windows") { + // Ignores SIGBREAK if the platform is not windows. + } else { + Deno.kill(Deno.pid, event as Deno.Signal); + } + } else { + return super.emit(event, ...args); + } + + return true; + } + + override prependListener( + event: "exit", + listener: (code: number) => void, + ): this; + override prependListener( + event: typeof notImplementedEvents[number], + // deno-lint-ignore ban-types + listener: Function, + ): this; + override prependListener( + event: string, + // deno-lint-ignore no-explicit-any + listener: (...args: any[]) => void, + ): this { + if (notImplementedEvents.includes(event)) { + warnNotImplemented(`process.prependListener("${event}")`); + super.prependListener(event, listener); + } else if (event.startsWith("SIG")) { + if (event === "SIGBREAK" && Deno.build.os !== "windows") { + // Ignores SIGBREAK if the platform is not windows. + } else { + Deno.addSignalListener(event as Deno.Signal, listener); + } + } else { + super.prependListener(event, listener); + } + + return this; + } + + /** https://nodejs.org/api/process.html#process_process_pid */ + get pid() { + if (!pid) { + pid = Deno.pid; + } + return pid; + } + + /** https://nodejs.org/api/process.html#process_process_platform */ + get platform() { + if (!platform) { + platform = isWindows ? "win32" : Deno.build.os; + } + return platform; + } + + override addListener(event: "exit", listener: (code: number) => void): this; + override addListener( + event: typeof notImplementedEvents[number], + // deno-lint-ignore ban-types + listener: Function, + ): this; + override addListener( + event: string, + // deno-lint-ignore no-explicit-any + listener: (...args: any[]) => void, + ): this { + if (notImplementedEvents.includes(event)) { + warnNotImplemented(`process.addListener("${event}")`); + } + + return this.on(event, listener); + } + + override removeListener( + event: "exit", + listener: (code: number) => void, + ): this; + override removeListener( + event: typeof notImplementedEvents[number], + // deno-lint-ignore ban-types + listener: Function, + ): this; + override removeListener( + event: string, + // deno-lint-ignore no-explicit-any + listener: (...args: any[]) => void, + ): this { + if (notImplementedEvents.includes(event)) { + warnNotImplemented(`process.removeListener("${event}")`); + } + + return this.off(event, listener); + } + + /** + * Returns the current high-resolution real time in a [seconds, nanoseconds] + * tuple. + * + * Note: You need to give --allow-hrtime permission to Deno to actually get + * nanoseconds precision values. If you don't give 'hrtime' permission, the returned + * values only have milliseconds precision. + * + * `time` is an optional parameter that must be the result of a previous process.hrtime() call to diff with the current time. + * + * These times are relative to an arbitrary time in the past, and not related to the time of day and therefore not subject to clock drift. The primary use is for measuring performance between intervals. + * https://nodejs.org/api/process.html#process_process_hrtime_time + */ + hrtime = hrtime; + + /** + * @private + * + * NodeJS internal, use process.kill instead + */ + _kill = _kill; + + /** https://nodejs.org/api/process.html#processkillpid-signal */ + kill = kill; + + memoryUsage = memoryUsage; + + /** https://nodejs.org/api/process.html#process_process_stderr */ + stderr = stderr; + + /** https://nodejs.org/api/process.html#process_process_stdin */ + stdin = stdin; + + /** https://nodejs.org/api/process.html#process_process_stdout */ + stdout = stdout; + + /** https://nodejs.org/api/process.html#process_process_version */ + version = version; + + /** https://nodejs.org/api/process.html#process_process_versions */ + versions = versions; + + /** https://nodejs.org/api/process.html#process_process_emitwarning_warning_options */ + emitWarning = emitWarning; + + binding(name: BindingName) { + return getBinding(name); + } + + /** https://nodejs.org/api/process.html#processumaskmask */ + umask() { + // Always return the system default umask value. + // We don't use Deno.umask here because it has a race + // condition bug. + // See https://github.com/denoland/deno_std/issues/1893#issuecomment-1032897779 + return 0o22; + } + + /** This method is removed on Windows */ + getgid?(): number { + return Deno.gid()!; + } + + /** This method is removed on Windows */ + getuid?(): number { + return Deno.uid()!; + } + + // TODO(kt3k): Implement this when we added -e option to node compat mode + _eval: string | undefined = undefined; + + /** https://nodejs.org/api/process.html#processexecpath */ + get execPath() { + if (execPath) { + return execPath; + } + execPath = Deno.execPath(); + return execPath; + } + + set execPath(path: string) { + execPath = path; + } + + #startTime = Date.now(); + /** https://nodejs.org/api/process.html#processuptime */ + uptime() { + return (Date.now() - this.#startTime) / 1000; + } + + #allowedFlags = buildAllowedFlags(); + /** https://nodejs.org/api/process.html#processallowednodeenvironmentflags */ + get allowedNodeEnvironmentFlags() { + return this.#allowedFlags; + } + + features = { inspector: false }; + + // TODO(kt3k): Get the value from --no-deprecation flag. + noDeprecation = false; +} + +// TODO(kt3k): Do the below at start up time. +/* +if (Deno.build.os === "windows") { + delete Process.prototype.getgid; + delete Process.prototype.getuid; +} +*/ + +/** https://nodejs.org/api/process.html#process_process */ +const process = new Process(); + +Object.defineProperty(process, Symbol.toStringTag, { + enumerable: false, + writable: true, + configurable: false, + value: "process", +}); + +addReadOnlyProcessAlias("noDeprecation", "--no-deprecation"); +addReadOnlyProcessAlias("throwDeprecation", "--throw-deprecation"); + +export const removeListener = process.removeListener; +export const removeAllListeners = process.removeAllListeners; + +// FIXME(bartlomieju): currently it's not called +// only call this from runtime's main.js +internals.__bootstrapNodeProcess = function () { + globalThis.addEventListener("unhandledrejection", (event) => { + if (process.listenerCount("unhandledRejection") === 0) { + // The Node.js default behavior is to raise an uncaught exception if + // an unhandled rejection occurs and there are no unhandledRejection + // listeners. + if (process.listenerCount("uncaughtException") === 0) { + throw event.reason; + } + + event.preventDefault(); + uncaughtExceptionHandler(event.reason, "unhandledRejection"); + return; + } + + event.preventDefault(); + process.emit("unhandledRejection", event.reason, event.promise); + }); + + globalThis.addEventListener("error", (event) => { + if (process.listenerCount("uncaughtException") > 0) { + event.preventDefault(); + } + + uncaughtExceptionHandler(event.error, "uncaughtException"); + }); + + globalThis.addEventListener("beforeunload", (e) => { + process.emit("beforeExit", process.exitCode || 0); + processTicksAndRejections(); + if (core.eventLoopHasMoreWork()) { + e.preventDefault(); + } + }); + + globalThis.addEventListener("unload", () => { + if (!process._exiting) { + process._exiting = true; + process.emit("exit", process.exitCode || 0); + } + }); +}; + +export default process; + +//TODO(Soremwar) +//Remove on 1.0 +//Kept for backwards compatibility with std +export { process }; diff --git a/ext/node/polyfills/punycode.ts b/ext/node/polyfills/punycode.ts new file mode 100644 index 0000000000..f58871c0aa --- /dev/null +++ b/ext/node/polyfills/punycode.ts @@ -0,0 +1,19 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + decode, + encode, + toASCII, + toUnicode, + ucs2, +} from "internal:deno_node/polyfills/internal/idna.ts"; + +export { decode, encode, toASCII, toUnicode, ucs2 }; + +export default { + decode, + encode, + toASCII, + toUnicode, + ucs2, +}; diff --git a/ext/node/polyfills/querystring.ts b/ext/node/polyfills/querystring.ts new file mode 100644 index 0000000000..d8fdfbcc8f --- /dev/null +++ b/ext/node/polyfills/querystring.ts @@ -0,0 +1,517 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + encodeStr, + hexTable, +} from "internal:deno_node/polyfills/internal/querystring.ts"; + +/** + * Alias of querystring.parse() + * @legacy + */ +export const decode = parse; + +/** + * Alias of querystring.stringify() + * @legacy + */ +export const encode = stringify; + +/** + * replaces encodeURIComponent() + * @see https://www.ecma-international.org/ecma-262/5.1/#sec-15.1.3.4 + */ +function qsEscape(str: unknown): string { + if (typeof str !== "string") { + if (typeof str === "object") { + str = String(str); + } else { + str += ""; + } + } + return encodeStr(str as string, noEscape, hexTable); +} + +/** + * Performs URL percent-encoding on the given `str` in a manner that is optimized for the specific requirements of URL query strings. + * Used by `querystring.stringify()` and is generally not expected to be used directly. + * It is exported primarily to allow application code to provide a replacement percent-encoding implementation if necessary by assigning `querystring.escape` to an alternative function. + * @legacy + * @see Tested in `test-querystring-escape.js` + */ +export const escape = qsEscape; + +export interface ParsedUrlQuery { + [key: string]: string | string[] | undefined; +} + +export interface ParsedUrlQueryInput { + [key: string]: + | string + | number + | boolean + | ReadonlyArray + | ReadonlyArray + | ReadonlyArray + | null + | undefined; +} + +interface ParseOptions { + /** The function to use when decoding percent-encoded characters in the query string. */ + decodeURIComponent?: (string: string) => string; + /** Specifies the maximum number of keys to parse. */ + maxKeys?: number; +} + +// deno-fmt-ignore +const isHexTable = new Int8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 64 - 79 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 80 - 95 + 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 96 - 111 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 112 - 127 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 128 ... + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // ... 256 +]); + +function charCodes(str: string): number[] { + const ret = new Array(str.length); + for (let i = 0; i < str.length; ++i) { + ret[i] = str.charCodeAt(i); + } + return ret; +} + +function addKeyVal( + obj: ParsedUrlQuery, + key: string, + value: string, + keyEncoded: boolean, + valEncoded: boolean, + decode: (encodedURIComponent: string) => string, +) { + if (key.length > 0 && keyEncoded) { + key = decode(key); + } + if (value.length > 0 && valEncoded) { + value = decode(value); + } + + if (obj[key] === undefined) { + obj[key] = value; + } else { + const curValue = obj[key]; + // A simple Array-specific property check is enough here to + // distinguish from a string value and is faster and still safe + // since we are generating all of the values being assigned. + if ((curValue as string[]).pop) { + (curValue as string[])[curValue!.length] = value; + } else { + obj[key] = [curValue as string, value]; + } + } +} + +/** + * Parses a URL query string into a collection of key and value pairs. + * @param str The URL query string to parse + * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. + * @param eq The substring used to delimit keys and values in the query string. Default: '='. + * @param options The parse options + * @param options.decodeURIComponent The function to use when decoding percent-encoded characters in the query string. Default: `querystring.unescape()`. + * @param options.maxKeys Specifies the maximum number of keys to parse. Specify `0` to remove key counting limitations. Default: `1000`. + * @legacy + * @see Tested in test-querystring.js + */ +export function parse( + str: string, + sep = "&", + eq = "=", + { decodeURIComponent = unescape, maxKeys = 1000 }: ParseOptions = {}, +): ParsedUrlQuery { + const obj: ParsedUrlQuery = Object.create(null); + + if (typeof str !== "string" || str.length === 0) { + return obj; + } + + const sepCodes = !sep ? [38] /* & */ : charCodes(String(sep)); + const eqCodes = !eq ? [61] /* = */ : charCodes(String(eq)); + const sepLen = sepCodes.length; + const eqLen = eqCodes.length; + + let pairs = 1000; + if (typeof maxKeys === "number") { + // -1 is used in place of a value like Infinity for meaning + // "unlimited pairs" because of additional checks V8 (at least as of v5.4) + // has to do when using variables that contain values like Infinity. Since + // `pairs` is always decremented and checked explicitly for 0, -1 works + // effectively the same as Infinity, while providing a significant + // performance boost. + pairs = maxKeys > 0 ? maxKeys : -1; + } + + let decode = unescape; + if (decodeURIComponent) { + decode = decodeURIComponent; + } + const customDecode = decode !== unescape; + + let lastPos = 0; + let sepIdx = 0; + let eqIdx = 0; + let key = ""; + let value = ""; + let keyEncoded = customDecode; + let valEncoded = customDecode; + const plusChar = customDecode ? "%20" : " "; + let encodeCheck = 0; + for (let i = 0; i < str.length; ++i) { + const code = str.charCodeAt(i); + + // Try matching key/value pair separator (e.g. '&') + if (code === sepCodes[sepIdx]) { + if (++sepIdx === sepLen) { + // Key/value pair separator match! + const end = i - sepIdx + 1; + if (eqIdx < eqLen) { + // We didn't find the (entire) key/value separator + if (lastPos < end) { + // Treat the substring as part of the key instead of the value + key += str.slice(lastPos, end); + } else if (key.length === 0) { + // We saw an empty substring between separators + if (--pairs === 0) { + return obj; + } + lastPos = i + 1; + sepIdx = eqIdx = 0; + continue; + } + } else if (lastPos < end) { + value += str.slice(lastPos, end); + } + + addKeyVal(obj, key, value, keyEncoded, valEncoded, decode); + + if (--pairs === 0) { + return obj; + } + key = value = ""; + encodeCheck = 0; + lastPos = i + 1; + sepIdx = eqIdx = 0; + } + } else { + sepIdx = 0; + // Try matching key/value separator (e.g. '=') if we haven't already + if (eqIdx < eqLen) { + if (code === eqCodes[eqIdx]) { + if (++eqIdx === eqLen) { + // Key/value separator match! + const end = i - eqIdx + 1; + if (lastPos < end) { + key += str.slice(lastPos, end); + } + encodeCheck = 0; + lastPos = i + 1; + } + continue; + } else { + eqIdx = 0; + if (!keyEncoded) { + // Try to match an (valid) encoded byte once to minimize unnecessary + // calls to string decoding functions + if (code === 37 /* % */) { + encodeCheck = 1; + continue; + } else if (encodeCheck > 0) { + if (isHexTable[code] === 1) { + if (++encodeCheck === 3) { + keyEncoded = true; + } + continue; + } else { + encodeCheck = 0; + } + } + } + } + if (code === 43 /* + */) { + if (lastPos < i) { + key += str.slice(lastPos, i); + } + key += plusChar; + lastPos = i + 1; + continue; + } + } + if (code === 43 /* + */) { + if (lastPos < i) { + value += str.slice(lastPos, i); + } + value += plusChar; + lastPos = i + 1; + } else if (!valEncoded) { + // Try to match an (valid) encoded byte (once) to minimize unnecessary + // calls to string decoding functions + if (code === 37 /* % */) { + encodeCheck = 1; + } else if (encodeCheck > 0) { + if (isHexTable[code] === 1) { + if (++encodeCheck === 3) { + valEncoded = true; + } + } else { + encodeCheck = 0; + } + } + } + } + } + + // Deal with any leftover key or value data + if (lastPos < str.length) { + if (eqIdx < eqLen) { + key += str.slice(lastPos); + } else if (sepIdx < sepLen) { + value += str.slice(lastPos); + } + } else if (eqIdx === 0 && key.length === 0) { + // We ended on an empty substring + return obj; + } + + addKeyVal(obj, key, value, keyEncoded, valEncoded, decode); + + return obj; +} + +interface StringifyOptions { + /** The function to use when converting URL-unsafe characters to percent-encoding in the query string. */ + encodeURIComponent: (string: string) => string; +} + +/** + * These characters do not need escaping when generating query strings: + * ! - . _ ~ + * ' ( ) * + * digits + * alpha (uppercase) + * alpha (lowercase) + */ +// deno-fmt-ignore +const noEscape = new Int8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 80 - 95 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 112 - 127 +]); + +// deno-lint-ignore no-explicit-any +function stringifyPrimitive(v: any): string { + if (typeof v === "string") { + return v; + } + if (typeof v === "number" && isFinite(v)) { + return "" + v; + } + if (typeof v === "bigint") { + return "" + v; + } + if (typeof v === "boolean") { + return v ? "true" : "false"; + } + return ""; +} + +function encodeStringifiedCustom( + // deno-lint-ignore no-explicit-any + v: any, + encode: (string: string) => string, +): string { + return encode(stringifyPrimitive(v)); +} + +// deno-lint-ignore no-explicit-any +function encodeStringified(v: any, encode: (string: string) => string): string { + if (typeof v === "string") { + return (v.length ? encode(v) : ""); + } + if (typeof v === "number" && isFinite(v)) { + // Values >= 1e21 automatically switch to scientific notation which requires + // escaping due to the inclusion of a '+' in the output + return (Math.abs(v) < 1e21 ? "" + v : encode("" + v)); + } + if (typeof v === "bigint") { + return "" + v; + } + if (typeof v === "boolean") { + return v ? "true" : "false"; + } + return ""; +} + +/** + * Produces a URL query string from a given obj by iterating through the object's "own properties". + * @param obj The object to serialize into a URL query string. + * @param sep The substring used to delimit key and value pairs in the query string. Default: '&'. + * @param eq The substring used to delimit keys and values in the query string. Default: '='. + * @param options The stringify options + * @param options.encodeURIComponent The function to use when converting URL-unsafe characters to percent-encoding in the query string. Default: `querystring.escape()`. + * @legacy + * @see Tested in `test-querystring.js` + */ +export function stringify( + // deno-lint-ignore no-explicit-any + obj: Record, + sep?: string, + eq?: string, + options?: StringifyOptions, +): string { + sep ||= "&"; + eq ||= "="; + const encode = options ? options.encodeURIComponent : qsEscape; + const convert = options ? encodeStringifiedCustom : encodeStringified; + + if (obj !== null && typeof obj === "object") { + const keys = Object.keys(obj); + const len = keys.length; + let fields = ""; + for (let i = 0; i < len; ++i) { + const k = keys[i]; + const v = obj[k]; + let ks = convert(k, encode); + ks += eq; + + if (Array.isArray(v)) { + const vlen = v.length; + if (vlen === 0) continue; + if (fields) { + fields += sep; + } + for (let j = 0; j < vlen; ++j) { + if (j) { + fields += sep; + } + fields += ks; + fields += convert(v[j], encode); + } + } else { + if (fields) { + fields += sep; + } + fields += ks; + fields += convert(v, encode); + } + } + return fields; + } + return ""; +} + +// deno-fmt-ignore +const unhexTable = new Int8Array([ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 0 - 15 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 16 - 31 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 32 - 47 + +0, +1, +2, +3, +4, +5, +6, +7, +8, +9, -1, -1, -1, -1, -1, -1, // 48 - 63 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 64 - 79 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 80 - 95 + -1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 96 - 111 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 112 - 127 + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 128 ... + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // ... 255 +]); + +/** + * A safe fast alternative to decodeURIComponent + */ +export function unescapeBuffer(s: string, decodeSpaces = false): Buffer { + const out = Buffer.alloc(s.length); + let index = 0; + let outIndex = 0; + let currentChar; + let nextChar; + let hexHigh; + let hexLow; + const maxLength = s.length - 2; + // Flag to know if some hex chars have been decoded + let hasHex = false; + while (index < s.length) { + currentChar = s.charCodeAt(index); + if (currentChar === 43 /* '+' */ && decodeSpaces) { + out[outIndex++] = 32; // ' ' + index++; + continue; + } + if (currentChar === 37 /* '%' */ && index < maxLength) { + currentChar = s.charCodeAt(++index); + hexHigh = unhexTable[currentChar]; + if (!(hexHigh >= 0)) { + out[outIndex++] = 37; // '%' + continue; + } else { + nextChar = s.charCodeAt(++index); + hexLow = unhexTable[nextChar]; + if (!(hexLow >= 0)) { + out[outIndex++] = 37; // '%' + index--; + } else { + hasHex = true; + currentChar = hexHigh * 16 + hexLow; + } + } + } + out[outIndex++] = currentChar; + index++; + } + return hasHex ? out.slice(0, outIndex) : out; +} + +function qsUnescape(s: string): string { + try { + return decodeURIComponent(s); + } catch { + return unescapeBuffer(s).toString(); + } +} + +/** + * Performs decoding of URL percent-encoded characters on the given `str`. + * Used by `querystring.parse()` and is generally not expected to be used directly. + * It is exported primarily to allow application code to provide a replacement decoding implementation if necessary by assigning `querystring.unescape` to an alternative function. + * @legacy + * @see Tested in `test-querystring-escape.js` + */ +export const unescape = qsUnescape; + +export default { + parse, + stringify, + decode, + encode, + unescape, + escape, + unescapeBuffer, +}; diff --git a/ext/node/polyfills/readline.ts b/ext/node/polyfills/readline.ts new file mode 100644 index 0000000000..99b73177d7 --- /dev/null +++ b/ext/node/polyfills/readline.ts @@ -0,0 +1,35 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// @deno-types="./_readline.d.ts" + +import { + clearLine, + clearScreenDown, + createInterface, + cursorTo, + emitKeypressEvents, + Interface, + moveCursor, + promises, +} from "internal:deno_node/polyfills/_readline.mjs"; + +export { + clearLine, + clearScreenDown, + createInterface, + cursorTo, + emitKeypressEvents, + Interface, + moveCursor, + promises, +}; + +export default { + Interface, + clearLine, + clearScreenDown, + createInterface, + cursorTo, + emitKeypressEvents, + moveCursor, + promises, +}; diff --git a/ext/node/polyfills/readline/promises.ts b/ext/node/polyfills/readline/promises.ts new file mode 100644 index 0000000000..47e3b5b22d --- /dev/null +++ b/ext/node/polyfills/readline/promises.ts @@ -0,0 +1,178 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { Readline } from "internal:deno_node/polyfills/internal/readline/promises.mjs"; + +import { + Interface as _Interface, + kQuestion, + kQuestionCancel, +} from "internal:deno_node/polyfills/internal/readline/interface.mjs"; +import { AbortError } from "internal:deno_node/polyfills/internal/errors.ts"; +import { validateAbortSignal } from "internal:deno_node/polyfills/internal/validators.mjs"; + +import { kEmptyObject } from "internal:deno_node/polyfills/internal/util.mjs"; +import type { Abortable } from "internal:deno_node/polyfills/_events.d.ts"; +import type { + AsyncCompleter, + Completer, + ReadLineOptions, +} from "internal:deno_node/polyfills/_readline_shared_types.d.ts"; + +import type { + ReadableStream, + WritableStream, +} from "internal:deno_node/polyfills/_global.d.ts"; + +/** + * The `readline/promise` module provides an API for reading lines of input from a Readable stream one line at a time. + * + * @see [source](https://github.com/nodejs/node/blob/v18.0.0/lib/readline/promises.js) + * @since v17.0.0 + */ +export interface Interface extends _Interface { + /** + * The rl.question() method displays the query by writing it to the output, waits for user input to be provided on input, + * then invokes the callback function passing the provided input as the first argument. + * + * When called, rl.question() will resume the input stream if it has been paused. + * + * If the readlinePromises.Interface was created with output set to null or undefined the query is not written. + * + * If the question is called after rl.close(), it returns a rejected promise. + * + * Example usage: + * + * ```js + * const answer = await rl.question('What is your favorite food? '); + * console.log(`Oh, so your favorite food is ${answer}`); + * ``` + * + * Using an AbortSignal to cancel a question. + * + * ```js + * const signal = AbortSignal.timeout(10_000); + * + * signal.addEventListener('abort', () => { + * console.log('The food question timed out'); + * }, { once: true }); + * + * const answer = await rl.question('What is your favorite food? ', { signal }); + * console.log(`Oh, so your favorite food is ${answer}`); + * ``` + * + * @since v17.0.0 + * @param query A statement or query to write to output, prepended to the prompt. + */ + question(query: string, options?: Abortable): Promise; +} + +export class Interface extends _Interface { + constructor( + input: ReadableStream | ReadLineOptions, + output?: WritableStream, + completer?: Completer | AsyncCompleter, + terminal?: boolean, + ) { + super(input, output, completer, terminal); + } + question(query: string, options: Abortable = kEmptyObject): Promise { + return new Promise((resolve, reject) => { + let cb = resolve; + + if (options?.signal) { + validateAbortSignal(options.signal, "options.signal"); + if (options.signal.aborted) { + return reject( + new AbortError(undefined, { cause: options.signal.reason }), + ); + } + + const onAbort = () => { + this[kQuestionCancel](); + reject(new AbortError(undefined, { cause: options!.signal!.reason })); + }; + options.signal.addEventListener("abort", onAbort, { once: true }); + cb = (answer) => { + options!.signal!.removeEventListener("abort", onAbort); + resolve(answer); + }; + } + + this[kQuestion](query, cb); + }); + } +} + +/** + * The `readlinePromises.createInterface()` method creates a new `readlinePromises.Interface` instance. + * + * ```js + * const readlinePromises = require('node:readline/promises'); + * const rl = readlinePromises.createInterface({ + * input: process.stdin, + * output: process.stdout + * }); + * ``` + * + * Once the `readlinePromises.Interface` instance is created, the most common case is to listen for the `'line'` event: + * + * ```js + * rl.on('line', (line) => { + * console.log(`Received: ${line}`); + * }); + * ``` + * + * If `terminal` is `true` for this instance then the `output` stream will get the best compatibility if it defines an `output.columns` property, + * and emits a `'resize'` event on the `output`, if or when the columns ever change (`process.stdout` does this automatically when it is a TTY). + * + * ## Use of the `completer` function + * + * The `completer` function takes the current line entered by the user as an argument, and returns an `Array` with 2 entries: + * + * - An Array with matching entries for the completion. + * - The substring that was used for the matching. + * + * For instance: `[[substr1, substr2, ...], originalsubstring]`. + * + * ```js + * function completer(line) { + * const completions = '.help .error .exit .quit .q'.split(' '); + * const hits = completions.filter((c) => c.startsWith(line)); + * // Show all completions if none found + * return [hits.length ? hits : completions, line]; + * } + * ``` + * + * The `completer` function can also returns a `Promise`, or be asynchronous: + * + * ```js + * async function completer(linePartial) { + * await someAsyncWork(); + * return [['123'], linePartial]; + * } + * ``` + */ +export function createInterface(options: ReadLineOptions): Interface; +export function createInterface( + input: ReadableStream, + output?: WritableStream, + completer?: Completer | AsyncCompleter, + terminal?: boolean, +): Interface; +export function createInterface( + inputOrOptions: ReadableStream | ReadLineOptions, + output?: WritableStream, + completer?: Completer | AsyncCompleter, + terminal?: boolean, +): Interface { + return new Interface(inputOrOptions, output, completer, terminal); +} + +export { Readline }; + +export default { + Interface, + Readline, + createInterface, +}; diff --git a/ext/node/polyfills/repl.ts b/ext/node/polyfills/repl.ts new file mode 100644 index 0000000000..33d904de3b --- /dev/null +++ b/ext/node/polyfills/repl.ts @@ -0,0 +1,62 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export class REPLServer { + constructor() { + notImplemented("REPLServer.prototype.constructor"); + } +} +export const builtinModules = [ + "assert", + "async_hooks", + "buffer", + "child_process", + "cluster", + "console", + "constants", + "crypto", + "dgram", + "diagnostics_channel", + "dns", + "domain", + "events", + "fs", + "http", + "http2", + "https", + "inspector", + "module", + "net", + "os", + "path", + "perf_hooks", + "process", + "punycode", + "querystring", + "readline", + "repl", + "stream", + "string_decoder", + "sys", + "timers", + "tls", + "trace_events", + "tty", + "url", + "util", + "v8", + "vm", + "wasi", + "worker_threads", + "zlib", +]; +export function start() { + notImplemented("repl.start"); +} +export default { + REPLServer, + builtinModules, + start, +}; diff --git a/ext/node/polyfills/stream.ts b/ext/node/polyfills/stream.ts new file mode 100644 index 0000000000..aac96a76ee --- /dev/null +++ b/ext/node/polyfills/stream.ts @@ -0,0 +1,37 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// compose, destroy and isDisturbed are experimental APIs without +// typings. They can be exposed once they are released as stable in Node + +// @deno-types="./_stream.d.ts" +import { + _isUint8Array, + _uint8ArrayToBuffer, + addAbortSignal, + // compose, + // destroy, + Duplex, + finished, + // isDisturbed, + PassThrough, + pipeline, + Readable, + Stream, + Transform, + Writable, +} from "internal:deno_node/polyfills/_stream.mjs"; + +export { + _isUint8Array, + _uint8ArrayToBuffer, + addAbortSignal, + Duplex, + finished, + PassThrough, + pipeline, + Readable, + Stream, + Transform, + Writable, +}; + +export default Stream; diff --git a/ext/node/polyfills/stream/consumers.mjs b/ext/node/polyfills/stream/consumers.mjs new file mode 100644 index 0000000000..61fe27020b --- /dev/null +++ b/ext/node/polyfills/stream/consumers.mjs @@ -0,0 +1,78 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { TextDecoder } from "internal:deno_web/08_text_encoding.js"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; + +/** + * @typedef {import('../_global.d.ts').ReadableStream + * } ReadableStream + * @typedef {import('../_stream.d.ts')} Readable + */ + +/** + * @param {AsyncIterable|ReadableStream|Readable} stream + * @returns {Promise} + */ +async function blob(stream) { + const chunks = []; + for await (const chunk of stream) { + chunks.push(chunk); + } + return new Blob(chunks); +} + +/** + * @param {AsyncIterable|ReadableStream|Readable} stream + * @returns {Promise} + */ +async function arrayBuffer(stream) { + const ret = await blob(stream); + return ret.arrayBuffer(); +} + +/** + * @param {AsyncIterable|ReadableStream|Readable} stream + * @returns {Promise} + */ +async function buffer(stream) { + return Buffer.from(await arrayBuffer(stream)); +} + +/** + * @param {AsyncIterable|ReadableStream|Readable} stream + * @returns {Promise} + */ +async function text(stream) { + const dec = new TextDecoder(); + let str = ""; + for await (const chunk of stream) { + if (typeof chunk === "string") { + str += chunk; + } else { + str += dec.decode(chunk, { stream: true }); + } + } + // Flush the streaming TextDecoder so that any pending + // incomplete multibyte characters are handled. + str += dec.decode(undefined, { stream: false }); + return str; +} + +/** + * @param {AsyncIterable|ReadableStream|Readable} stream + * @returns {Promise} + */ +async function json(stream) { + const str = await text(stream); + return JSON.parse(str); +} + +export default { + arrayBuffer, + blob, + buffer, + json, + text, +}; +export { arrayBuffer, blob, buffer, json, text }; diff --git a/ext/node/polyfills/stream/promises.mjs b/ext/node/polyfills/stream/promises.mjs new file mode 100644 index 0000000000..8c1f7439be --- /dev/null +++ b/ext/node/polyfills/stream/promises.mjs @@ -0,0 +1,12 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import stream from "internal:deno_node/polyfills/_stream.mjs"; + +const { finished, pipeline } = stream.promises; + +export default { + finished, + pipeline, +}; +export { finished, pipeline }; diff --git a/ext/node/polyfills/stream/web.ts b/ext/node/polyfills/stream/web.ts new file mode 100644 index 0000000000..d97a6b0f10 --- /dev/null +++ b/ext/node/polyfills/stream/web.ts @@ -0,0 +1,51 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + ByteLengthQueuingStrategy, + CountQueuingStrategy, + ReadableByteStreamController, + ReadableStream, + ReadableStreamDefaultController, + ReadableStreamDefaultReader, + TransformStream, + TransformStreamDefaultController, + WritableStream, + WritableStreamDefaultController, + WritableStreamDefaultWriter, +} from "internal:deno_web/06_streams.js"; +import { + TextDecoderStream, + TextEncoderStream, +} from "internal:deno_web/08_text_encoding.js"; + +export { + ByteLengthQueuingStrategy, + CountQueuingStrategy, + ReadableByteStreamController, + ReadableStream, + ReadableStreamDefaultController, + ReadableStreamDefaultReader, + TextDecoderStream, + TextEncoderStream, + TransformStream, + TransformStreamDefaultController, + WritableStream, + WritableStreamDefaultController, + WritableStreamDefaultWriter, +}; + +export default { + ReadableStream, + ReadableStreamDefaultReader, + ReadableByteStreamController, + ReadableStreamDefaultController, + TransformStream, + TransformStreamDefaultController, + WritableStream, + WritableStreamDefaultWriter, + WritableStreamDefaultController, + ByteLengthQueuingStrategy, + CountQueuingStrategy, + TextEncoderStream, + TextDecoderStream, +}; diff --git a/ext/node/polyfills/string_decoder.ts b/ext/node/polyfills/string_decoder.ts new file mode 100644 index 0000000000..33ff6a2f00 --- /dev/null +++ b/ext/node/polyfills/string_decoder.ts @@ -0,0 +1,340 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { + normalizeEncoding as castEncoding, + notImplemented, +} from "internal:deno_node/polyfills/_utils.ts"; + +enum NotImplemented { + "ascii", + "latin1", + "utf16le", +} + +function normalizeEncoding(enc?: string): string { + const encoding = castEncoding(enc ?? null); + if (encoding && encoding in NotImplemented) notImplemented(encoding); + if (!encoding && typeof enc === "string" && enc.toLowerCase() !== "raw") { + throw new Error(`Unknown encoding: ${enc}`); + } + return String(encoding); +} + +/** + * Check is `ArrayBuffer` and not `TypedArray`. Typescript allowed `TypedArray` to be passed as `ArrayBuffer` and does not do a deep check + */ + +function isBufferType(buf: Buffer) { + return buf instanceof ArrayBuffer && buf.BYTES_PER_ELEMENT; +} + +/* + * Checks the type of a UTF-8 byte, whether it's ASCII, a leading byte, or a + * continuation byte. If an invalid byte is detected, -2 is returned. + */ +function utf8CheckByte(byte: number): number { + if (byte <= 0x7f) return 0; + else if (byte >> 5 === 0x06) return 2; + else if (byte >> 4 === 0x0e) return 3; + else if (byte >> 3 === 0x1e) return 4; + return byte >> 6 === 0x02 ? -1 : -2; +} + +/* + * Checks at most 3 bytes at the end of a Buffer in order to detect an + * incomplete multi-byte UTF-8 character. The total number of bytes (2, 3, or 4) + * needed to complete the UTF-8 character (if applicable) are returned. + */ +function utf8CheckIncomplete( + self: StringDecoderBase, + buf: Buffer, + i: number, +): number { + let j = buf.length - 1; + if (j < i) return 0; + let nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 1; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) self.lastNeed = nb - 2; + return nb; + } + if (--j < i || nb === -2) return 0; + nb = utf8CheckByte(buf[j]); + if (nb >= 0) { + if (nb > 0) { + if (nb === 2) nb = 0; + else self.lastNeed = nb - 3; + } + return nb; + } + return 0; +} + +/* + * Validates as many continuation bytes for a multi-byte UTF-8 character as + * needed or are available. If we see a non-continuation byte where we expect + * one, we "replace" the validated continuation bytes we've seen so far with + * a single UTF-8 replacement character ('\ufffd'), to match v8's UTF-8 decoding + * behavior. The continuation byte check is included three times in the case + * where all of the continuation bytes for a character exist in the same buffer. + * It is also done this way as a slight performance increase instead of using a + * loop. + */ +function utf8CheckExtraBytes( + self: StringDecoderBase, + buf: Buffer, +): string | undefined { + if ((buf[0] & 0xc0) !== 0x80) { + self.lastNeed = 0; + return "\ufffd"; + } + if (self.lastNeed > 1 && buf.length > 1) { + if ((buf[1] & 0xc0) !== 0x80) { + self.lastNeed = 1; + return "\ufffd"; + } + if (self.lastNeed > 2 && buf.length > 2) { + if ((buf[2] & 0xc0) !== 0x80) { + self.lastNeed = 2; + return "\ufffd"; + } + } + } +} + +/* + * Attempts to complete a multi-byte UTF-8 character using bytes from a Buffer. + */ +function utf8FillLastComplete( + this: StringDecoderBase, + buf: Buffer, +): string | undefined { + const p = this.lastTotal - this.lastNeed; + const r = utf8CheckExtraBytes(this, buf); + if (r !== undefined) return r; + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, p, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, p, 0, buf.length); + this.lastNeed -= buf.length; +} + +/* + * Attempts to complete a partial non-UTF-8 character using bytes from a Buffer + */ +function utf8FillLastIncomplete( + this: StringDecoderBase, + buf: Buffer, +): string | undefined { + if (this.lastNeed <= buf.length) { + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, this.lastNeed); + return this.lastChar.toString(this.encoding, 0, this.lastTotal); + } + buf.copy(this.lastChar, this.lastTotal - this.lastNeed, 0, buf.length); + this.lastNeed -= buf.length; +} + +/* + * Returns all complete UTF-8 characters in a Buffer. If the Buffer ended on a + * partial character, the character's bytes are buffered until the required + * number of bytes are available. + */ +function utf8Text(this: StringDecoderBase, buf: Buffer, i: number): string { + const total = utf8CheckIncomplete(this, buf, i); + if (!this.lastNeed) return buf.toString("utf8", i); + this.lastTotal = total; + const end = buf.length - (total - this.lastNeed); + buf.copy(this.lastChar, 0, end); + return buf.toString("utf8", i, end); +} + +/* + * For UTF-8, a replacement character is added when ending on a partial + * character. + */ +function utf8End(this: Utf8Decoder, buf?: Buffer): string { + const r = buf && buf.length ? this.write(buf) : ""; + if (this.lastNeed) return r + "\ufffd"; + return r; +} + +function utf8Write( + this: Utf8Decoder | Base64Decoder, + buf: Buffer | string, +): string { + if (typeof buf === "string") { + return buf; + } + if (buf.length === 0) return ""; + let r; + let i; + // Because `TypedArray` is recognized as `ArrayBuffer` but in the reality, there are some fundamental difference. We would need to cast it properly + const normalizedBuffer: Buffer = isBufferType(buf) ? buf : Buffer.from(buf); + if (this.lastNeed) { + r = this.fillLast(normalizedBuffer); + if (r === undefined) return ""; + i = this.lastNeed; + this.lastNeed = 0; + } else { + i = 0; + } + if (i < buf.length) { + return r + ? r + this.text(normalizedBuffer, i) + : this.text(normalizedBuffer, i); + } + return r || ""; +} + +function base64Text(this: StringDecoderBase, buf: Buffer, i: number): string { + const n = (buf.length - i) % 3; + if (n === 0) return buf.toString("base64", i); + this.lastNeed = 3 - n; + this.lastTotal = 3; + if (n === 1) { + this.lastChar[0] = buf[buf.length - 1]; + } else { + this.lastChar[0] = buf[buf.length - 2]; + this.lastChar[1] = buf[buf.length - 1]; + } + return buf.toString("base64", i, buf.length - n); +} + +function base64End(this: Base64Decoder, buf?: Buffer): string { + const r = buf && buf.length ? this.write(buf) : ""; + if (this.lastNeed) { + return r + this.lastChar.toString("base64", 0, 3 - this.lastNeed); + } + return r; +} + +function simpleWrite( + this: StringDecoderBase, + buf: Buffer | string, +): string { + if (typeof buf === "string") { + return buf; + } + return buf.toString(this.encoding); +} + +function simpleEnd(this: GenericDecoder, buf?: Buffer): string { + return buf && buf.length ? this.write(buf) : ""; +} + +class StringDecoderBase { + public lastChar: Buffer; + public lastNeed = 0; + public lastTotal = 0; + constructor(public encoding: string, nb: number) { + this.lastChar = Buffer.allocUnsafe(nb); + } +} + +class Base64Decoder extends StringDecoderBase { + public end = base64End; + public fillLast = utf8FillLastIncomplete; + public text = base64Text; + public write = utf8Write; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 3); + } +} + +class GenericDecoder extends StringDecoderBase { + public end = simpleEnd; + public fillLast = undefined; + public text = utf8Text; + public write = simpleWrite; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 4); + } +} + +class Utf8Decoder extends StringDecoderBase { + public end = utf8End; + public fillLast = utf8FillLastComplete; + public text = utf8Text; + public write = utf8Write; + + constructor(encoding?: string) { + super(normalizeEncoding(encoding), 4); + } +} + +/* + * StringDecoder provides an interface for efficiently splitting a series of + * buffers into a series of JS strings without breaking apart multi-byte + * characters. + */ +export class StringDecoder { + public encoding: string; + public end: (buf?: Buffer) => string; + public fillLast: ((buf: Buffer) => string | undefined) | undefined; + public lastChar: Buffer; + public lastNeed: number; + public lastTotal: number; + public text: (buf: Buffer, n: number) => string; + public write: (buf: Buffer) => string; + + constructor(encoding?: string) { + const normalizedEncoding = normalizeEncoding(encoding); + let decoder: Utf8Decoder | Base64Decoder | GenericDecoder; + switch (normalizedEncoding) { + case "utf8": + decoder = new Utf8Decoder(encoding); + break; + case "base64": + decoder = new Base64Decoder(encoding); + break; + default: + decoder = new GenericDecoder(encoding); + } + this.encoding = decoder.encoding; + this.end = decoder.end; + this.fillLast = decoder.fillLast; + this.lastChar = decoder.lastChar; + this.lastNeed = decoder.lastNeed; + this.lastTotal = decoder.lastTotal; + this.text = decoder.text; + this.write = decoder.write; + } +} +// Allow calling StringDecoder() without new +const PStringDecoder = new Proxy(StringDecoder, { + apply(_target, thisArg, args) { + // @ts-ignore tedious to replicate types ... + return Object.assign(thisArg, new StringDecoder(...args)); + }, +}); + +export default { StringDecoder: PStringDecoder }; diff --git a/ext/node/polyfills/string_decoder_bench.js b/ext/node/polyfills/string_decoder_bench.js new file mode 100644 index 0000000000..4a89ddaaef --- /dev/null +++ b/ext/node/polyfills/string_decoder_bench.js @@ -0,0 +1,20 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { StringDecoder } from "internal:deno_node/polyfills/string_decoder.ts"; + +const stringDecoder = new StringDecoder(); + +// deno-fmt-ignore +const smallUint8 = new Uint8Array([226, 132, 149, 32, 226, 138, 134, 32, 226, 132, 149, 226, 130, 128, 32, 226, 138, 130, 32, 226, 132, 164, 32, 226, 138, 130, 32, 226, 132, 154, 32, 226, 138, 130, 32, 226, 132, 157, 32, 226, 138, 130, 32, 226, 132, 130, 44, 32, 226, 138, 165, 32, 60, 32, 97, 32, 226, 137, 160, 32, 98, 32, 226, 137, 161, 32, 99, 32, 226, 137, 164, 32, 100, 32, 226, 137, 170, 32, 226, 138, 164, 32, 226, 135, 146, 32, 40, 65, 32, 226, 135, 148, 32, 66, 41, 44]) +// deno-fmt-ignore +const bigUint8 = new Uint8Array([60, 33, 68, 79, 67, 84, 89, 80, 69, 32, 104, 116, 109, 108, 62, 10, 60, 104, 116, 109, 108, 32, 108, 97, 110, 103, 61, 34, 101, 110, 34, 62, 10, 10, 60, 104, 101, 97, 100, 62, 10, 32, 32, 32, 32, 60, 109, 101, 116, 97, 32, 99, 104, 97, 114, 115, 101, 116, 61, 34, 85, 84, 70, 45, 56, 34, 62, 10, 32, 32, 32, 32, 60, 116, 105, 116, 108, 101, 62, 104, 116, 116, 112, 98, 105, 110, 46, 111, 114, 103, 60, 47, 116, 105, 116, 108, 101, 62, 10, 32, 32, 32, 32, 60, 108, 105, 110, 107, 32, 104, 114, 101, 102, 61, 34, 104, 116, 116, 112, 115, 58, 47, 47, 102, 111, 110, 116, 115, 46, 103, 111, 111, 103, 108, 101, 97, 112, 105, 115, 46, 99, 111, 109, 47, 99, 115, 115, 63, 102, 97, 109, 105, 108, 121, 61, 79, 112, 101, 110, 43, 83, 97, 110, 115, 58, 52, 48, 48, 44, 55, 48, 48, 124, 83, 111, 117, 114, 99, 101, 43, 67, 111, 100, 101, 43, 80, 114, 111, 58, 51, 48, 48, 44, 54, 48, 48, 124, 84, 105, 116, 105, 108, 108, 105, 117, 109, 43, 87, 101, 98, 58, 52, 48, 48, 44, 54, 48, 48, 44, 55, 48, 48, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 114, 101, 108, 61, 34, 115, 116, 121, 108, 101, 115, 104, 101, 101, 116, 34, 62, 10, 32, 32, 32, 32, 60, 108, 105, 110, 107, 32, 114, 101, 108, 61, 34, 115, 116, 121, 108, 101, 115, 104, 101, 101, 116, 34, 32, 116, 121, 112, 101, 61, 34, 116, 101, 120, 116, 47, 99, 115, 115, 34, 32, 104, 114, 101, 102, 61, 34, 47, 102, 108, 97, 115, 103, 103, 101, 114, 95, 115, 116, 97, 116, 105, 99, 47, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 46, 99, 115, 115, 34, 62, 10, 32, 32, 32, 32, 60, 108, 105, 110, 107, 32, 114, 101, 108, 61, 34, 105, 99, 111, 110, 34, 32, 116, 121, 112, 101, 61, 34, 105, 109, 97, 103, 101, 47, 112, 110, 103, 34, 32, 104, 114, 101, 102, 61, 34, 47, 115, 116, 97, 116, 105, 99, 47, 102, 97, 118, 105, 99, 111, 110, 46, 105, 99, 111, 34, 32, 115, 105, 122, 101, 115, 61, 34, 54, 52, 120, 54, 52, 32, 51, 50, 120, 51, 50, 32, 49, 54, 120, 49, 54, 34, 32, 47, 62, 10, 32, 32, 32, 32, 60, 115, 116, 121, 108, 101, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 104, 116, 109, 108, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, 111, 120, 45, 115, 105, 122, 105, 110, 103, 58, 32, 98, 111, 114, 100, 101, 114, 45, 98, 111, 120, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 111, 118, 101, 114, 102, 108, 111, 119, 58, 32, 45, 109, 111, 122, 45, 115, 99, 114, 111, 108, 108, 98, 97, 114, 115, 45, 118, 101, 114, 116, 105, 99, 97, 108, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 111, 118, 101, 114, 102, 108, 111, 119, 45, 121, 58, 32, 115, 99, 114, 111, 108, 108, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 42, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 42, 58, 98, 101, 102, 111, 114, 101, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 42, 58, 97, 102, 116, 101, 114, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, 111, 120, 45, 115, 105, 122, 105, 110, 103, 58, 32, 105, 110, 104, 101, 114, 105, 116, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 98, 111, 100, 121, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 109, 97, 114, 103, 105, 110, 58, 32, 48, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 98, 97, 99, 107, 103, 114, 111, 117, 110, 100, 58, 32, 35, 102, 97, 102, 97, 102, 97, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 60, 47, 115, 116, 121, 108, 101, 62, 10, 60, 47, 104, 101, 97, 100, 62, 10, 10, 60, 98, 111, 100, 121, 62, 10, 32, 32, 32, 32, 60, 97, 32, 104, 114, 101, 102, 61, 34, 104, 116, 116, 112, 115, 58, 47, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 114, 101, 113, 117, 101, 115, 116, 115, 47, 104, 116, 116, 112, 98, 105, 110, 34, 32, 99, 108, 97, 115, 115, 61, 34, 103, 105, 116, 104, 117, 98, 45, 99, 111, 114, 110, 101, 114, 34, 32, 97, 114, 105, 97, 45, 108, 97, 98, 101, 108, 61, 34, 86, 105, 101, 119, 32, 115, 111, 117, 114, 99, 101, 32, 111, 110, 32, 71, 105, 116, 104, 117, 98, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 118, 103, 32, 119, 105, 100, 116, 104, 61, 34, 56, 48, 34, 32, 104, 101, 105, 103, 104, 116, 61, 34, 56, 48, 34, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 53, 48, 32, 50, 53, 48, 34, 32, 115, 116, 121, 108, 101, 61, 34, 102, 105, 108, 108, 58, 35, 49, 53, 49, 53, 49, 51, 59, 32, 99, 111, 108, 111, 114, 58, 35, 102, 102, 102, 59, 32, 112, 111, 115, 105, 116, 105, 111, 110, 58, 32, 97, 98, 115, 111, 108, 117, 116, 101, 59, 32, 116, 111, 112, 58, 32, 48, 59, 32, 98, 111, 114, 100, 101, 114, 58, 32, 48, 59, 32, 114, 105, 103, 104, 116, 58, 32, 48, 59, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 97, 114, 105, 97, 45, 104, 105, 100, 100, 101, 110, 61, 34, 116, 114, 117, 101, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 48, 44, 48, 32, 76, 49, 49, 53, 44, 49, 49, 53, 32, 76, 49, 51, 48, 44, 49, 49, 53, 32, 76, 49, 52, 50, 44, 49, 52, 50, 32, 76, 50, 53, 48, 44, 50, 53, 48, 32, 76, 50, 53, 48, 44, 48, 32, 90, 34, 62, 60, 47, 112, 97, 116, 104, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 50, 56, 46, 51, 44, 49, 48, 57, 46, 48, 32, 67, 49, 49, 51, 46, 56, 44, 57, 57, 46, 55, 32, 49, 49, 57, 46, 48, 44, 56, 57, 46, 54, 32, 49, 49, 57, 46, 48, 44, 56, 57, 46, 54, 32, 67, 49, 50, 50, 46, 48, 44, 56, 50, 46, 55, 32, 49, 50, 48, 46, 53, 44, 55, 56, 46, 54, 32, 49, 50, 48, 46, 53, 44, 55, 56, 46, 54, 32, 67, 49, 49, 57, 46, 50, 44, 55, 50, 46, 48, 32, 49, 50, 51, 46, 52, 44, 55, 54, 46, 51, 32, 49, 50, 51, 46, 52, 44, 55, 54, 46, 51, 32, 67, 49, 50, 55, 46, 51, 44, 56, 48, 46, 57, 32, 49, 50, 53, 46, 53, 44, 56, 55, 46, 51, 32, 49, 50, 53, 46, 53, 44, 56, 55, 46, 51, 32, 67, 49, 50, 50, 46, 57, 44, 57, 55, 46, 54, 32, 49, 51, 48, 46, 54, 44, 49, 48, 49, 46, 57, 32, 49, 51, 52, 46, 52, 44, 49, 48, 51, 46, 50, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 102, 105, 108, 108, 61, 34, 99, 117, 114, 114, 101, 110, 116, 67, 111, 108, 111, 114, 34, 32, 115, 116, 121, 108, 101, 61, 34, 116, 114, 97, 110, 115, 102, 111, 114, 109, 45, 111, 114, 105, 103, 105, 110, 58, 32, 49, 51, 48, 112, 120, 32, 49, 48, 54, 112, 120, 59, 34, 32, 99, 108, 97, 115, 115, 61, 34, 111, 99, 116, 111, 45, 97, 114, 109, 34, 62, 60, 47, 112, 97, 116, 104, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 49, 53, 46, 48, 44, 49, 49, 53, 46, 48, 32, 67, 49, 49, 52, 46, 57, 44, 49, 49, 53, 46, 49, 32, 49, 49, 56, 46, 55, 44, 49, 49, 54, 46, 53, 32, 49, 49, 57, 46, 56, 44, 49, 49, 53, 46, 52, 32, 76, 49, 51, 51, 46, 55, 44, 49, 48, 49, 46, 54, 32, 67, 49, 51, 54, 46, 57, 44, 57, 57, 46, 50, 32, 49, 51, 57, 46, 57, 44, 57, 56, 46, 52, 32, 49, 52, 50, 46, 50, 44, 57, 56, 46, 54, 32, 67, 49, 51, 51, 46, 56, 44, 56, 56, 46, 48, 32, 49, 50, 55, 46, 53, 44, 55, 52, 46, 52, 32, 49, 52, 51, 46, 56, 44, 53, 56, 46, 48, 32, 67, 49, 52, 56, 46, 53, 44, 53, 51, 46, 52, 32, 49, 53, 52, 46, 48, 44, 53, 49, 46, 50, 32, 49, 53, 57, 46, 55, 44, 53, 49, 46, 48, 32, 67, 49, 54, 48, 46, 51, 44, 52, 57, 46, 52, 32, 49, 54, 51, 46, 50, 44, 52, 51, 46, 54, 32, 49, 55, 49, 46, 52, 44, 52, 48, 46, 49, 32, 67, 49, 55, 49, 46, 52, 44, 52, 48, 46, 49, 32, 49, 55, 54, 46, 49, 44, 52, 50, 46, 53, 32, 49, 55, 56, 46, 56, 44, 53, 54, 46, 50, 32, 67, 49, 56, 51, 46, 49, 44, 53, 56, 46, 54, 32, 49, 56, 55, 46, 50, 44, 54, 49, 46, 56, 32, 49, 57, 48, 46, 57, 44, 54, 53, 46, 52, 32, 67, 49, 57, 52, 46, 53, 44, 54, 57, 46, 48, 32, 49, 57, 55, 46, 55, 44, 55, 51, 46, 50, 32, 50, 48, 48, 46, 49, 44, 55, 55, 46, 54, 32, 67, 50, 49, 51, 46, 56, 44, 56, 48, 46, 50, 32, 50, 49, 54, 46, 51, 44, 56, 52, 46, 57, 32, 50, 49, 54, 46, 51, 44, 56, 52, 46, 57, 32, 67, 50, 49, 50, 46, 55, 44, 57, 51, 46, 49, 32, 50, 48, 54, 46, 57, 44, 57, 54, 46, 48, 32, 50, 48, 53, 46, 52, 44, 57, 54, 46, 54, 32, 67, 50, 48, 53, 46, 49, 44, 49, 48, 50, 46, 52, 32, 50, 48, 51, 46, 48, 44, 49, 48, 55, 46, 56, 32, 49, 57, 56, 46, 51, 44, 49, 49, 50, 46, 53, 32, 67, 49, 56, 49, 46, 57, 44, 49, 50, 56, 46, 57, 32, 49, 54, 56, 46, 51, 44, 49, 50, 50, 46, 53, 32, 49, 53, 55, 46, 55, 44, 49, 49, 52, 46, 49, 32, 67, 49, 53, 55, 46, 57, 44, 49, 49, 54, 46, 57, 32, 49, 53, 54, 46, 55, 44, 49, 50, 48, 46, 57, 32, 49, 53, 50, 46, 55, 44, 49, 50, 52, 46, 57, 32, 76, 49, 52, 49, 46, 48, 44, 49, 51, 54, 46, 53, 32, 67, 49, 51, 57, 46, 56, 44, 49, 51, 55, 46, 55, 32, 49, 52, 49, 46, 54, 44, 49, 52, 49, 46, 57, 32, 49, 52, 49, 46, 56, 44, 49, 52, 49, 46, 56, 32, 90, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 102, 105, 108, 108, 61, 34, 99, 117, 114, 114, 101, 110, 116, 67, 111, 108, 111, 114, 34, 32, 99, 108, 97, 115, 115, 61, 34, 111, 99, 116, 111, 45, 98, 111, 100, 121, 34, 62, 60, 47, 112, 97, 116, 104, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 118, 103, 62, 10, 32, 32, 32, 32, 60, 47, 97, 62, 10, 32, 32, 32, 32, 60, 115, 118, 103, 32, 120, 109, 108, 110, 115, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 50, 48, 48, 48, 47, 115, 118, 103, 34, 32, 120, 109, 108, 110, 115, 58, 120, 108, 105, 110, 107, 61, 34, 104, 116, 116, 112, 58, 47, 47, 119, 119, 119, 46, 119, 51, 46, 111, 114, 103, 47, 49, 57, 57, 57, 47, 120, 108, 105, 110, 107, 34, 32, 115, 116, 121, 108, 101, 61, 34, 112, 111, 115, 105, 116, 105, 111, 110, 58, 97, 98, 115, 111, 108, 117, 116, 101, 59, 119, 105, 100, 116, 104, 58, 48, 59, 104, 101, 105, 103, 104, 116, 58, 48, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 101, 102, 115, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 121, 109, 98, 111, 108, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 48, 32, 50, 48, 34, 32, 105, 100, 61, 34, 117, 110, 108, 111, 99, 107, 101, 100, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 53, 46, 56, 32, 56, 72, 49, 52, 86, 53, 46, 54, 67, 49, 52, 32, 50, 46, 55, 48, 51, 32, 49, 50, 46, 54, 54, 53, 32, 49, 32, 49, 48, 32, 49, 32, 55, 46, 51, 51, 52, 32, 49, 32, 54, 32, 50, 46, 55, 48, 51, 32, 54, 32, 53, 46, 54, 86, 54, 104, 50, 118, 45, 46, 56, 48, 49, 67, 56, 32, 51, 46, 55, 53, 52, 32, 56, 46, 55, 57, 55, 32, 51, 32, 49, 48, 32, 51, 99, 49, 46, 50, 48, 51, 32, 48, 32, 50, 32, 46, 55, 53, 52, 32, 50, 32, 50, 46, 49, 57, 57, 86, 56, 72, 52, 99, 45, 46, 53, 53, 51, 32, 48, 45, 49, 32, 46, 54, 52, 54, 45, 49, 32, 49, 46, 49, 57, 57, 86, 49, 55, 99, 48, 32, 46, 53, 52, 57, 46, 52, 50, 56, 32, 49, 46, 49, 51, 57, 46, 57, 53, 49, 32, 49, 46, 51, 48, 55, 108, 49, 46, 49, 57, 55, 46, 51, 56, 55, 67, 53, 46, 54, 55, 50, 32, 49, 56, 46, 56, 54, 49, 32, 54, 46, 53, 53, 32, 49, 57, 32, 55, 46, 49, 32, 49, 57, 104, 53, 46, 56, 99, 46, 53, 52, 57, 32, 48, 32, 49, 46, 52, 50, 56, 45, 46, 49, 51, 57, 32, 49, 46, 57, 53, 49, 45, 46, 51, 48, 55, 108, 49, 46, 49, 57, 54, 45, 46, 51, 56, 55, 99, 46, 53, 50, 52, 45, 46, 49, 54, 55, 46, 57, 53, 51, 45, 46, 55, 53, 55, 46, 57, 53, 51, 45, 49, 46, 51, 48, 54, 86, 57, 46, 49, 57, 57, 67, 49, 55, 32, 56, 46, 54, 52, 54, 32, 49, 54, 46, 51, 53, 50, 32, 56, 32, 49, 53, 46, 56, 32, 56, 122, 34, 62, 60, 47, 112, 97, 116, 104, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 121, 109, 98, 111, 108, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 121, 109, 98, 111, 108, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 48, 32, 50, 48, 34, 32, 105, 100, 61, 34, 108, 111, 99, 107, 101, 100, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 53, 46, 56, 32, 56, 72, 49, 52, 86, 53, 46, 54, 67, 49, 52, 32, 50, 46, 55, 48, 51, 32, 49, 50, 46, 54, 54, 53, 32, 49, 32, 49, 48, 32, 49, 32, 55, 46, 51, 51, 52, 32, 49, 32, 54, 32, 50, 46, 55, 48, 51, 32, 54, 32, 53, 46, 54, 86, 56, 72, 52, 99, 45, 46, 53, 53, 51, 32, 48, 45, 49, 32, 46, 54, 52, 54, 45, 49, 32, 49, 46, 49, 57, 57, 86, 49, 55, 99, 48, 32, 46, 53, 52, 57, 46, 52, 50, 56, 32, 49, 46, 49, 51, 57, 46, 57, 53, 49, 32, 49, 46, 51, 48, 55, 108, 49, 46, 49, 57, 55, 46, 51, 56, 55, 67, 53, 46, 54, 55, 50, 32, 49, 56, 46, 56, 54, 49, 32, 54, 46, 53, 53, 32, 49, 57, 32, 55, 46, 49, 32, 49, 57, 104, 53, 46, 56, 99, 46, 53, 52, 57, 32, 48, 32, 49, 46, 52, 50, 56, 45, 46, 49, 51, 57, 32, 49, 46, 57, 53, 49, 45, 46, 51, 48, 55, 108, 49, 46, 49, 57, 54, 45, 46, 51, 56, 55, 99, 46, 53, 50, 52, 45, 46, 49, 54, 55, 46, 57, 53, 51, 45, 46, 55, 53, 55, 46, 57, 53, 51, 45, 49, 46, 51, 48, 54, 86, 57, 46, 49, 57, 57, 67, 49, 55, 32, 56, 46, 54, 52, 54, 32, 49, 54, 46, 51, 53, 50, 32, 56, 32, 49, 53, 46, 56, 32, 56, 122, 77, 49, 50, 32, 56, 72, 56, 86, 53, 46, 49, 57, 57, 67, 56, 32, 51, 46, 55, 53, 52, 32, 56, 46, 55, 57, 55, 32, 51, 32, 49, 48, 32, 51, 99, 49, 46, 50, 48, 51, 32, 48, 32, 50, 32, 46, 55, 53, 52, 32, 50, 32, 50, 46, 49, 57, 57, 86, 56, 122, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 121, 109, 98, 111, 108, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 121, 109, 98, 111, 108, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 48, 32, 50, 48, 34, 32, 105, 100, 61, 34, 99, 108, 111, 115, 101, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 52, 46, 51, 52, 56, 32, 49, 52, 46, 56, 52, 57, 99, 45, 46, 52, 54, 57, 46, 52, 54, 57, 45, 49, 46, 50, 50, 57, 46, 52, 54, 57, 45, 49, 46, 54, 57, 55, 32, 48, 76, 49, 48, 32, 49, 49, 46, 56, 49, 57, 108, 45, 50, 46, 54, 53, 49, 32, 51, 46, 48, 50, 57, 99, 45, 46, 52, 54, 57, 46, 52, 54, 57, 45, 49, 46, 50, 50, 57, 46, 52, 54, 57, 45, 49, 46, 54, 57, 55, 32, 48, 45, 46, 52, 54, 57, 45, 46, 52, 54, 57, 45, 46, 52, 54, 57, 45, 49, 46, 50, 50, 57, 32, 48, 45, 49, 46, 54, 57, 55, 108, 50, 46, 55, 53, 56, 45, 51, 46, 49, 53, 45, 50, 46, 55, 53, 57, 45, 51, 46, 49, 53, 50, 99, 45, 46, 52, 54, 57, 45, 46, 52, 54, 57, 45, 46, 52, 54, 57, 45, 49, 46, 50, 50, 56, 32, 48, 45, 49, 46, 54, 57, 55, 46, 52, 54, 57, 45, 46, 52, 54, 57, 32, 49, 46, 50, 50, 56, 45, 46, 52, 54, 57, 32, 49, 46, 54, 57, 55, 32, 48, 76, 49, 48, 32, 56, 46, 49, 56, 51, 108, 50, 46, 54, 53, 49, 45, 51, 46, 48, 51, 49, 99, 46, 52, 54, 57, 45, 46, 52, 54, 57, 32, 49, 46, 50, 50, 56, 45, 46, 52, 54, 57, 32, 49, 46, 54, 57, 55, 32, 48, 32, 46, 52, 54, 57, 46, 52, 54, 57, 46, 52, 54, 57, 32, 49, 46, 50, 50, 57, 32, 48, 32, 49, 46, 54, 57, 55, 108, 45, 50, 46, 55, 53, 56, 32, 51, 46, 49, 53, 50, 32, 50, 46, 55, 53, 56, 32, 51, 46, 49, 53, 99, 46, 52, 54, 57, 46, 52, 54, 57, 46, 52, 54, 57, 32, 49, 46, 50, 50, 57, 32, 48, 32, 49, 46, 54, 57, 56, 122, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 121, 109, 98, 111, 108, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 121, 109, 98, 111, 108, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 48, 32, 50, 48, 34, 32, 105, 100, 61, 34, 108, 97, 114, 103, 101, 45, 97, 114, 114, 111, 119, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 51, 46, 50, 53, 32, 49, 48, 76, 54, 46, 49, 48, 57, 32, 50, 46, 53, 56, 99, 45, 46, 50, 54, 56, 45, 46, 50, 55, 45, 46, 50, 54, 56, 45, 46, 55, 48, 55, 32, 48, 45, 46, 57, 55, 57, 46, 50, 54, 56, 45, 46, 50, 55, 46, 55, 48, 49, 45, 46, 50, 55, 46, 57, 54, 57, 32, 48, 108, 55, 46, 56, 51, 32, 55, 46, 57, 48, 56, 99, 46, 50, 54, 56, 46, 50, 55, 49, 46, 50, 54, 56, 46, 55, 48, 57, 32, 48, 32, 46, 57, 55, 57, 108, 45, 55, 46, 56, 51, 32, 55, 46, 57, 48, 56, 99, 45, 46, 50, 54, 56, 46, 50, 55, 49, 45, 46, 55, 48, 49, 46, 50, 55, 45, 46, 57, 54, 57, 32, 48, 45, 46, 50, 54, 56, 45, 46, 50, 54, 57, 45, 46, 50, 54, 56, 45, 46, 55, 48, 55, 32, 48, 45, 46, 57, 55, 57, 76, 49, 51, 46, 50, 53, 32, 49, 48, 122, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 121, 109, 98, 111, 108, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 121, 109, 98, 111, 108, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 48, 32, 50, 48, 34, 32, 105, 100, 61, 34, 108, 97, 114, 103, 101, 45, 97, 114, 114, 111, 119, 45, 100, 111, 119, 110, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 55, 46, 52, 49, 56, 32, 54, 46, 49, 48, 57, 99, 46, 50, 55, 50, 45, 46, 50, 54, 56, 46, 55, 48, 57, 45, 46, 50, 54, 56, 46, 57, 55, 57, 32, 48, 115, 46, 50, 55, 49, 46, 55, 48, 49, 32, 48, 32, 46, 57, 54, 57, 108, 45, 55, 46, 57, 48, 56, 32, 55, 46, 56, 51, 99, 45, 46, 50, 55, 46, 50, 54, 56, 45, 46, 55, 48, 55, 46, 50, 54, 56, 45, 46, 57, 55, 57, 32, 48, 108, 45, 55, 46, 57, 48, 56, 45, 55, 46, 56, 51, 99, 45, 46, 50, 55, 45, 46, 50, 54, 56, 45, 46, 50, 55, 45, 46, 55, 48, 49, 32, 48, 45, 46, 57, 54, 57, 46, 50, 55, 49, 45, 46, 50, 54, 56, 46, 55, 48, 57, 45, 46, 50, 54, 56, 46, 57, 55, 57, 32, 48, 76, 49, 48, 32, 49, 51, 46, 50, 53, 108, 55, 46, 52, 49, 56, 45, 55, 46, 49, 52, 49, 122, 34, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 121, 109, 98, 111, 108, 62, 10, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 121, 109, 98, 111, 108, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 52, 32, 50, 52, 34, 32, 105, 100, 61, 34, 106, 117, 109, 112, 45, 116, 111, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 57, 32, 55, 118, 52, 72, 53, 46, 56, 51, 108, 51, 46, 53, 56, 45, 51, 46, 53, 57, 76, 56, 32, 54, 108, 45, 54, 32, 54, 32, 54, 32, 54, 32, 49, 46, 52, 49, 45, 49, 46, 52, 49, 76, 53, 46, 56, 51, 32, 49, 51, 72, 50, 49, 86, 55, 122, 34, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 121, 109, 98, 111, 108, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 121, 109, 98, 111, 108, 32, 118, 105, 101, 119, 66, 111, 120, 61, 34, 48, 32, 48, 32, 50, 52, 32, 50, 52, 34, 32, 105, 100, 61, 34, 101, 120, 112, 97, 110, 100, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 97, 116, 104, 32, 100, 61, 34, 77, 49, 48, 32, 49, 56, 104, 52, 118, 45, 50, 104, 45, 52, 118, 50, 122, 77, 51, 32, 54, 118, 50, 104, 49, 56, 86, 54, 72, 51, 122, 109, 51, 32, 55, 104, 49, 50, 118, 45, 50, 72, 54, 118, 50, 122, 34, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 121, 109, 98, 111, 108, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 101, 102, 115, 62, 10, 32, 32, 32, 32, 60, 47, 115, 118, 103, 62, 10, 10, 10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 105, 100, 61, 34, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 100, 97, 116, 97, 45, 114, 101, 97, 99, 116, 114, 111, 111, 116, 61, 34, 34, 32, 99, 108, 97, 115, 115, 61, 34, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 105, 110, 102, 111, 114, 109, 97, 116, 105, 111, 110, 45, 99, 111, 110, 116, 97, 105, 110, 101, 114, 32, 119, 114, 97, 112, 112, 101, 114, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 101, 99, 116, 105, 111, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 108, 111, 99, 107, 32, 99, 111, 108, 45, 49, 50, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 105, 110, 102, 111, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 104, 103, 114, 111, 117, 112, 32, 99, 108, 97, 115, 115, 61, 34, 109, 97, 105, 110, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 104, 50, 32, 99, 108, 97, 115, 115, 61, 34, 116, 105, 116, 108, 101, 34, 62, 104, 116, 116, 112, 98, 105, 110, 46, 111, 114, 103, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 109, 97, 108, 108, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 114, 101, 32, 99, 108, 97, 115, 115, 61, 34, 118, 101, 114, 115, 105, 111, 110, 34, 62, 48, 46, 57, 46, 50, 60, 47, 112, 114, 101, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 109, 97, 108, 108, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 104, 50, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 114, 101, 32, 99, 108, 97, 115, 115, 61, 34, 98, 97, 115, 101, 45, 117, 114, 108, 34, 62, 91, 32, 66, 97, 115, 101, 32, 85, 82, 76, 58, 32, 104, 116, 116, 112, 98, 105, 110, 46, 111, 114, 103, 47, 32, 93, 60, 47, 112, 114, 101, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 104, 103, 114, 111, 117, 112, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 100, 101, 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 109, 97, 114, 107, 100, 111, 119, 110, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 112, 62, 65, 32, 115, 105, 109, 112, 108, 101, 32, 72, 84, 84, 80, 32, 82, 101, 113, 117, 101, 115, 116, 32, 38, 97, 109, 112, 59, 32, 82, 101, 115, 112, 111, 110, 115, 101, 32, 83, 101, 114, 118, 105, 99, 101, 46, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 98, 114, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 98, 114, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 98, 62, 82, 117, 110, 32, 108, 111, 99, 97, 108, 108, 121, 58, 32, 60, 47, 98, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 99, 111, 100, 101, 62, 36, 32, 100, 111, 99, 107, 101, 114, 32, 114, 117, 110, 32, 45, 112, 32, 56, 48, 58, 56, 48, 32, 107, 101, 110, 110, 101, 116, 104, 114, 101, 105, 116, 122, 47, 104, 116, 116, 112, 98, 105, 110, 60, 47, 99, 111, 100, 101, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 112, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 97, 32, 104, 114, 101, 102, 61, 34, 104, 116, 116, 112, 115, 58, 47, 47, 107, 101, 110, 110, 101, 116, 104, 114, 101, 105, 116, 122, 46, 111, 114, 103, 34, 32, 116, 97, 114, 103, 101, 116, 61, 34, 95, 98, 108, 97, 110, 107, 34, 62, 116, 104, 101, 32, 100, 101, 118, 101, 108, 111, 112, 101, 114, 32, 45, 32, 87, 101, 98, 115, 105, 116, 101, 60, 47, 97, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 97, 32, 104, 114, 101, 102, 61, 34, 109, 97, 105, 108, 116, 111, 58, 109, 101, 64, 107, 101, 110, 110, 101, 116, 104, 114, 101, 105, 116, 122, 46, 111, 114, 103, 34, 62, 83, 101, 110, 100, 32, 101, 109, 97, 105, 108, 32, 116, 111, 32, 116, 104, 101, 32, 100, 101, 118, 101, 108, 111, 112, 101, 114, 60, 47, 97, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 33, 45, 45, 32, 65, 68, 68, 83, 32, 84, 72, 69, 32, 76, 79, 65, 68, 69, 82, 32, 83, 80, 73, 78, 78, 69, 82, 32, 45, 45, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 108, 111, 97, 100, 105, 110, 103, 45, 99, 111, 110, 116, 97, 105, 110, 101, 114, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 108, 111, 97, 100, 105, 110, 103, 34, 62, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 101, 99, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 10, 10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 39, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 39, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 119, 114, 97, 112, 112, 101, 114, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 101, 99, 116, 105, 111, 110, 32, 99, 108, 97, 115, 115, 61, 34, 99, 108, 101, 97, 114, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 112, 97, 110, 32, 115, 116, 121, 108, 101, 61, 34, 102, 108, 111, 97, 116, 58, 32, 114, 105, 103, 104, 116, 59, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 91, 80, 111, 119, 101, 114, 101, 100, 32, 98, 121, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 97, 32, 116, 97, 114, 103, 101, 116, 61, 34, 95, 98, 108, 97, 110, 107, 34, 32, 104, 114, 101, 102, 61, 34, 104, 116, 116, 112, 115, 58, 47, 47, 103, 105, 116, 104, 117, 98, 46, 99, 111, 109, 47, 114, 111, 99, 104, 97, 99, 98, 114, 117, 110, 111, 47, 102, 108, 97, 115, 103, 103, 101, 114, 34, 62, 70, 108, 97, 115, 103, 103, 101, 114, 60, 47, 97, 62, 93, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 98, 114, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 112, 97, 110, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 101, 99, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 10, 10, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 115, 114, 99, 61, 34, 47, 102, 108, 97, 115, 103, 103, 101, 114, 95, 115, 116, 97, 116, 105, 99, 47, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 45, 98, 117, 110, 100, 108, 101, 46, 106, 115, 34, 62, 32, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 115, 114, 99, 61, 34, 47, 102, 108, 97, 115, 103, 103, 101, 114, 95, 115, 116, 97, 116, 105, 99, 47, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 45, 115, 116, 97, 110, 100, 97, 108, 111, 110, 101, 45, 112, 114, 101, 115, 101, 116, 46, 106, 115, 34, 62, 32, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 32, 115, 114, 99, 61, 39, 47, 102, 108, 97, 115, 103, 103, 101, 114, 95, 115, 116, 97, 116, 105, 99, 47, 108, 105, 98, 47, 106, 113, 117, 101, 114, 121, 46, 109, 105, 110, 46, 106, 115, 39, 32, 116, 121, 112, 101, 61, 39, 116, 101, 120, 116, 47, 106, 97, 118, 97, 115, 99, 114, 105, 112, 116, 39, 62, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 32, 32, 32, 32, 60, 115, 99, 114, 105, 112, 116, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 119, 105, 110, 100, 111, 119, 46, 111, 110, 108, 111, 97, 100, 32, 61, 32, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 102, 101, 116, 99, 104, 40, 34, 47, 115, 112, 101, 99, 46, 106, 115, 111, 110, 34, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 116, 104, 101, 110, 40, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 114, 101, 115, 112, 111, 110, 115, 101, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 114, 101, 115, 112, 111, 110, 115, 101, 46, 106, 115, 111, 110, 40, 41, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 46, 116, 104, 101, 110, 40, 102, 117, 110, 99, 116, 105, 111, 110, 32, 40, 106, 115, 111, 110, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 99, 117, 114, 114, 101, 110, 116, 95, 112, 114, 111, 116, 111, 99, 111, 108, 32, 61, 32, 119, 105, 110, 100, 111, 119, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 112, 114, 111, 116, 111, 99, 111, 108, 46, 115, 108, 105, 99, 101, 40, 48, 44, 32, 45, 49, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 105, 102, 32, 40, 106, 115, 111, 110, 46, 115, 99, 104, 101, 109, 101, 115, 91, 48, 93, 32, 33, 61, 32, 99, 117, 114, 114, 101, 110, 116, 95, 112, 114, 111, 116, 111, 99, 111, 108, 41, 32, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 83, 119, 105, 116, 99, 104, 101, 115, 32, 115, 99, 104, 101, 109, 101, 32, 116, 111, 32, 116, 104, 101, 32, 99, 117, 114, 114, 101, 110, 116, 32, 105, 110, 32, 117, 115, 101, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 114, 32, 111, 116, 104, 101, 114, 95, 112, 114, 111, 116, 111, 99, 111, 108, 32, 61, 32, 106, 115, 111, 110, 46, 115, 99, 104, 101, 109, 101, 115, 91, 48, 93, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 106, 115, 111, 110, 46, 115, 99, 104, 101, 109, 101, 115, 91, 48, 93, 32, 61, 32, 99, 117, 114, 114, 101, 110, 116, 95, 112, 114, 111, 116, 111, 99, 111, 108, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 106, 115, 111, 110, 46, 115, 99, 104, 101, 109, 101, 115, 91, 49, 93, 32, 61, 32, 111, 116, 104, 101, 114, 95, 112, 114, 111, 116, 111, 99, 111, 108, 59, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 125, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 106, 115, 111, 110, 46, 104, 111, 115, 116, 32, 61, 32, 119, 105, 110, 100, 111, 119, 46, 108, 111, 99, 97, 116, 105, 111, 110, 46, 104, 111, 115, 116, 59, 32, 32, 47, 47, 32, 115, 101, 116, 115, 32, 116, 104, 101, 32, 99, 117, 114, 114, 101, 110, 116, 32, 104, 111, 115, 116, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 99, 111, 110, 115, 116, 32, 117, 105, 32, 61, 32, 83, 119, 97, 103, 103, 101, 114, 85, 73, 66, 117, 110, 100, 108, 101, 40, 123, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 115, 112, 101, 99, 58, 32, 106, 115, 111, 110, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 118, 97, 108, 105, 100, 97, 116, 111, 114, 85, 114, 108, 58, 32, 110, 117, 108, 108, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 100, 111, 109, 95, 105, 100, 58, 32, 39, 35, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 39, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 100, 101, 101, 112, 76, 105, 110, 107, 105, 110, 103, 58, 32, 116, 114, 117, 101, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 106, 115, 111, 110, 69, 100, 105, 116, 111, 114, 58, 32, 116, 114, 117, 101, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 100, 111, 99, 69, 120, 112, 97, 110, 115, 105, 111, 110, 58, 32, 34, 110, 111, 110, 101, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 97, 112, 105, 115, 83, 111, 114, 116, 101, 114, 58, 32, 34, 97, 108, 112, 104, 97, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 111, 112, 101, 114, 97, 116, 105, 111, 110, 115, 83, 111, 114, 116, 101, 114, 58, 32, 34, 97, 108, 112, 104, 97, 34, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 112, 114, 101, 115, 101, 116, 115, 58, 32, 91, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 83, 119, 97, 103, 103, 101, 114, 85, 73, 66, 117, 110, 100, 108, 101, 46, 112, 114, 101, 115, 101, 116, 115, 46, 97, 112, 105, 115, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 121, 97, 121, 32, 69, 83, 54, 32, 109, 111, 100, 117, 108, 101, 115, 32, 226, 134, 152, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 65, 114, 114, 97, 121, 46, 105, 115, 65, 114, 114, 97, 121, 40, 83, 119, 97, 103, 103, 101, 114, 85, 73, 83, 116, 97, 110, 100, 97, 108, 111, 110, 101, 80, 114, 101, 115, 101, 116, 41, 32, 63, 32, 83, 119, 97, 103, 103, 101, 114, 85, 73, 83, 116, 97, 110, 100, 97, 108, 111, 110, 101, 80, 114, 101, 115, 101, 116, 32, 58, 32, 83, 119, 97, 103, 103, 101, 114, 85, 73, 83, 116, 97, 110, 100, 97, 108, 111, 110, 101, 80, 114, 101, 115, 101, 116, 46, 100, 101, 102, 97, 117, 108, 116, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 93, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 112, 108, 117, 103, 105, 110, 115, 58, 32, 91, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 83, 119, 97, 103, 103, 101, 114, 85, 73, 66, 117, 110, 100, 108, 101, 46, 112, 108, 117, 103, 105, 110, 115, 46, 68, 111, 119, 110, 108, 111, 97, 100, 85, 114, 108, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 93, 44, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 108, 97, 121, 111, 117, 116, 58, 32, 34, 83, 116, 97, 110, 100, 97, 108, 111, 110, 101, 76, 97, 121, 111, 117, 116, 34, 32, 32, 47, 47, 32, 117, 110, 99, 111, 109, 109, 101, 110, 116, 32, 116, 111, 32, 101, 110, 97, 98, 108, 101, 32, 116, 104, 101, 32, 103, 114, 101, 101, 110, 32, 116, 111, 112, 32, 104, 101, 97, 100, 101, 114, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 41, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 119, 105, 110, 100, 111, 119, 46, 117, 105, 32, 61, 32, 117, 105, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 117, 110, 99, 111, 109, 109, 101, 110, 116, 32, 116, 111, 32, 114, 101, 110, 97, 109, 101, 32, 116, 104, 101, 32, 116, 111, 112, 32, 98, 114, 97, 110, 100, 32, 105, 102, 32, 108, 97, 121, 111, 117, 116, 32, 105, 115, 32, 101, 110, 97, 98, 108, 101, 100, 10, 32, 32, 32, 32, 32, 32, 32, 32, 47, 47, 32, 36, 40, 34, 46, 116, 111, 112, 98, 97, 114, 45, 119, 114, 97, 112, 112, 101, 114, 32, 46, 108, 105, 110, 107, 32, 115, 112, 97, 110, 34, 41, 46, 114, 101, 112, 108, 97, 99, 101, 87, 105, 116, 104, 40, 34, 60, 115, 112, 97, 110, 62, 104, 116, 116, 112, 98, 105, 110, 60, 47, 115, 112, 97, 110, 62, 34, 41, 59, 10, 32, 32, 32, 32, 32, 32, 32, 32, 125, 41, 10, 32, 32, 32, 32, 125, 41, 10, 125, 10, 32, 32, 32, 32, 60, 47, 115, 99, 114, 105, 112, 116, 62, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 39, 115, 119, 97, 103, 103, 101, 114, 45, 117, 105, 39, 62, 10, 32, 32, 32, 32, 60, 100, 105, 118, 32, 99, 108, 97, 115, 115, 61, 34, 119, 114, 97, 112, 112, 101, 114, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 115, 101, 99, 116, 105, 111, 110, 32, 99, 108, 97, 115, 115, 61, 34, 98, 108, 111, 99, 107, 32, 99, 111, 108, 45, 49, 50, 32, 98, 108, 111, 99, 107, 45, 100, 101, 115, 107, 116, 111, 112, 32, 99, 111, 108, 45, 49, 50, 45, 100, 101, 115, 107, 116, 111, 112, 34, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 100, 105, 118, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 104, 50, 62, 79, 116, 104, 101, 114, 32, 85, 116, 105, 108, 105, 116, 105, 101, 115, 60, 47, 104, 50, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 117, 108, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 108, 105, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 97, 32, 104, 114, 101, 102, 61, 34, 47, 102, 111, 114, 109, 115, 47, 112, 111, 115, 116, 34, 62, 72, 84, 77, 76, 32, 102, 111, 114, 109, 60, 47, 97, 62, 32, 116, 104, 97, 116, 32, 112, 111, 115, 116, 115, 32, 116, 111, 32, 47, 112, 111, 115, 116, 32, 47, 102, 111, 114, 109, 115, 47, 112, 111, 115, 116, 60, 47, 108, 105, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 117, 108, 62, 10, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 98, 114, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 98, 114, 32, 47, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 32, 32, 32, 32, 32, 32, 32, 32, 60, 47, 115, 101, 99, 116, 105, 111, 110, 62, 10, 32, 32, 32, 32, 60, 47, 100, 105, 118, 62, 10, 60, 47, 100, 105, 118, 62, 10, 60, 47, 98, 111, 100, 121, 62, 10, 10, 60, 47, 104, 116, 109, 108, 62]) + +Deno.bench("smallUint8", () => { + stringDecoder.write(smallUint8); + stringDecoder.end(); +}); + +Deno.bench("bigUint8", () => { + stringDecoder.write(bigUint8); + stringDecoder.end(); +}); diff --git a/ext/node/polyfills/sys.ts b/ext/node/polyfills/sys.ts new file mode 100644 index 0000000000..e6297ca162 --- /dev/null +++ b/ext/node/polyfills/sys.ts @@ -0,0 +1,3 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +export * from "internal:deno_node/polyfills/util.ts"; +export { default } from "internal:deno_node/polyfills/util.ts"; diff --git a/ext/node/polyfills/timers.ts b/ext/node/polyfills/timers.ts new file mode 100644 index 0000000000..57ff71a382 --- /dev/null +++ b/ext/node/polyfills/timers.ts @@ -0,0 +1,65 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { + setUnrefTimeout, + Timeout, +} from "internal:deno_node/polyfills/internal/timers.mjs"; +import { validateFunction } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; +export { setUnrefTimeout } from "internal:deno_node/polyfills/internal/timers.mjs"; + +const clearTimeout_ = globalThis.clearTimeout; +const clearInterval_ = globalThis.clearInterval; + +export function setTimeout( + callback: (...args: unknown[]) => void, + timeout?: number, + ...args: unknown[] +) { + validateFunction(callback, "callback"); + return new Timeout(callback, timeout, args, false, true); +} + +Object.defineProperty(setTimeout, promisify.custom, { + value: (timeout: number, ...args: unknown[]) => { + return new Promise((cb) => setTimeout(cb, timeout, ...args)); + }, + enumerable: true, +}); +export function clearTimeout(timeout?: Timeout | number) { + if (timeout == null) { + return; + } + clearTimeout_(+timeout); +} +export function setInterval( + callback: (...args: unknown[]) => void, + timeout?: number, + ...args: unknown[] +) { + validateFunction(callback, "callback"); + return new Timeout(callback, timeout, args, true, true); +} +export function clearInterval(timeout?: Timeout | number | string) { + if (timeout == null) { + return; + } + clearInterval_(+timeout); +} +// TODO(bartlomieju): implement the 'NodeJS.Immediate' versions of the timers. +// https://github.com/DefinitelyTyped/DefinitelyTyped/blob/1163ead296d84e7a3c80d71e7c81ecbd1a130e9a/types/node/v12/globals.d.ts#L1120-L1131 +export const setImmediate = ( + cb: (...args: unknown[]) => void, + ...args: unknown[] +): Timeout => setTimeout(cb, 0, ...args); +export const clearImmediate = clearTimeout; + +export default { + setTimeout, + clearTimeout, + setInterval, + clearInterval, + setImmediate, + setUnrefTimeout, + clearImmediate, +}; diff --git a/ext/node/polyfills/timers/promises.ts b/ext/node/polyfills/timers/promises.ts new file mode 100644 index 0000000000..4700f43ec0 --- /dev/null +++ b/ext/node/polyfills/timers/promises.ts @@ -0,0 +1,13 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { promisify } from "internal:deno_node/polyfills/util.ts"; +import timers from "internal:deno_node/polyfills/timers.ts"; + +export const setTimeout = promisify(timers.setTimeout), + setImmediate = promisify(timers.setImmediate), + setInterval = promisify(timers.setInterval); + +export default { + setTimeout, + setImmediate, + setInterval, +}; diff --git a/ext/node/polyfills/tls.ts b/ext/node/polyfills/tls.ts new file mode 100644 index 0000000000..b920ffc7d4 --- /dev/null +++ b/ext/node/polyfills/tls.ts @@ -0,0 +1,65 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import tlsCommon from "internal:deno_node/polyfills/_tls_common.ts"; +import tlsWrap from "internal:deno_node/polyfills/_tls_wrap.ts"; + +// openssl -> rustls +const cipherMap = { + "__proto__": null, + "AES128-GCM-SHA256": "TLS13_AES_128_GCM_SHA256", + "AES256-GCM-SHA384": "TLS13_AES_256_GCM_SHA384", + "ECDHE-ECDSA-AES128-GCM-SHA256": "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256", + "ECDHE-ECDSA-AES256-GCM-SHA384": "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384", + "ECDHE-ECDSA-CHACHA20-POLY1305": + "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", + "ECDHE-RSA-AES128-GCM-SHA256": "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", + "ECDHE-RSA-AES256-GCM-SHA384": "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", + "ECDHE-RSA-CHACHA20-POLY1305": "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", + "TLS_AES_128_GCM_SHA256": "TLS13_AES_128_GCM_SHA256", + "TLS_AES_256_GCM_SHA384": "TLS13_AES_256_GCM_SHA384", + "TLS_CHACHA20_POLY1305_SHA256": "TLS13_CHACHA20_POLY1305_SHA256", +}; + +export function getCiphers() { + // TODO(bnoordhuis) Use locale-insensitive toLowerCase() + return Object.keys(cipherMap).map((name) => name.toLowerCase()); +} + +export const rootCertificates = undefined; +export const DEFAULT_ECDH_CURVE = "auto"; +export const DEFAULT_MAX_VERSION = "TLSv1.3"; +export const DEFAULT_MIN_VERSION = "TLSv1.2"; + +export class CryptoStream {} +export class SecurePair {} +export const Server = tlsWrap.Server; +export function createSecurePair() { + notImplemented("tls.createSecurePair"); +} + +export default { + CryptoStream, + SecurePair, + Server, + TLSSocket: tlsWrap.TLSSocket, + checkServerIdentity: tlsWrap.checkServerIdentity, + connect: tlsWrap.connect, + createSecureContext: tlsCommon.createSecureContext, + createSecurePair, + createServer: tlsWrap.createServer, + getCiphers, + rootCertificates, + DEFAULT_CIPHERS: tlsWrap.DEFAULT_CIPHERS, + DEFAULT_ECDH_CURVE, + DEFAULT_MAX_VERSION, + DEFAULT_MIN_VERSION, +}; + +export const checkServerIdentity = tlsWrap.checkServerIdentity; +export const connect = tlsWrap.connect; +export const createSecureContext = tlsCommon.createSecureContext; +export const createServer = tlsWrap.createServer; +export const DEFAULT_CIPHERS = tlsWrap.DEFAULT_CIPHERS; +export const TLSSocket = tlsWrap.TLSSocket; diff --git a/ext/node/polyfills/tty.ts b/ext/node/polyfills/tty.ts new file mode 100644 index 0000000000..b3b9b62da6 --- /dev/null +++ b/ext/node/polyfills/tty.ts @@ -0,0 +1,25 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +import { Socket } from "internal:deno_node/polyfills/net.ts"; + +// Returns true when the given numeric fd is associated with a TTY and false otherwise. +function isatty(fd: number) { + if (typeof fd !== "number") { + return false; + } + try { + return Deno.isatty(fd); + } catch (_) { + return false; + } +} + +// TODO(kt3k): Implement tty.ReadStream class +export class ReadStream extends Socket { +} +// TODO(kt3k): Implement tty.WriteStream class +export class WriteStream extends Socket { +} + +export { isatty }; +export default { isatty, WriteStream, ReadStream }; diff --git a/ext/node/polyfills/upstream_modules.ts b/ext/node/polyfills/upstream_modules.ts new file mode 100644 index 0000000000..ed8d6faa00 --- /dev/null +++ b/ext/node/polyfills/upstream_modules.ts @@ -0,0 +1,39 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Upstream modules +const callerPath = `const callerCallsite = require("caller-callsite"); +const re = /^file:/; + +module.exports = () => { + const fileUrl = callerCallsite().getFileName(); + return fileUrl.replace(re, ""); +}; +`; + +// From: https://github.com/stefanpenner/get-caller-file/blob/2383bf9e98ed3c568ff69d7586cf59c0f1dcb9d3/index.ts +const getCallerFile = ` +const re = /^file:\\/\\//; + +module.exports = function getCallerFile(position = 2) { + if (position >= Error.stackTraceLimit) { + throw new TypeError('getCallerFile(position) requires position be less then Error.stackTraceLimit but position was: "' + position + '" and Error.stackTraceLimit was: "' + Error.stackTraceLimit + '"'); + } + + const oldPrepareStackTrace = Error.prepareStackTrace; + Error.prepareStackTrace = (_, stack) => stack; + const stack = new Error().stack; + Error.prepareStackTrace = oldPrepareStackTrace; + + + if (stack !== null && typeof stack === 'object') { + // stack[0] holds this file + // stack[1] holds where this function was called + // stack[2] holds the file we're interested in + return stack[position] ? stack[position].getFileName().replace(re, "") : undefined; + } +}; +`; + +export default { + "caller-path": callerPath, + "get-caller-file": getCallerFile, +} as Record; diff --git a/ext/node/polyfills/url.ts b/ext/node/polyfills/url.ts new file mode 100644 index 0000000000..31b1e676f9 --- /dev/null +++ b/ext/node/polyfills/url.ts @@ -0,0 +1,1506 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// 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. + +import { + ERR_INVALID_ARG_TYPE, + ERR_INVALID_ARG_VALUE, + ERR_INVALID_FILE_URL_HOST, + ERR_INVALID_FILE_URL_PATH, + ERR_INVALID_URL, + ERR_INVALID_URL_SCHEME, +} from "internal:deno_node/polyfills/internal/errors.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; +import { + CHAR_0, + CHAR_9, + CHAR_AT, + CHAR_BACKWARD_SLASH, + CHAR_CARRIAGE_RETURN, + CHAR_CIRCUMFLEX_ACCENT, + CHAR_DOT, + CHAR_DOUBLE_QUOTE, + CHAR_FORM_FEED, + CHAR_FORWARD_SLASH, + CHAR_GRAVE_ACCENT, + CHAR_HASH, + CHAR_HYPHEN_MINUS, + CHAR_LEFT_ANGLE_BRACKET, + CHAR_LEFT_CURLY_BRACKET, + CHAR_LEFT_SQUARE_BRACKET, + CHAR_LINE_FEED, + CHAR_LOWERCASE_A, + CHAR_LOWERCASE_Z, + CHAR_NO_BREAK_SPACE, + CHAR_PERCENT, + CHAR_PLUS, + CHAR_QUESTION_MARK, + CHAR_RIGHT_ANGLE_BRACKET, + CHAR_RIGHT_CURLY_BRACKET, + CHAR_RIGHT_SQUARE_BRACKET, + CHAR_SEMICOLON, + CHAR_SINGLE_QUOTE, + CHAR_SPACE, + CHAR_TAB, + CHAR_UNDERSCORE, + CHAR_UPPERCASE_A, + CHAR_UPPERCASE_Z, + CHAR_VERTICAL_LINE, + CHAR_ZERO_WIDTH_NOBREAK_SPACE, +} from "internal:deno_node/polyfills/path/_constants.ts"; +import * as path from "internal:deno_node/polyfills/path.ts"; +import { + regexNonASCII, + regexPunycode, + toASCII, + toUnicode, +} from "internal:deno_node/polyfills/internal/idna.ts"; +import { isWindows, osType } from "internal:deno_node/polyfills/_util/os.ts"; +import { + encodeStr, + hexTable, +} from "internal:deno_node/polyfills/internal/querystring.ts"; +import querystring from "internal:deno_node/polyfills/querystring.ts"; +import type { + ParsedUrlQuery, + ParsedUrlQueryInput, +} from "internal:deno_node/polyfills/querystring.ts"; +import { URL, URLSearchParams } from "internal:deno_url/00_url.js"; + +const forwardSlashRegEx = /\//g; +const percentRegEx = /%/g; +const backslashRegEx = /\\/g; +const newlineRegEx = /\n/g; +const carriageReturnRegEx = /\r/g; +const tabRegEx = /\t/g; +// Reference: RFC 3986, RFC 1808, RFC 2396 + +// define these here so at least they only have to be +// compiled once on the first module load. +const protocolPattern = /^[a-z0-9.+-]+:/i; +const portPattern = /:[0-9]*$/; +const hostPattern = /^\/\/[^@/]+@[^@/]+/; +// Special case for a simple path URL +const simplePathPattern = /^(\/\/?(?!\/)[^?\s]*)(\?[^\s]*)?$/; +// Protocols that can allow "unsafe" and "unwise" chars. +const unsafeProtocol = new Set(["javascript", "javascript:"]); +// Protocols that never have a hostname. +const hostlessProtocol = new Set(["javascript", "javascript:"]); +// Protocols that always contain a // bit. +const slashedProtocol = new Set([ + "http", + "http:", + "https", + "https:", + "ftp", + "ftp:", + "gopher", + "gopher:", + "file", + "file:", + "ws", + "ws:", + "wss", + "wss:", +]); + +const hostnameMaxLen = 255; + +// These characters do not need escaping: +// ! - . _ ~ +// ' ( ) * : +// digits +// alpha (uppercase) +// alpha (lowercase) +// deno-fmt-ignore +const noEscapeAuth = new Int8Array([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1F + 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, // 0x20 - 0x2F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, // 0x30 - 0x3F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 - 0x4F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 0x50 - 0x5F + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, // 0x70 - 0x7F +]); + +// This prevents some common spoofing bugs due to our use of IDNA toASCII. For +// compatibility, the set of characters we use here is the *intersection* of +// "forbidden host code point" in the WHATWG URL Standard [1] and the +// characters in the host parsing loop in Url.prototype.parse, with the +// following additions: +// +// - ':' since this could cause a "protocol spoofing" bug +// - '@' since this could cause parts of the hostname to be confused with auth +// - '[' and ']' since this could cause a non-IPv6 hostname to be interpreted +// as IPv6 by isIpv6Hostname above +// +// [1]: https://url.spec.whatwg.org/#forbidden-host-code-point +const forbiddenHostChars = /[\0\t\n\r #%/:<>?@[\\\]^|]/; +// For IPv6, permit '[', ']', and ':'. +const forbiddenHostCharsIpv6 = /[\0\t\n\r #%/<>?@\\^|]/; + +const _url = URL; +export { _url as URL }; + +// Legacy URL API +export class Url { + public protocol: string | null; + public slashes: boolean | null; + public auth: string | null; + public host: string | null; + public port: string | null; + public hostname: string | null; + public hash: string | null; + public search: string | null; + public query: string | ParsedUrlQuery | null; + public pathname: string | null; + public path: string | null; + public href: string | null; + [key: string]: unknown; + + constructor() { + this.protocol = null; + this.slashes = null; + this.auth = null; + this.host = null; + this.port = null; + this.hostname = null; + this.hash = null; + this.search = null; + this.query = null; + this.pathname = null; + this.path = null; + this.href = null; + } + + #parseHost() { + let host = this.host || ""; + let port: RegExpExecArray | null | string = portPattern.exec(host); + if (port) { + port = port[0]; + if (port !== ":") { + this.port = port.slice(1); + } + host = host.slice(0, host.length - port.length); + } + if (host) this.hostname = host; + } + + public resolve(relative: string) { + return this.resolveObject(parse(relative, false, true)).format(); + } + + public resolveObject(relative: string | Url) { + if (typeof relative === "string") { + const rel = new Url(); + rel.urlParse(relative, false, true); + relative = rel; + } + + const result = new Url(); + const tkeys = Object.keys(this); + for (let tk = 0; tk < tkeys.length; tk++) { + const tkey = tkeys[tk]; + result[tkey] = this[tkey]; + } + + // Hash is always overridden, no matter what. + // even href="" will remove it. + result.hash = relative.hash; + + // If the relative url is empty, then there's nothing left to do here. + if (relative.href === "") { + result.href = result.format(); + return result; + } + + // Hrefs like //foo/bar always cut to the protocol. + if (relative.slashes && !relative.protocol) { + // Take everything except the protocol from relative + const rkeys = Object.keys(relative); + for (let rk = 0; rk < rkeys.length; rk++) { + const rkey = rkeys[rk]; + if (rkey !== "protocol") result[rkey] = relative[rkey]; + } + + // urlParse appends trailing / to urls like http://www.example.com + if ( + result.protocol && + slashedProtocol.has(result.protocol) && + result.hostname && + !result.pathname + ) { + result.path = result.pathname = "/"; + } + + result.href = result.format(); + return result; + } + + if (relative.protocol && relative.protocol !== result.protocol) { + // If it's a known url protocol, then changing + // the protocol does weird things + // first, if it's not file:, then we MUST have a host, + // and if there was a path + // to begin with, then we MUST have a path. + // if it is file:, then the host is dropped, + // because that's known to be hostless. + // anything else is assumed to be absolute. + if (!slashedProtocol.has(relative.protocol)) { + const keys = Object.keys(relative); + for (let v = 0; v < keys.length; v++) { + const k = keys[v]; + result[k] = relative[k]; + } + result.href = result.format(); + return result; + } + + result.protocol = relative.protocol; + if ( + !relative.host && + !/^file:?$/.test(relative.protocol) && + !hostlessProtocol.has(relative.protocol) + ) { + const relPath = (relative.pathname || "").split("/"); + while (relPath.length && !(relative.host = relPath.shift() || null)); + if (!relative.host) relative.host = ""; + if (!relative.hostname) relative.hostname = ""; + if (relPath[0] !== "") relPath.unshift(""); + if (relPath.length < 2) relPath.unshift(""); + result.pathname = relPath.join("/"); + } else { + result.pathname = relative.pathname; + } + result.search = relative.search; + result.query = relative.query; + result.host = relative.host || ""; + result.auth = relative.auth; + result.hostname = relative.hostname || relative.host; + result.port = relative.port; + // To support http.request + if (result.pathname || result.search) { + const p = result.pathname || ""; + const s = result.search || ""; + result.path = p + s; + } + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + } + + const isSourceAbs = result.pathname && result.pathname.charAt(0) === "/"; + const isRelAbs = relative.host || + (relative.pathname && relative.pathname.charAt(0) === "/"); + let mustEndAbs: string | boolean | number | null = isRelAbs || + isSourceAbs || (result.host && relative.pathname); + const removeAllDots = mustEndAbs; + let srcPath = (result.pathname && result.pathname.split("/")) || []; + const relPath = (relative.pathname && relative.pathname.split("/")) || []; + const noLeadingSlashes = result.protocol && + !slashedProtocol.has(result.protocol); + + // If the url is a non-slashed url, then relative + // links like ../.. should be able + // to crawl up to the hostname, as well. This is strange. + // result.protocol has already been set by now. + // Later on, put the first path part into the host field. + if (noLeadingSlashes) { + result.hostname = ""; + result.port = null; + if (result.host) { + if (srcPath[0] === "") srcPath[0] = result.host; + else srcPath.unshift(result.host); + } + result.host = ""; + if (relative.protocol) { + relative.hostname = null; + relative.port = null; + result.auth = null; + if (relative.host) { + if (relPath[0] === "") relPath[0] = relative.host; + else relPath.unshift(relative.host); + } + relative.host = null; + } + mustEndAbs = mustEndAbs && (relPath[0] === "" || srcPath[0] === ""); + } + + if (isRelAbs) { + // it's absolute. + if (relative.host || relative.host === "") { + if (result.host !== relative.host) result.auth = null; + result.host = relative.host; + result.port = relative.port; + } + if (relative.hostname || relative.hostname === "") { + if (result.hostname !== relative.hostname) result.auth = null; + result.hostname = relative.hostname; + } + result.search = relative.search; + result.query = relative.query; + srcPath = relPath; + // Fall through to the dot-handling below. + } else if (relPath.length) { + // it's relative + // throw away the existing file, and take the new path instead. + if (!srcPath) srcPath = []; + srcPath.pop(); + srcPath = srcPath.concat(relPath); + result.search = relative.search; + result.query = relative.query; + } else if (relative.search !== null && relative.search !== undefined) { + // Just pull out the search. + // like href='?foo'. + // Put this after the other two cases because it simplifies the booleans + if (noLeadingSlashes) { + result.hostname = result.host = srcPath.shift() || null; + // Occasionally the auth can get stuck only in host. + // This especially happens in cases like + // url.resolveObject('mailto:local1@domain1', 'local2@domain2') + const authInHost = result.host && result.host.indexOf("@") > 0 && + result.host.split("@"); + if (authInHost) { + result.auth = authInHost.shift() || null; + result.host = result.hostname = authInHost.shift() || null; + } + } + result.search = relative.search; + result.query = relative.query; + // To support http.request + if (result.pathname !== null || result.search !== null) { + result.path = (result.pathname ? result.pathname : "") + + (result.search ? result.search : ""); + } + result.href = result.format(); + return result; + } + + if (!srcPath.length) { + // No path at all. All other things were already handled above. + result.pathname = null; + // To support http.request + if (result.search) { + result.path = "/" + result.search; + } else { + result.path = null; + } + result.href = result.format(); + return result; + } + + // If a url ENDs in . or .., then it must get a trailing slash. + // however, if it ends in anything else non-slashy, + // then it must NOT get a trailing slash. + let last = srcPath.slice(-1)[0]; + const hasTrailingSlash = + ((result.host || relative.host || srcPath.length > 1) && + (last === "." || last === "..")) || + last === ""; + + // Strip single dots, resolve double dots to parent dir + // if the path tries to go above the root, `up` ends up > 0 + let up = 0; + for (let i = srcPath.length - 1; i >= 0; i--) { + last = srcPath[i]; + if (last === ".") { + srcPath.splice(i, 1); + } else if (last === "..") { + srcPath.splice(i, 1); + up++; + } else if (up) { + srcPath.splice(i, 1); + up--; + } + } + + // If the path is allowed to go above the root, restore leading ..s + if (!mustEndAbs && !removeAllDots) { + while (up--) { + srcPath.unshift(".."); + } + } + + if ( + mustEndAbs && + srcPath[0] !== "" && + (!srcPath[0] || srcPath[0].charAt(0) !== "/") + ) { + srcPath.unshift(""); + } + + if (hasTrailingSlash && srcPath.join("/").slice(-1) !== "/") { + srcPath.push(""); + } + + const isAbsolute = srcPath[0] === "" || + (srcPath[0] && srcPath[0].charAt(0) === "/"); + + // put the host back + if (noLeadingSlashes) { + result.hostname = result.host = isAbsolute + ? "" + : srcPath.length + ? srcPath.shift() || null + : ""; + // Occasionally the auth can get stuck only in host. + // This especially happens in cases like + // url.resolveObject('mailto:local1@domain1', 'local2@domain2') + const authInHost = result.host && result.host.indexOf("@") > 0 + ? result.host.split("@") + : false; + if (authInHost) { + result.auth = authInHost.shift() || null; + result.host = result.hostname = authInHost.shift() || null; + } + } + + mustEndAbs = mustEndAbs || (result.host && srcPath.length); + + if (mustEndAbs && !isAbsolute) { + srcPath.unshift(""); + } + + if (!srcPath.length) { + result.pathname = null; + result.path = null; + } else { + result.pathname = srcPath.join("/"); + } + + // To support request.http + if (result.pathname !== null || result.search !== null) { + result.path = (result.pathname ? result.pathname : "") + + (result.search ? result.search : ""); + } + result.auth = relative.auth || result.auth; + result.slashes = result.slashes || relative.slashes; + result.href = result.format(); + return result; + } + + format() { + let auth = this.auth || ""; + if (auth) { + auth = encodeStr(auth, noEscapeAuth, hexTable); + auth += "@"; + } + + let protocol = this.protocol || ""; + let pathname = this.pathname || ""; + let hash = this.hash || ""; + let host = ""; + let query = ""; + + if (this.host) { + host = auth + this.host; + } else if (this.hostname) { + host = auth + + (this.hostname.includes(":") && !isIpv6Hostname(this.hostname) + ? "[" + this.hostname + "]" + : this.hostname); + if (this.port) { + host += ":" + this.port; + } + } + + if (this.query !== null && typeof this.query === "object") { + query = querystring.stringify(this.query); + } + + let search = this.search || (query && "?" + query) || ""; + + if (protocol && protocol.charCodeAt(protocol.length - 1) !== 58 /* : */) { + protocol += ":"; + } + + let newPathname = ""; + let lastPos = 0; + for (let i = 0; i < pathname.length; ++i) { + switch (pathname.charCodeAt(i)) { + case CHAR_HASH: + if (i - lastPos > 0) { + newPathname += pathname.slice(lastPos, i); + } + newPathname += "%23"; + lastPos = i + 1; + break; + case CHAR_QUESTION_MARK: + if (i - lastPos > 0) { + newPathname += pathname.slice(lastPos, i); + } + newPathname += "%3F"; + lastPos = i + 1; + break; + } + } + if (lastPos > 0) { + if (lastPos !== pathname.length) { + pathname = newPathname + pathname.slice(lastPos); + } else pathname = newPathname; + } + + // Only the slashedProtocols get the //. Not mailto:, xmpp:, etc. + // unless they had them to begin with. + if (this.slashes || slashedProtocol.has(protocol)) { + if (this.slashes || host) { + if (pathname && pathname.charCodeAt(0) !== CHAR_FORWARD_SLASH) { + pathname = "/" + pathname; + } + host = "//" + host; + } else if ( + protocol.length >= 4 && + protocol.charCodeAt(0) === 102 /* f */ && + protocol.charCodeAt(1) === 105 /* i */ && + protocol.charCodeAt(2) === 108 /* l */ && + protocol.charCodeAt(3) === 101 /* e */ + ) { + host = "//"; + } + } + + search = search.replace(/#/g, "%23"); + + if (hash && hash.charCodeAt(0) !== CHAR_HASH) { + hash = "#" + hash; + } + if (search && search.charCodeAt(0) !== CHAR_QUESTION_MARK) { + search = "?" + search; + } + + return protocol + host + pathname + search + hash; + } + + public urlParse( + url: string, + parseQueryString: boolean, + slashesDenoteHost: boolean, + ) { + validateString(url, "url"); + + // Copy chrome, IE, opera backslash-handling behavior. + // Back slashes before the query string get converted to forward slashes + // See: https://code.google.com/p/chromium/issues/detail?id=25916 + let hasHash = false; + let start = -1; + let end = -1; + let rest = ""; + let lastPos = 0; + for (let i = 0, inWs = false, split = false; i < url.length; ++i) { + const code = url.charCodeAt(i); + + // Find first and last non-whitespace characters for trimming + const isWs = code === CHAR_SPACE || + code === CHAR_TAB || + code === CHAR_CARRIAGE_RETURN || + code === CHAR_LINE_FEED || + code === CHAR_FORM_FEED || + code === CHAR_NO_BREAK_SPACE || + code === CHAR_ZERO_WIDTH_NOBREAK_SPACE; + if (start === -1) { + if (isWs) continue; + lastPos = start = i; + } else if (inWs) { + if (!isWs) { + end = -1; + inWs = false; + } + } else if (isWs) { + end = i; + inWs = true; + } + + // Only convert backslashes while we haven't seen a split character + if (!split) { + switch (code) { + case CHAR_HASH: + hasHash = true; + // Fall through + case CHAR_QUESTION_MARK: + split = true; + break; + case CHAR_BACKWARD_SLASH: + if (i - lastPos > 0) rest += url.slice(lastPos, i); + rest += "/"; + lastPos = i + 1; + break; + } + } else if (!hasHash && code === CHAR_HASH) { + hasHash = true; + } + } + + // Check if string was non-empty (including strings with only whitespace) + if (start !== -1) { + if (lastPos === start) { + // We didn't convert any backslashes + + if (end === -1) { + if (start === 0) rest = url; + else rest = url.slice(start); + } else { + rest = url.slice(start, end); + } + } else if (end === -1 && lastPos < url.length) { + // We converted some backslashes and have only part of the entire string + rest += url.slice(lastPos); + } else if (end !== -1 && lastPos < end) { + // We converted some backslashes and have only part of the entire string + rest += url.slice(lastPos, end); + } + } + + if (!slashesDenoteHost && !hasHash) { + // Try fast path regexp + const simplePath = simplePathPattern.exec(rest); + if (simplePath) { + this.path = rest; + this.href = rest; + this.pathname = simplePath[1]; + if (simplePath[2]) { + this.search = simplePath[2]; + if (parseQueryString) { + this.query = querystring.parse(this.search.slice(1)); + } else { + this.query = this.search.slice(1); + } + } else if (parseQueryString) { + this.search = null; + this.query = Object.create(null); + } + return this; + } + } + + let proto: RegExpExecArray | null | string = protocolPattern.exec(rest); + let lowerProto = ""; + if (proto) { + proto = proto[0]; + lowerProto = proto.toLowerCase(); + this.protocol = lowerProto; + rest = rest.slice(proto.length); + } + + // Figure out if it's got a host + // user@server is *always* interpreted as a hostname, and url + // resolution will treat //foo/bar as host=foo,path=bar because that's + // how the browser resolves relative URLs. + let slashes; + if (slashesDenoteHost || proto || hostPattern.test(rest)) { + slashes = rest.charCodeAt(0) === CHAR_FORWARD_SLASH && + rest.charCodeAt(1) === CHAR_FORWARD_SLASH; + if (slashes && !(proto && hostlessProtocol.has(lowerProto))) { + rest = rest.slice(2); + this.slashes = true; + } + } + + if ( + !hostlessProtocol.has(lowerProto) && + (slashes || (proto && !slashedProtocol.has(proto))) + ) { + // there's a hostname. + // the first instance of /, ?, ;, or # ends the host. + // + // If there is an @ in the hostname, then non-host chars *are* allowed + // to the left of the last @ sign, unless some host-ending character + // comes *before* the @-sign. + // URLs are obnoxious. + // + // ex: + // http://a@b@c/ => user:a@b host:c + // http://a@b?@c => user:a host:b path:/?@c + + let hostEnd = -1; + let atSign = -1; + let nonHost = -1; + for (let i = 0; i < rest.length; ++i) { + switch (rest.charCodeAt(i)) { + case CHAR_TAB: + case CHAR_LINE_FEED: + case CHAR_CARRIAGE_RETURN: + case CHAR_SPACE: + case CHAR_DOUBLE_QUOTE: + case CHAR_PERCENT: + case CHAR_SINGLE_QUOTE: + case CHAR_SEMICOLON: + case CHAR_LEFT_ANGLE_BRACKET: + case CHAR_RIGHT_ANGLE_BRACKET: + case CHAR_BACKWARD_SLASH: + case CHAR_CIRCUMFLEX_ACCENT: + case CHAR_GRAVE_ACCENT: + case CHAR_LEFT_CURLY_BRACKET: + case CHAR_VERTICAL_LINE: + case CHAR_RIGHT_CURLY_BRACKET: + // Characters that are never ever allowed in a hostname from RFC 2396 + if (nonHost === -1) nonHost = i; + break; + case CHAR_HASH: + case CHAR_FORWARD_SLASH: + case CHAR_QUESTION_MARK: + // Find the first instance of any host-ending characters + if (nonHost === -1) nonHost = i; + hostEnd = i; + break; + case CHAR_AT: + // At this point, either we have an explicit point where the + // auth portion cannot go past, or the last @ char is the decider. + atSign = i; + nonHost = -1; + break; + } + if (hostEnd !== -1) break; + } + start = 0; + if (atSign !== -1) { + this.auth = decodeURIComponent(rest.slice(0, atSign)); + start = atSign + 1; + } + if (nonHost === -1) { + this.host = rest.slice(start); + rest = ""; + } else { + this.host = rest.slice(start, nonHost); + rest = rest.slice(nonHost); + } + + // pull out port. + this.#parseHost(); + + // We've indicated that there is a hostname, + // so even if it's empty, it has to be present. + if (typeof this.hostname !== "string") this.hostname = ""; + + const hostname = this.hostname; + + // If hostname begins with [ and ends with ] + // assume that it's an IPv6 address. + const ipv6Hostname = isIpv6Hostname(hostname); + + // validate a little. + if (!ipv6Hostname) { + rest = getHostname(this, rest, hostname); + } + + if (this.hostname.length > hostnameMaxLen) { + this.hostname = ""; + } else { + // Hostnames are always lower case. + this.hostname = this.hostname.toLowerCase(); + } + + if (this.hostname !== "") { + if (ipv6Hostname) { + if (forbiddenHostCharsIpv6.test(this.hostname)) { + throw new ERR_INVALID_URL(url); + } + } else { + // IDNA Support: Returns a punycoded representation of "domain". + // It only converts parts of the domain name that + // have non-ASCII characters, i.e. it doesn't matter if + // you call it with a domain that already is ASCII-only. + + // Use lenient mode (`true`) to try to support even non-compliant + // URLs. + this.hostname = toASCII(this.hostname); + + // Prevent two potential routes of hostname spoofing. + // 1. If this.hostname is empty, it must have become empty due to toASCII + // since we checked this.hostname above. + // 2. If any of forbiddenHostChars appears in this.hostname, it must have + // also gotten in due to toASCII. This is since getHostname would have + // filtered them out otherwise. + // Rather than trying to correct this by moving the non-host part into + // the pathname as we've done in getHostname, throw an exception to + // convey the severity of this issue. + if (this.hostname === "" || forbiddenHostChars.test(this.hostname)) { + throw new ERR_INVALID_URL(url); + } + } + } + + const p = this.port ? ":" + this.port : ""; + const h = this.hostname || ""; + this.host = h + p; + + // strip [ and ] from the hostname + // the host field still retains them, though + if (ipv6Hostname) { + this.hostname = this.hostname.slice(1, -1); + if (rest[0] !== "/") { + rest = "/" + rest; + } + } + } + + // Now rest is set to the post-host stuff. + // Chop off any delim chars. + if (!unsafeProtocol.has(lowerProto)) { + // First, make 100% sure that any "autoEscape" chars get + // escaped, even if encodeURIComponent doesn't think they + // need to be. + rest = autoEscapeStr(rest); + } + + let questionIdx = -1; + let hashIdx = -1; + for (let i = 0; i < rest.length; ++i) { + const code = rest.charCodeAt(i); + if (code === CHAR_HASH) { + this.hash = rest.slice(i); + hashIdx = i; + break; + } else if (code === CHAR_QUESTION_MARK && questionIdx === -1) { + questionIdx = i; + } + } + + if (questionIdx !== -1) { + if (hashIdx === -1) { + this.search = rest.slice(questionIdx); + this.query = rest.slice(questionIdx + 1); + } else { + this.search = rest.slice(questionIdx, hashIdx); + this.query = rest.slice(questionIdx + 1, hashIdx); + } + if (parseQueryString) { + this.query = querystring.parse(this.query); + } + } else if (parseQueryString) { + // No query string, but parseQueryString still requested + this.search = null; + this.query = Object.create(null); + } + + const useQuestionIdx = questionIdx !== -1 && + (hashIdx === -1 || questionIdx < hashIdx); + const firstIdx = useQuestionIdx ? questionIdx : hashIdx; + if (firstIdx === -1) { + if (rest.length > 0) this.pathname = rest; + } else if (firstIdx > 0) { + this.pathname = rest.slice(0, firstIdx); + } + if (slashedProtocol.has(lowerProto) && this.hostname && !this.pathname) { + this.pathname = "/"; + } + + // To support http.request + if (this.pathname || this.search) { + const p = this.pathname || ""; + const s = this.search || ""; + this.path = p + s; + } + + // Finally, reconstruct the href based on what has been validated. + this.href = this.format(); + return this; + } +} + +interface UrlObject { + auth?: string | null | undefined; + hash?: string | null | undefined; + host?: string | null | undefined; + hostname?: string | null | undefined; + href?: string | null | undefined; + pathname?: string | null | undefined; + protocol?: string | null | undefined; + search?: string | null | undefined; + slashes?: boolean | null | undefined; + port?: string | number | null | undefined; + query?: string | null | ParsedUrlQueryInput | undefined; +} + +export function format( + urlObject: string | URL | Url | UrlObject, + options?: { + auth: boolean; + fragment: boolean; + search: boolean; + unicode: boolean; + }, +): string { + if (typeof urlObject === "string") { + urlObject = parse(urlObject, true, false); + } else if (typeof urlObject !== "object" || urlObject === null) { + throw new ERR_INVALID_ARG_TYPE( + "urlObject", + ["Object", "string"], + urlObject, + ); + } else if (!(urlObject instanceof Url)) { + if (urlObject instanceof URL) { + return formatWhatwg(urlObject, options); + } + return Url.prototype.format.call(urlObject); + } + + return (urlObject as Url).format(); +} + +/** + * The URL object has both a `toString()` method and `href` property that return string serializations of the URL. + * These are not, however, customizable in any way. + * This method allows for basic customization of the output. + * @see Tested in `parallel/test-url-format-whatwg.js`. + * @param urlObject + * @param options + * @param options.auth `true` if the serialized URL string should include the username and password, `false` otherwise. **Default**: `true`. + * @param options.fragment `true` if the serialized URL string should include the fragment, `false` otherwise. **Default**: `true`. + * @param options.search `true` if the serialized URL string should include the search query, **Default**: `true`. + * @param options.unicode `true` if Unicode characters appearing in the host component of the URL string should be encoded directly as opposed to being Punycode encoded. **Default**: `false`. + * @returns a customizable serialization of a URL `String` representation of a `WHATWG URL` object. + */ +function formatWhatwg( + urlObject: string | URL, + options?: { + auth: boolean; + fragment: boolean; + search: boolean; + unicode: boolean; + }, +): string { + if (typeof urlObject === "string") { + urlObject = new URL(urlObject); + } + if (options) { + if (typeof options !== "object") { + throw new ERR_INVALID_ARG_TYPE("options", "object", options); + } + } + + options = { + auth: true, + fragment: true, + search: true, + unicode: false, + ...options, + }; + + let ret = urlObject.protocol; + if (urlObject.host !== null) { + ret += "//"; + const hasUsername = !!urlObject.username; + const hasPassword = !!urlObject.password; + if (options.auth && (hasUsername || hasPassword)) { + if (hasUsername) { + ret += urlObject.username; + } + if (hasPassword) { + ret += `:${urlObject.password}`; + } + ret += "@"; + } + // TODO(wafuwfu13): Support unicode option + // ret += options.unicode ? + // domainToUnicode(urlObject.host) : urlObject.host; + ret += urlObject.host; + if (urlObject.port) { + ret += `:${urlObject.port}`; + } + } + + ret += urlObject.pathname; + + if (options.search && urlObject.search) { + ret += urlObject.search; + } + if (options.fragment && urlObject.hash) { + ret += urlObject.hash; + } + + return ret; +} + +function isIpv6Hostname(hostname: string) { + return ( + hostname.charCodeAt(0) === CHAR_LEFT_SQUARE_BRACKET && + hostname.charCodeAt(hostname.length - 1) === CHAR_RIGHT_SQUARE_BRACKET + ); +} + +function getHostname(self: Url, rest: string, hostname: string) { + for (let i = 0; i < hostname.length; ++i) { + const code = hostname.charCodeAt(i); + const isValid = (code >= CHAR_LOWERCASE_A && code <= CHAR_LOWERCASE_Z) || + code === CHAR_DOT || + (code >= CHAR_UPPERCASE_A && code <= CHAR_UPPERCASE_Z) || + (code >= CHAR_0 && code <= CHAR_9) || + code === CHAR_HYPHEN_MINUS || + code === CHAR_PLUS || + code === CHAR_UNDERSCORE || + code > 127; + + // Invalid host character + if (!isValid) { + self.hostname = hostname.slice(0, i); + return `/${hostname.slice(i)}${rest}`; + } + } + return rest; +} + +// Escaped characters. Use empty strings to fill up unused entries. +// Using Array is faster than Object/Map +// deno-fmt-ignore +const escapedCodes = [ + /* 0 - 9 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "%09", + /* 10 - 19 */ "%0A", + "", + "", + "%0D", + "", + "", + "", + "", + "", + "", + /* 20 - 29 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 30 - 39 */ "", + "", + "%20", + "", + "%22", + "", + "", + "", + "", + "%27", + /* 40 - 49 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 50 - 59 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 60 - 69 */ "%3C", + "", + "%3E", + "", + "", + "", + "", + "", + "", + "", + /* 70 - 79 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 80 - 89 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 90 - 99 */ "", + "", + "%5C", + "", + "%5E", + "", + "%60", + "", + "", + "", + /* 100 - 109 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 110 - 119 */ "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + /* 120 - 125 */ "", + "", + "", + "%7B", + "%7C", + "%7D" +]; + +// Automatically escape all delimiters and unwise characters from RFC 2396. +// Also escape single quotes in case of an XSS attack. +// Return the escaped string. +function autoEscapeStr(rest: string) { + let escaped = ""; + let lastEscapedPos = 0; + for (let i = 0; i < rest.length; ++i) { + // `escaped` contains substring up to the last escaped character. + const escapedChar = escapedCodes[rest.charCodeAt(i)]; + if (escapedChar) { + // Concat if there are ordinary characters in the middle. + if (i > lastEscapedPos) { + escaped += rest.slice(lastEscapedPos, i); + } + escaped += escapedChar; + lastEscapedPos = i + 1; + } + } + if (lastEscapedPos === 0) { + // Nothing has been escaped. + return rest; + } + + // There are ordinary characters at the end. + if (lastEscapedPos < rest.length) { + escaped += rest.slice(lastEscapedPos); + } + + return escaped; +} + +/** + * The url.urlParse() method takes a URL string, parses it, and returns a URL object. + * + * @see Tested in `parallel/test-url-parse-format.js`. + * @param url The URL string to parse. + * @param parseQueryString If `true`, the query property will always be set to an object returned by the querystring module's parse() method. If false, + * the query property on the returned URL object will be an unparsed, undecoded string. Default: false. + * @param slashesDenoteHost If `true`, the first token after the literal string // and preceding the next / will be interpreted as the host + */ +export function parse( + url: string | Url, + parseQueryString: boolean, + slashesDenoteHost: boolean, +) { + if (url instanceof Url) return url; + + const urlObject = new Url(); + urlObject.urlParse(url, parseQueryString, slashesDenoteHost); + return urlObject; +} + +/** The url.resolve() method resolves a target URL relative to a base URL in a manner similar to that of a Web browser resolving an anchor tag HREF. + * @see https://nodejs.org/api/url.html#urlresolvefrom-to + * @legacy + */ +export function resolve(from: string, to: string) { + return parse(from, false, true).resolve(to); +} + +export function resolveObject(source: string | Url, relative: string) { + if (!source) return relative; + return parse(source, false, true).resolveObject(relative); +} + +/** + * The url.domainToASCII() takes an arbitrary domain and attempts to convert it into an IDN + * + * @param domain The domain to convert to an IDN + * @see https://www.rfc-editor.org/rfc/rfc3490#section-4 + */ +export function domainToASCII(domain: string) { + if (regexPunycode.test(domain) && regexNonASCII.test(domain)) { + return ""; // Failure case + } + + return toASCII(domain); +} + +/** + * The url.domainToUnicode() takes an IDN and attempts to convert it into unicode + * + * @param domain The IDN to convert to Unicode + * @see https://www.rfc-editor.org/rfc/rfc3490#section-4 + */ +export function domainToUnicode(domain: string) { + if (regexPunycode.test(domain) && regexNonASCII.test(domain)) { + return ""; // Failure case + } + + return toUnicode(domain); +} + +/** + * This function ensures the correct decodings of percent-encoded characters as well as ensuring a cross-platform valid absolute path string. + * @see Tested in `parallel/test-fileurltopath.js`. + * @param path The file URL string or URL object to convert to a path. + * @returns The fully-resolved platform-specific Node.js file path. + */ +export function fileURLToPath(path: string | URL): string { + if (typeof path === "string") path = new URL(path); + else if (!(path instanceof URL)) { + throw new ERR_INVALID_ARG_TYPE("path", ["string", "URL"], path); + } + if (path.protocol !== "file:") { + throw new ERR_INVALID_URL_SCHEME("file"); + } + return isWindows ? getPathFromURLWin(path) : getPathFromURLPosix(path); +} + +function getPathFromURLWin(url: URL): string { + const hostname = url.hostname; + let pathname = url.pathname; + for (let n = 0; n < pathname.length; n++) { + if (pathname[n] === "%") { + const third = pathname.codePointAt(n + 2)! | 0x20; + if ( + (pathname[n + 1] === "2" && third === 102) || // 2f 2F / + (pathname[n + 1] === "5" && third === 99) // 5c 5C \ + ) { + throw new ERR_INVALID_FILE_URL_PATH( + "must not include encoded \\ or / characters", + ); + } + } + } + + pathname = pathname.replace(forwardSlashRegEx, "\\"); + pathname = decodeURIComponent(pathname); + if (hostname !== "") { + // TODO(bartlomieju): add support for punycode encodings + return `\\\\${hostname}${pathname}`; + } else { + // Otherwise, it's a local path that requires a drive letter + const letter = pathname.codePointAt(1)! | 0x20; + const sep = pathname[2]; + if ( + letter < CHAR_LOWERCASE_A || + letter > CHAR_LOWERCASE_Z || // a..z A..Z + sep !== ":" + ) { + throw new ERR_INVALID_FILE_URL_PATH("must be absolute"); + } + return pathname.slice(1); + } +} + +function getPathFromURLPosix(url: URL): string { + if (url.hostname !== "") { + throw new ERR_INVALID_FILE_URL_HOST(osType); + } + const pathname = url.pathname; + for (let n = 0; n < pathname.length; n++) { + if (pathname[n] === "%") { + const third = pathname.codePointAt(n + 2)! | 0x20; + if (pathname[n + 1] === "2" && third === 102) { + throw new ERR_INVALID_FILE_URL_PATH( + "must not include encoded / characters", + ); + } + } + } + return decodeURIComponent(pathname); +} + +/** + * The following characters are percent-encoded when converting from file path + * to URL: + * - %: The percent character is the only character not encoded by the + * `pathname` setter. + * - \: Backslash is encoded on non-windows platforms since it's a valid + * character but the `pathname` setters replaces it by a forward slash. + * - LF: The newline character is stripped out by the `pathname` setter. + * (See whatwg/url#419) + * - CR: The carriage return character is also stripped out by the `pathname` + * setter. + * - TAB: The tab character is also stripped out by the `pathname` setter. + */ +function encodePathChars(filepath: string): string { + if (filepath.includes("%")) { + filepath = filepath.replace(percentRegEx, "%25"); + } + // In posix, backslash is a valid character in paths: + if (!isWindows && filepath.includes("\\")) { + filepath = filepath.replace(backslashRegEx, "%5C"); + } + if (filepath.includes("\n")) { + filepath = filepath.replace(newlineRegEx, "%0A"); + } + if (filepath.includes("\r")) { + filepath = filepath.replace(carriageReturnRegEx, "%0D"); + } + if (filepath.includes("\t")) { + filepath = filepath.replace(tabRegEx, "%09"); + } + return filepath; +} + +/** + * This function ensures that `filepath` is resolved absolutely, and that the URL control characters are correctly encoded when converting into a File URL. + * @see Tested in `parallel/test-url-pathtofileurl.js`. + * @param filepath The file path string to convert to a file URL. + * @returns The file URL object. + */ +export function pathToFileURL(filepath: string): URL { + const outURL = new URL("file://"); + if (isWindows && filepath.startsWith("\\\\")) { + // UNC path format: \\server\share\resource + const paths = filepath.split("\\"); + if (paths.length <= 3) { + throw new ERR_INVALID_ARG_VALUE( + "filepath", + filepath, + "Missing UNC resource path", + ); + } + const hostname = paths[2]; + if (hostname.length === 0) { + throw new ERR_INVALID_ARG_VALUE( + "filepath", + filepath, + "Empty UNC servername", + ); + } + + outURL.hostname = domainToASCII(hostname); + outURL.pathname = encodePathChars(paths.slice(3).join("/")); + } else { + let resolved = path.resolve(filepath); + // path.resolve strips trailing slashes so we must add them back + const filePathLast = filepath.charCodeAt(filepath.length - 1); + if ( + (filePathLast === CHAR_FORWARD_SLASH || + (isWindows && filePathLast === CHAR_BACKWARD_SLASH)) && + resolved[resolved.length - 1] !== path.sep + ) { + resolved += "/"; + } + + outURL.pathname = encodePathChars(resolved); + } + return outURL; +} + +interface HttpOptions { + protocol: string; + hostname: string; + hash: string; + search: string; + pathname: string; + path: string; + href: string; + port?: number; + auth?: string; +} + +/** + * This utility function converts a URL object into an ordinary options object as expected by the `http.request()` and `https.request()` APIs. + * @see Tested in `parallel/test-url-urltooptions.js`. + * @param url The `WHATWG URL` object to convert to an options object. + * @returns HttpOptions + * @returns HttpOptions.protocol Protocol to use. + * @returns HttpOptions.hostname A domain name or IP address of the server to issue the request to. + * @returns HttpOptions.hash The fragment portion of the URL. + * @returns HttpOptions.search The serialized query portion of the URL. + * @returns HttpOptions.pathname The path portion of the URL. + * @returns HttpOptions.path Request path. Should include query string if any. E.G. `'/index.html?page=12'`. An exception is thrown when the request path contains illegal characters. Currently, only spaces are rejected but that may change in the future. + * @returns HttpOptions.href The serialized URL. + * @returns HttpOptions.port Port of remote server. + * @returns HttpOptions.auth Basic authentication i.e. `'user:password'` to compute an Authorization header. + */ +export function urlToHttpOptions(url: URL): HttpOptions { + const options: HttpOptions = { + protocol: url.protocol, + hostname: typeof url.hostname === "string" && url.hostname.startsWith("[") + ? url.hostname.slice(1, -1) + : url.hostname, + hash: url.hash, + search: url.search, + pathname: url.pathname, + path: `${url.pathname || ""}${url.search || ""}`, + href: url.href, + }; + if (url.port !== "") { + options.port = Number(url.port); + } + if (url.username || url.password) { + options.auth = `${decodeURIComponent(url.username)}:${ + decodeURIComponent( + url.password, + ) + }`; + } + return options; +} + +const URLSearchParams_ = URLSearchParams; +export { URLSearchParams_ as URLSearchParams }; + +export default { + parse, + format, + resolve, + resolveObject, + domainToASCII, + domainToUnicode, + fileURLToPath, + pathToFileURL, + urlToHttpOptions, + Url, + URL, + URLSearchParams, +}; diff --git a/ext/node/polyfills/util.ts b/ext/node/polyfills/util.ts new file mode 100644 index 0000000000..32dfcae748 --- /dev/null +++ b/ext/node/polyfills/util.ts @@ -0,0 +1,293 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { promisify } from "internal:deno_node/polyfills/internal/util.mjs"; +import { callbackify } from "internal:deno_node/polyfills/_util/_util_callbackify.ts"; +import { debuglog } from "internal:deno_node/polyfills/internal/util/debuglog.ts"; +import { + format, + formatWithOptions, + inspect, + stripVTControlCharacters, +} from "internal:deno_node/polyfills/internal/util/inspect.mjs"; +import { codes } from "internal:deno_node/polyfills/internal/error_codes.ts"; +import types from "internal:deno_node/polyfills/util/types.ts"; +import { Buffer } from "internal:deno_node/polyfills/buffer.ts"; +import { isDeepStrictEqual } from "internal:deno_node/polyfills/internal/util/comparisons.ts"; +import process from "internal:deno_node/polyfills/process.ts"; +import { validateString } from "internal:deno_node/polyfills/internal/validators.mjs"; + +export { + callbackify, + debuglog, + format, + formatWithOptions, + inspect, + promisify, + stripVTControlCharacters, + types, +}; + +/** @deprecated - use `Array.isArray()` instead. */ +export function isArray(value: unknown): boolean { + return Array.isArray(value); +} + +/** @deprecated - use `typeof value === "boolean" || value instanceof Boolean` instead. */ +export function isBoolean(value: unknown): boolean { + return typeof value === "boolean" || value instanceof Boolean; +} + +/** @deprecated - use `value === null` instead. */ +export function isNull(value: unknown): boolean { + return value === null; +} + +/** @deprecated - use `value === null || value === undefined` instead. */ +export function isNullOrUndefined(value: unknown): boolean { + return value === null || value === undefined; +} + +/** @deprecated - use `typeof value === "number" || value instanceof Number` instead. */ +export function isNumber(value: unknown): boolean { + return typeof value === "number" || value instanceof Number; +} + +/** @deprecated - use `typeof value === "string" || value instanceof String` instead. */ +export function isString(value: unknown): boolean { + return typeof value === "string" || value instanceof String; +} + +/** @deprecated - use `typeof value === "symbol"` instead. */ +export function isSymbol(value: unknown): boolean { + return typeof value === "symbol"; +} + +/** @deprecated - use `value === undefined` instead. */ +export function isUndefined(value: unknown): boolean { + return value === undefined; +} + +/** @deprecated - use `value !== null && typeof value === "object"` instead. */ +export function isObject(value: unknown): boolean { + return value !== null && typeof value === "object"; +} + +/** @deprecated - use `e instanceof Error` instead. */ +export function isError(e: unknown): boolean { + return e instanceof Error; +} + +/** @deprecated - use `typeof value === "function"` instead. */ +export function isFunction(value: unknown): boolean { + return typeof value === "function"; +} + +/** @deprecated Use util.types.RegExp() instead. */ +export function isRegExp(value: unknown): boolean { + return types.isRegExp(value); +} + +/** @deprecated Use util.types.isDate() instead. */ +export function isDate(value: unknown): boolean { + return types.isDate(value); +} + +/** @deprecated - use `value === null || (typeof value !== "object" && typeof value !== "function")` instead. */ +export function isPrimitive(value: unknown): boolean { + return ( + value === null || (typeof value !== "object" && typeof value !== "function") + ); +} + +/** @deprecated Use Buffer.isBuffer() instead. */ +export function isBuffer(value: unknown): boolean { + return Buffer.isBuffer(value); +} + +/** @deprecated Use Object.assign() instead. */ +export function _extend( + target: Record, + source: unknown, +): Record { + // Don't do anything if source isn't an object + if (source === null || typeof source !== "object") return target; + + const keys = Object.keys(source!); + let i = keys.length; + while (i--) { + target[keys[i]] = (source as Record)[keys[i]]; + } + return target; +} + +/** + * https://nodejs.org/api/util.html#util_util_inherits_constructor_superconstructor + * @param ctor Constructor function which needs to inherit the prototype. + * @param superCtor Constructor function to inherit prototype from. + */ +export function inherits( + ctor: new (...args: unknown[]) => T, + superCtor: new (...args: unknown[]) => U, +) { + if (ctor === undefined || ctor === null) { + throw new codes.ERR_INVALID_ARG_TYPE("ctor", "Function", ctor); + } + + if (superCtor === undefined || superCtor === null) { + throw new codes.ERR_INVALID_ARG_TYPE("superCtor", "Function", superCtor); + } + + if (superCtor.prototype === undefined) { + throw new codes.ERR_INVALID_ARG_TYPE( + "superCtor.prototype", + "Object", + superCtor.prototype, + ); + } + Object.defineProperty(ctor, "super_", { + value: superCtor, + writable: true, + configurable: true, + }); + Object.setPrototypeOf(ctor.prototype, superCtor.prototype); +} + +import { + _TextDecoder, + _TextEncoder, + getSystemErrorName, +} from "internal:deno_node/polyfills/_utils.ts"; + +/** The global TextDecoder */ +export type TextDecoder = import("./_utils.ts")._TextDecoder; +export const TextDecoder = _TextDecoder; + +/** The global TextEncoder */ +export type TextEncoder = import("./_utils.ts")._TextEncoder; +export const TextEncoder = _TextEncoder; + +function pad(n: number) { + return n.toString().padStart(2, "0"); +} + +const months = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", +]; + +/** + * @returns 26 Feb 16:19:34 + */ +function timestamp(): string { + const d = new Date(); + const t = [ + pad(d.getHours()), + pad(d.getMinutes()), + pad(d.getSeconds()), + ].join(":"); + return `${(d.getDate())} ${months[(d).getMonth()]} ${t}`; +} + +/** + * Log is just a thin wrapper to console.log that prepends a timestamp + * @deprecated + */ +// deno-lint-ignore no-explicit-any +export function log(...args: any[]) { + console.log("%s - %s", timestamp(), format(...args)); +} + +// Keep a list of deprecation codes that have been warned on so we only warn on +// each one once. +const codesWarned = new Set(); + +// Mark that a method should not be used. +// Returns a modified function which warns once by default. +// If --no-deprecation is set, then it is a no-op. +// deno-lint-ignore no-explicit-any +export function deprecate(fn: any, msg: string, code?: any) { + if (process.noDeprecation === true) { + return fn; + } + + if (code !== undefined) { + validateString(code, "code"); + } + + let warned = false; + // deno-lint-ignore no-explicit-any + function deprecated(this: any, ...args: any[]) { + if (!warned) { + warned = true; + if (code !== undefined) { + if (!codesWarned.has(code)) { + process.emitWarning(msg, "DeprecationWarning", code, deprecated); + codesWarned.add(code); + } + } else { + // deno-lint-ignore no-explicit-any + process.emitWarning(msg, "DeprecationWarning", deprecated as any); + } + } + if (new.target) { + return Reflect.construct(fn, args, new.target); + } + return Reflect.apply(fn, this, args); + } + + // The wrapper will keep the same prototype as fn to maintain prototype chain + Object.setPrototypeOf(deprecated, fn); + if (fn.prototype) { + // Setting this (rather than using Object.setPrototype, as above) ensures + // that calling the unwrapped constructor gives an instanceof the wrapped + // constructor. + deprecated.prototype = fn.prototype; + } + + return deprecated; +} + +export { getSystemErrorName, isDeepStrictEqual }; + +export default { + format, + formatWithOptions, + inspect, + isArray, + isBoolean, + isNull, + isNullOrUndefined, + isNumber, + isString, + isSymbol, + isUndefined, + isObject, + isError, + isFunction, + isRegExp, + isDate, + isPrimitive, + isBuffer, + _extend, + getSystemErrorName, + deprecate, + callbackify, + promisify, + inherits, + types, + stripVTControlCharacters, + TextDecoder, + TextEncoder, + log, + debuglog, + isDeepStrictEqual, +}; diff --git a/ext/node/polyfills/util/types.ts b/ext/node/polyfills/util/types.ts new file mode 100644 index 0000000000..2ed7d16914 --- /dev/null +++ b/ext/node/polyfills/util/types.ts @@ -0,0 +1,4 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import * as types from "internal:deno_node/polyfills/internal/util/types.ts"; +export * from "internal:deno_node/polyfills/internal/util/types.ts"; +export default { ...types }; diff --git a/ext/node/polyfills/v8.ts b/ext/node/polyfills/v8.ts new file mode 100644 index 0000000000..f186ff023d --- /dev/null +++ b/ext/node/polyfills/v8.ts @@ -0,0 +1,90 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export function cachedDataVersionTag() { + notImplemented("v8.cachedDataVersionTag"); +} +export function getHeapCodeStatistics() { + notImplemented("v8.getHeapCodeStatistics"); +} +export function getHeapSnapshot() { + notImplemented("v8.getHeapSnapshot"); +} +export function getHeapSpaceStatistics() { + notImplemented("v8.getHeapSpaceStatistics"); +} +export function getHeapStatistics() { + notImplemented("v8.getHeapStatistics"); +} +export function setFlagsFromString() { + notImplemented("v8.setFlagsFromString"); +} +export function stopCoverage() { + notImplemented("v8.stopCoverage"); +} +export function takeCoverage() { + notImplemented("v8.takeCoverage"); +} +export function writeHeapSnapshot() { + notImplemented("v8.writeHeapSnapshot"); +} +export function serialize() { + notImplemented("v8.serialize"); +} +export function deserialize() { + notImplemented("v8.deserialize"); +} +export class Serializer { + constructor() { + notImplemented("v8.Serializer.prototype.constructor"); + } +} +export class Deserializer { + constructor() { + notImplemented("v8.Deserializer.prototype.constructor"); + } +} +export class DefaultSerializer { + constructor() { + notImplemented("v8.DefaultSerializer.prototype.constructor"); + } +} +export class DefaultDeserializer { + constructor() { + notImplemented("v8.DefaultDeserializer.prototype.constructor"); + } +} +export const promiseHooks = { + onInit() { + notImplemented("v8.promiseHooks.onInit"); + }, + onSettled() { + notImplemented("v8.promiseHooks.onSetttled"); + }, + onBefore() { + notImplemented("v8.promiseHooks.onBefore"); + }, + createHook() { + notImplemented("v8.promiseHooks.createHook"); + }, +}; +export default { + cachedDataVersionTag, + getHeapCodeStatistics, + getHeapSnapshot, + getHeapSpaceStatistics, + getHeapStatistics, + setFlagsFromString, + stopCoverage, + takeCoverage, + writeHeapSnapshot, + serialize, + deserialize, + Serializer, + Deserializer, + DefaultSerializer, + DefaultDeserializer, + promiseHooks, +}; diff --git a/ext/node/polyfills/vm.ts b/ext/node/polyfills/vm.ts new file mode 100644 index 0000000000..0d5de72c60 --- /dev/null +++ b/ext/node/polyfills/vm.ts @@ -0,0 +1,83 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file no-explicit-any + +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; + +export class Script { + code: string; + constructor(code: string, _options = {}) { + this.code = `${code}`; + } + + runInThisContext(_options: any) { + return eval.call(globalThis, this.code); + } + + runInContext(_contextifiedObject: any, _options: any) { + notImplemented("Script.prototype.runInContext"); + } + + runInNewContext(_contextObject: any, _options: any) { + notImplemented("Script.prototype.runInNewContext"); + } + + createCachedData() { + notImplemented("Script.prototyp.createCachedData"); + } +} + +export function createContext(_contextObject: any, _options: any) { + notImplemented("createContext"); +} + +export function createScript(code: string, options: any) { + return new Script(code, options); +} + +export function runInContext( + _code: string, + _contextifiedObject: any, + _options: any, +) { + notImplemented("runInContext"); +} + +export function runInNewContext( + _code: string, + _contextObject: any, + _options: any, +) { + notImplemented("runInNewContext"); +} + +export function runInThisContext( + code: string, + options: any, +) { + return createScript(code, options).runInThisContext(options); +} + +export function isContext(_maybeContext: any) { + notImplemented("isContext"); +} + +export function compileFunction(_code: string, _params: any, _options: any) { + notImplemented("compileFunction"); +} + +export function measureMemory(_options: any) { + notImplemented("measureMemory"); +} + +export default { + Script, + createContext, + createScript, + runInContext, + runInNewContext, + runInThisContext, + isContext, + compileFunction, + measureMemory, +}; diff --git a/ext/node/polyfills/wasi.ts b/ext/node/polyfills/wasi.ts new file mode 100644 index 0000000000..a68063ba90 --- /dev/null +++ b/ext/node/polyfills/wasi.ts @@ -0,0 +1,11 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. + +class Context { + constructor() { + throw new Error("Context is currently not supported"); + } +} + +export const WASI = Context; + +export default { WASI }; diff --git a/ext/node/polyfills/worker_threads.ts b/ext/node/polyfills/worker_threads.ts new file mode 100644 index 0000000000..7bca5fc4ef --- /dev/null +++ b/ext/node/polyfills/worker_threads.ts @@ -0,0 +1,248 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +// Copyright Joyent and Node contributors. All rights reserved. MIT license. + +import { resolve, toFileUrl } from "internal:deno_node/polyfills/path.ts"; +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { EventEmitter } from "internal:deno_node/polyfills/events.ts"; + +const environmentData = new Map(); +let threads = 0; + +export interface WorkerOptions { + // only for typings + argv?: unknown[]; + env?: Record; + execArgv?: string[]; + stdin?: boolean; + stdout?: boolean; + stderr?: boolean; + trackUnmanagedFds?: boolean; + resourceLimits?: { + maxYoungGenerationSizeMb?: number; + maxOldGenerationSizeMb?: number; + codeRangeSizeMb?: number; + stackSizeMb?: number; + }; + + eval?: boolean; + transferList?: Transferable[]; + workerData?: unknown; +} + +const kHandle = Symbol("kHandle"); +const PRIVATE_WORKER_THREAD_NAME = "$DENO_STD_NODE_WORKER_THREAD"; +class _Worker extends EventEmitter { + readonly threadId: number; + readonly resourceLimits: Required< + NonNullable + > = { + maxYoungGenerationSizeMb: -1, + maxOldGenerationSizeMb: -1, + codeRangeSizeMb: -1, + stackSizeMb: 4, + }; + private readonly [kHandle]: Worker; + + postMessage: Worker["postMessage"]; + + constructor(specifier: URL | string, options?: WorkerOptions) { + notImplemented("Worker"); + super(); + if (options?.eval === true) { + specifier = `data:text/javascript,${specifier}`; + } else if (typeof specifier === "string") { + // @ts-ignore This API is temporarily disabled + specifier = toFileUrl(resolve(specifier)); + } + const handle = this[kHandle] = new Worker( + specifier, + { + name: PRIVATE_WORKER_THREAD_NAME, + type: "module", + } as globalThis.WorkerOptions, // bypass unstable type error + ); + handle.addEventListener( + "error", + (event) => this.emit("error", event.error || event.message), + ); + handle.addEventListener( + "messageerror", + (event) => this.emit("messageerror", event.data), + ); + handle.addEventListener( + "message", + (event) => this.emit("message", event.data), + ); + handle.postMessage({ + environmentData, + threadId: (this.threadId = ++threads), + workerData: options?.workerData, + }, options?.transferList || []); + this.postMessage = handle.postMessage.bind(handle); + this.emit("online"); + } + + terminate() { + this[kHandle].terminate(); + this.emit("exit", 0); + } + + readonly getHeapSnapshot = () => + notImplemented("Worker.prototype.getHeapSnapshot"); + // fake performance + readonly performance = globalThis.performance; +} + +export const isMainThread = + // deno-lint-ignore no-explicit-any + (globalThis as any).name !== PRIVATE_WORKER_THREAD_NAME; + +// fake resourceLimits +export const resourceLimits = isMainThread ? {} : { + maxYoungGenerationSizeMb: 48, + maxOldGenerationSizeMb: 2048, + codeRangeSizeMb: 0, + stackSizeMb: 4, +}; + +const threadId = 0; +const workerData: unknown = null; + +// Like https://github.com/nodejs/node/blob/48655e17e1d84ba5021d7a94b4b88823f7c9c6cf/lib/internal/event_target.js#L611 +interface NodeEventTarget extends + Pick< + EventEmitter, + "eventNames" | "listenerCount" | "emit" | "removeAllListeners" + > { + setMaxListeners(n: number): void; + getMaxListeners(): number; + // deno-lint-ignore no-explicit-any + off(eventName: string, listener: (...args: any[]) => void): NodeEventTarget; + // deno-lint-ignore no-explicit-any + on(eventName: string, listener: (...args: any[]) => void): NodeEventTarget; + // deno-lint-ignore no-explicit-any + once(eventName: string, listener: (...args: any[]) => void): NodeEventTarget; + addListener: NodeEventTarget["on"]; + removeListener: NodeEventTarget["off"]; +} + +type ParentPort = typeof self & NodeEventTarget; + +// deno-lint-ignore no-explicit-any +const parentPort: ParentPort = null as any; + +/* +if (!isMainThread) { + // deno-lint-ignore no-explicit-any + delete (globalThis as any).name; + // deno-lint-ignore no-explicit-any + const listeners = new WeakMap<(...args: any[]) => void, (ev: any) => any>(); + + parentPort = self as ParentPort; + parentPort.off = parentPort.removeListener = function ( + this: ParentPort, + name, + listener, + ) { + this.removeEventListener(name, listeners.get(listener)!); + listeners.delete(listener); + return this; + }; + parentPort.on = parentPort.addListener = function ( + this: ParentPort, + name, + listener, + ) { + // deno-lint-ignore no-explicit-any + const _listener = (ev: any) => listener(ev.data); + listeners.set(listener, _listener); + this.addEventListener(name, _listener); + return this; + }; + parentPort.once = function (this: ParentPort, name, listener) { + // deno-lint-ignore no-explicit-any + const _listener = (ev: any) => listener(ev.data); + listeners.set(listener, _listener); + this.addEventListener(name, _listener); + return this; + }; + + // mocks + parentPort.setMaxListeners = () => {}; + parentPort.getMaxListeners = () => Infinity; + parentPort.eventNames = () => [""]; + parentPort.listenerCount = () => 0; + + parentPort.emit = () => notImplemented("parentPort.emit"); + parentPort.removeAllListeners = () => + notImplemented("parentPort.removeAllListeners"); + + // Receive startup message + [{ threadId, workerData, environmentData }] = await once( + parentPort, + "message", + ); + + // alias + parentPort.addEventListener("offline", () => { + parentPort.emit("close"); + }); +} +*/ + +export function getEnvironmentData(key: unknown) { + notImplemented("getEnvironmentData"); + return environmentData.get(key); +} + +export function setEnvironmentData(key: unknown, value?: unknown) { + notImplemented("setEnvironmentData"); + if (value === undefined) { + environmentData.delete(key); + } else { + environmentData.set(key, value); + } +} + +// deno-lint-ignore no-explicit-any +const _MessagePort: typeof MessagePort = (globalThis as any).MessagePort; +const _MessageChannel: typeof MessageChannel = + // deno-lint-ignore no-explicit-any + (globalThis as any).MessageChannel; +export const BroadcastChannel = globalThis.BroadcastChannel; +export const SHARE_ENV = Symbol.for("nodejs.worker_threads.SHARE_ENV"); +export function markAsUntransferable() { + notImplemented("markAsUntransferable"); +} +export function moveMessagePortToContext() { + notImplemented("moveMessagePortToContext"); +} +export function receiveMessageOnPort() { + notImplemented("receiveMessageOnPort"); +} +export { + _MessageChannel as MessageChannel, + _MessagePort as MessagePort, + _Worker as Worker, + parentPort, + threadId, + workerData, +}; + +export default { + markAsUntransferable, + moveMessagePortToContext, + receiveMessageOnPort, + MessagePort: _MessagePort, + MessageChannel: _MessageChannel, + BroadcastChannel, + Worker: _Worker, + getEnvironmentData, + setEnvironmentData, + SHARE_ENV, + threadId, + workerData, + resourceLimits, + parentPort, + isMainThread, +}; diff --git a/ext/node/polyfills/zlib.ts b/ext/node/polyfills/zlib.ts new file mode 100644 index 0000000000..ac52b4d4a5 --- /dev/null +++ b/ext/node/polyfills/zlib.ts @@ -0,0 +1,154 @@ +// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. +import { notImplemented } from "internal:deno_node/polyfills/_utils.ts"; +import { zlib as constants } from "internal:deno_node/polyfills/internal_binding/constants.ts"; +import { + codes, + createDeflate, + createDeflateRaw, + createGunzip, + createGzip, + createInflate, + createInflateRaw, + createUnzip, + Deflate, + deflate, + DeflateRaw, + deflateRaw, + deflateRawSync, + deflateSync, + Gunzip, + gunzip, + gunzipSync, + Gzip, + gzip, + gzipSync, + Inflate, + inflate, + InflateRaw, + inflateRaw, + inflateRawSync, + inflateSync, + Unzip, + unzip, + unzipSync, +} from "internal:deno_node/polyfills/_zlib.mjs"; +export class Options { + constructor() { + notImplemented("Options.prototype.constructor"); + } +} +export class BrotliOptions { + constructor() { + notImplemented("BrotliOptions.prototype.constructor"); + } +} +export class BrotliCompress { + constructor() { + notImplemented("BrotliCompress.prototype.constructor"); + } +} +export class BrotliDecompress { + constructor() { + notImplemented("BrotliDecompress.prototype.constructor"); + } +} +export class ZlibBase { + constructor() { + notImplemented("ZlibBase.prototype.constructor"); + } +} +export { constants }; +export function createBrotliCompress() { + notImplemented("createBrotliCompress"); +} +export function createBrotliDecompress() { + notImplemented("createBrotliDecompress"); +} +export function brotliCompress() { + notImplemented("brotliCompress"); +} +export function brotliCompressSync() { + notImplemented("brotliCompressSync"); +} +export function brotliDecompress() { + notImplemented("brotliDecompress"); +} +export function brotliDecompressSync() { + notImplemented("brotliDecompressSync"); +} + +export default { + Options, + BrotliOptions, + BrotliCompress, + BrotliDecompress, + Deflate, + DeflateRaw, + Gunzip, + Gzip, + Inflate, + InflateRaw, + Unzip, + ZlibBase, + constants, + codes, + createBrotliCompress, + createBrotliDecompress, + createDeflate, + createDeflateRaw, + createGunzip, + createGzip, + createInflate, + createInflateRaw, + createUnzip, + brotliCompress, + brotliCompressSync, + brotliDecompress, + brotliDecompressSync, + deflate, + deflateSync, + deflateRaw, + deflateRawSync, + gunzip, + gunzipSync, + gzip, + gzipSync, + inflate, + inflateSync, + inflateRaw, + inflateRawSync, + unzip, + unzipSync, +}; + +export { + codes, + createDeflate, + createDeflateRaw, + createGunzip, + createGzip, + createInflate, + createInflateRaw, + createUnzip, + Deflate, + deflate, + DeflateRaw, + deflateRaw, + deflateRawSync, + deflateSync, + Gunzip, + gunzip, + gunzipSync, + Gzip, + gzip, + gzipSync, + Inflate, + inflate, + InflateRaw, + inflateRaw, + inflateRawSync, + inflateSync, + Unzip, + unzip, + unzipSync, +}; diff --git a/ext/web/00_infra.js b/ext/web/00_infra.js index c44b124c94..0a8491563c 100644 --- a/ext/web/00_infra.js +++ b/ext/web/00_infra.js @@ -249,6 +249,60 @@ function forgivingBase64Decode(data) { return ops.op_base64_decode(data); } +// Taken from std/encoding/base64url.ts +/* + * Some variants allow or require omitting the padding '=' signs: + * https://en.wikipedia.org/wiki/Base64#The_URL_applications + * @param base64url + */ +/** + * @param {string} base64url + * @returns {string} + */ +function addPaddingToBase64url(base64url) { + if (base64url.length % 4 === 2) return base64url + "=="; + if (base64url.length % 4 === 3) return base64url + "="; + if (base64url.length % 4 === 1) { + throw new TypeError("Illegal base64url string!"); + } + return base64url; +} + +/** + * @param {string} base64url + * @returns {string} + */ +function convertBase64urlToBase64(base64url) { + if (!/^[-_A-Z0-9]*?={0,2}$/i.test(base64url)) { + // Contains characters not part of base64url spec. + throw new TypeError("Failed to decode base64url: invalid character"); + } + return addPaddingToBase64url(base64url).replace(/\-/g, "+").replace( + /_/g, + "/", + ); +} + +/** + * Encodes a given ArrayBuffer or string into a base64url representation + * @param {ArrayBuffer | string} data + * @returns {string} + */ +function forgivingBase64UrlEncode(data) { + return forgivingBase64Encode( + typeof data === "string" ? new TextEncoder().encode(data) : data, + ).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); +} + +/** + * Converts given base64url encoded data back to original + * @param {string} b64url + * @returns {Uint8Array} + */ +function forgivingBase64UrlDecode(b64url) { + return forgivingBase64Decode(convertBase64urlToBase64(b64url)); +} + /** * @param {string} char * @returns {boolean} @@ -320,6 +374,8 @@ export { collectSequenceOfCodepoints, forgivingBase64Decode, forgivingBase64Encode, + forgivingBase64UrlDecode, + forgivingBase64UrlEncode, HTTP_QUOTED_STRING_TOKEN_POINT, HTTP_QUOTED_STRING_TOKEN_POINT_RE, HTTP_TAB_OR_SPACE, diff --git a/runtime/build.rs b/runtime/build.rs index e70e8fe5dc..bbdec8f57e 100644 --- a/runtime/build.rs +++ b/runtime/build.rs @@ -26,8 +26,12 @@ mod not_docs { let should_transpile = match media_type { MediaType::JavaScript => false, + MediaType::Mjs => false, MediaType::TypeScript => true, - _ => panic!("Unsupported media type for snapshotting {media_type:?}"), + _ => panic!( + "Unsupported media type for snapshotting {media_type:?} for file {}", + file_source.specifier + ), }; if !should_transpile { @@ -42,7 +46,12 @@ mod not_docs { scope_analysis: false, maybe_syntax: None, })?; - let transpiled_source = parsed.transpile(&Default::default())?; + let transpiled_source = parsed.transpile(&deno_ast::EmitOptions { + imports_not_used_as_values: deno_ast::ImportsNotUsedAsValues::Remove, + inline_source_map: false, + ..Default::default() + })?; + Ok(transpiled_source.text) } @@ -171,7 +180,8 @@ mod not_docs { "deno_crypto", "deno_webgpu", "deno_broadcast_channel", - "deno_node", + // FIXME(bartlomieju): this should be reenabled + // "deno_node", "deno_ffi", "deno_net", "deno_napi", @@ -225,7 +235,6 @@ mod not_docs { deno_broadcast_channel::InMemoryBroadcastChannel::default(), false, // No --unstable. ), - deno_node::init::(None), deno_ffi::init::(false), deno_net::init::( None, false, // No --unstable. @@ -235,6 +244,10 @@ mod not_docs { deno_http::init(), deno_flash::init::(false), // No --unstable runtime_extension, + // FIXME(bartlomieju): these extensions are specified last, because they + // depend on `runtime`, even though it should be other way around + deno_node::init::(None), + deno_node::init_polyfill(), ]; if let Some(additional_extension) = maybe_additional_extension { diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index fa9b0a20d2..399d12d385 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -515,6 +515,11 @@ function bootstrapMainRuntime(runtimeOptions) { ObjectDefineProperty(globalThis, "Deno", util.readOnly(finalDenoNs)); util.log("args", runtimeOptions.args); + + // FIXME(bartlomieju): this should be a helper function that is placed in + // "internals" namespace + // Initialize Node polyfills + // internals.__bootstrapNodeProcess(); } function bootstrapWorkerRuntime( diff --git a/runtime/web_worker.rs b/runtime/web_worker.rs index 052d954514..df75d79c15 100644 --- a/runtime/web_worker.rs +++ b/runtime/web_worker.rs @@ -435,6 +435,7 @@ impl WebWorker { options.unsafely_ignore_certificate_errors.clone(), ), deno_napi::init::(), + deno_node::init_polyfill(), deno_node::init::(options.npm_resolver), ops::os::init_for_worker(), ops::permissions::init(), diff --git a/runtime/worker.rs b/runtime/worker.rs index bffa912657..141b5a6506 100644 --- a/runtime/worker.rs +++ b/runtime/worker.rs @@ -267,6 +267,7 @@ impl MainWorker { options.unsafely_ignore_certificate_errors.clone(), ), deno_napi::init::(), + deno_node::init_polyfill(), deno_node::init::(options.npm_resolver), ops::os::init(exit_code.clone()), ops::permissions::init(),