mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is transpiled and snapshotted during the build process. During the first pass a minimal amount of work was done to create the snapshot, a lot of code in "ext/node" depends on presence of "Deno" global. This code will be gradually fixed in the follow up PRs to migrate it to import relevant APIs from "internal:" modules. Currently the code from snapshot is not used in any way, and all Node/npm compatibility still uses code from "https://deno.land/std/node" (or from the location specified by "DENO_NODE_COMPAT_URL"). This will also be handled in a follow up PRs. --------- Co-authored-by: crowlkats <crowlkats@toaxl.com> Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com> Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
This commit is contained in:
parent
1d00bbe47e
commit
d47147fb6a
332 changed files with 98173 additions and 8 deletions
54
Cargo.lock
generated
54
Cargo.lock
generated
|
@ -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"
|
||||
|
|
|
@ -343,6 +343,7 @@ fn create_cli_snapshot(snapshot_path: PathBuf) {
|
|||
false, // No --unstable.
|
||||
),
|
||||
deno_node::init::<PermissionsContainer>(None), // No --unstable.
|
||||
deno_node::init_polyfill(),
|
||||
deno_ffi::init::<PermissionsContainer>(false),
|
||||
deno_net::init::<PermissionsContainer>(
|
||||
None, false, // No --unstable.
|
||||
|
|
|
@ -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"
|
||||
|
|
117
ext/node/crypto/digest.rs
Normal file
117
ext/node/crypto/digest.rs
Normal file
|
@ -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<md4::Md4>),
|
||||
Md5(Box<md5::Md5>),
|
||||
Ripemd160(Box<ripemd::Ripemd160>),
|
||||
Sha1(Box<sha1::Sha1>),
|
||||
Sha224(Box<sha2::Sha224>),
|
||||
Sha256(Box<sha2::Sha256>),
|
||||
Sha384(Box<sha2::Sha384>),
|
||||
Sha512(Box<sha2::Sha512>),
|
||||
}
|
||||
|
||||
pub struct Context {
|
||||
pub hash: Rc<RefCell<Hash>>,
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new(algorithm: &str) -> Result<Self, AnyError> {
|
||||
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<Box<[u8]>, 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<str> {
|
||||
"cryptoDigest".into()
|
||||
}
|
||||
}
|
||||
|
||||
use Hash::*;
|
||||
|
||||
impl Hash {
|
||||
pub fn new(algorithm_name: &str) -> Result<Self, AnyError> {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
49
ext/node/crypto/mod.rs
Normal file
49
ext/node/crypto/mod.rs
Normal file
|
@ -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<ResourceId, AnyError> {
|
||||
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::<digest::Context>(rid)?;
|
||||
context.update(data);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[op]
|
||||
pub fn op_node_hash_digest(
|
||||
state: &mut OpState,
|
||||
rid: ResourceId,
|
||||
) -> Result<ZeroCopyBuf, AnyError> {
|
||||
let context = state.resource_table.take::<digest::Context>(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<ResourceId, AnyError> {
|
||||
let context = state.resource_table.get::<digest::Context>(rid)?;
|
||||
Ok(state.resource_table.add(context.as_ref().clone()))
|
||||
}
|
341
ext/node/lib.rs
341
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<String> = Lazy::new(|| {
|
||||
let now = std::time::SystemTime::now();
|
||||
let seconds = now
|
||||
|
@ -83,10 +84,344 @@ pub static NODE_ENV_VAR_ALLOWLIST: Lazy<HashSet<String>> = 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<P: NodePermissions + 'static>(
|
||||
maybe_npm_resolver: Option<Rc<dyn RequireNpmResolver>>,
|
||||
) -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
Extension::builder("deno_node_loading")
|
||||
.esm(include_js_files!(
|
||||
"01_node.js",
|
||||
"02_require.js",
|
||||
|
|
|
@ -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 {
|
||||
|
|
248
ext/node/polyfills/README.md
Normal file
248
ext/node/polyfills/README.md
Normal file
|
@ -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 -- <pattern-to-match>
|
||||
```
|
||||
|
||||
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).
|
83
ext/node/polyfills/_core.ts
Normal file
83
ext/node/polyfills/_core.ts
Normal file
|
@ -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<unknown>): [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",
|
||||
);
|
||||
},
|
||||
},
|
||||
};
|
2
ext/node/polyfills/_crypto/crypto_browserify/README.md
Normal file
2
ext/node/polyfills/_crypto/crypto_browserify/README.md
Normal file
|
@ -0,0 +1,2 @@
|
|||
This directory contains the libraries ported from
|
||||
[crypto-browserify](https://github.com/crypto-browserify) organization.
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
}
|
|
@ -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");
|
||||
};
|
96
ext/node/polyfills/_crypto/crypto_browserify/asn1.js/mod.js
Normal file
96
ext/node/polyfills/_crypto/crypto_browserify/asn1.js/mod.js
Normal file
|
@ -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,
|
||||
};
|
3605
ext/node/polyfills/_crypto/crypto_browserify/bn.js/bn.js
Normal file
3605
ext/node/polyfills/_crypto/crypto_browserify/bn.js/bn.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
};
|
|
@ -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;
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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);
|
||||
};
|
|
@ -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];
|
||||
}
|
|
@ -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);
|
||||
};
|
|
@ -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;
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
110
ext/node/polyfills/_crypto/crypto_browserify/cipher_base.js
Normal file
110
ext/node/polyfills/_crypto/crypto_browserify/cipher_base.js
Normal file
|
@ -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;
|
|
@ -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;
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"//": "Sets type module to make compat mode interpret .js as ESM",
|
||||
"type": "module"
|
||||
}
|
117
ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/asn1.js
Normal file
117
ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/asn1.js
Normal file
|
@ -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(),
|
||||
);
|
||||
});
|
|
@ -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;
|
|
@ -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,
|
||||
};
|
||||
}
|
138
ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/mod.js
Normal file
138
ext/node/polyfills/_crypto/crypto_browserify/parse_asn1/mod.js
Normal file
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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(),
|
||||
);
|
||||
}
|
|
@ -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;
|
||||
}
|
47
ext/node/polyfills/_crypto/crypto_browserify/randombytes.ts
Normal file
47
ext/node/polyfills/_crypto/crypto_browserify/randombytes.ts
Normal file
|
@ -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;
|
||||
}
|
826
ext/node/polyfills/_events.d.ts
vendored
Normal file
826
ext/node/polyfills/_events.d.ts
vendored
Normal file
|
@ -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<any>;
|
||||
|
||||
/**
|
||||
* 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<any[]>;
|
||||
export function once(
|
||||
emitter: EventTarget,
|
||||
eventName: string,
|
||||
options?: StaticEventEmitterOptions,
|
||||
): Promise<any[]>;
|
||||
|
||||
/**
|
||||
* `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<string | symbol>;
|
||||
|
||||
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<any[]>;
|
||||
static once(
|
||||
emitter: EventTarget,
|
||||
eventName: string,
|
||||
options?: StaticEventEmitterOptions,
|
||||
): Promise<any[]>;
|
||||
/**
|
||||
* ```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;
|
1033
ext/node/polyfills/_events.mjs
Normal file
1033
ext/node/polyfills/_events.mjs
Normal file
File diff suppressed because it is too large
Load diff
127
ext/node/polyfills/_fs/_fs_access.ts
Normal file
127
ext/node/polyfills/_fs/_fs_access.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
73
ext/node/polyfills/_fs/_fs_appendFile.ts
Normal file
73
ext/node/polyfills/_fs/_fs_appendFile.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
69
ext/node/polyfills/_fs/_fs_chmod.ts
Normal file
69
ext/node/polyfills/_fs/_fs_chmod.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
56
ext/node/polyfills/_fs/_fs_chown.ts
Normal file
56
ext/node/polyfills/_fs/_fs_chown.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
21
ext/node/polyfills/_fs/_fs_close.ts
Normal file
21
ext/node/polyfills/_fs/_fs_close.ts
Normal file
|
@ -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);
|
||||
}
|
233
ext/node/polyfills/_fs/_fs_common.ts
Normal file
233
ext/node/polyfills/_fs/_fs_common.ts
Normal file
|
@ -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);
|
||||
}
|
39
ext/node/polyfills/_fs/_fs_constants.ts
Normal file
39
ext/node/polyfills/_fs/_fs_constants.ts
Normal file
|
@ -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;
|
88
ext/node/polyfills/_fs/_fs_copy.ts
Normal file
88
ext/node/polyfills/_fs/_fs_copy.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
104
ext/node/polyfills/_fs/_fs_dir.ts
Normal file
104
ext/node/polyfills/_fs/_fs_dir.ts
Normal file
|
@ -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<Deno.DirEntry, undefined> | null;
|
||||
#asyncIterator!: AsyncIterator<Deno.DirEntry, undefined> | 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<Dirent | null> {
|
||||
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<void> {
|
||||
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<Dirent> {
|
||||
try {
|
||||
while (true) {
|
||||
const dirent: Dirent | null = await this.read();
|
||||
if (dirent === null) {
|
||||
break;
|
||||
}
|
||||
yield dirent;
|
||||
}
|
||||
} finally {
|
||||
await this.close();
|
||||
}
|
||||
}
|
||||
}
|
46
ext/node/polyfills/_fs/_fs_dirent.ts
Normal file
46
ext/node/polyfills/_fs/_fs_dirent.ts
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
40
ext/node/polyfills/_fs/_fs_exists.ts
Normal file
40
ext/node/polyfills/_fs/_fs_exists.ts
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
13
ext/node/polyfills/_fs/_fs_fdatasync.ts
Normal file
13
ext/node/polyfills/_fs/_fs_fdatasync.ts
Normal file
|
@ -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);
|
||||
}
|
60
ext/node/polyfills/_fs/_fs_fstat.ts
Normal file
60
ext/node/polyfills/_fs/_fs_fstat.ts
Normal file
|
@ -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);
|
||||
}
|
13
ext/node/polyfills/_fs/_fs_fsync.ts
Normal file
13
ext/node/polyfills/_fs/_fs_fsync.ts
Normal file
|
@ -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);
|
||||
}
|
23
ext/node/polyfills/_fs/_fs_ftruncate.ts
Normal file
23
ext/node/polyfills/_fs/_fs_ftruncate.ts
Normal file
|
@ -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);
|
||||
}
|
50
ext/node/polyfills/_fs/_fs_futimes.ts
Normal file
50
ext/node/polyfills/_fs/_fs_futimes.ts
Normal file
|
@ -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);
|
||||
}
|
46
ext/node/polyfills/_fs/_fs_link.ts
Normal file
46
ext/node/polyfills/_fs/_fs_link.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
67
ext/node/polyfills/_fs/_fs_lstat.ts
Normal file
67
ext/node/polyfills/_fs/_fs_lstat.ts
Normal file
|
@ -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<Stats>)
|
||||
& ((path: string | URL, options: { bigint: false }) => Promise<Stats>)
|
||||
& ((path: string | URL, options: { bigint: true }) => Promise<BigIntStats>)
|
||||
);
|
||||
|
||||
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);
|
||||
}
|
77
ext/node/polyfills/_fs/_fs_mkdir.ts
Normal file
77
ext/node/polyfills/_fs/_fs_mkdir.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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 });
|
||||
}
|
||||
}
|
115
ext/node/polyfills/_fs/_fs_mkdtemp.ts
Normal file
115
ext/node/polyfills/_fs/_fs_mkdtemp.ts
Normal file
|
@ -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<string>;
|
||||
|
||||
// 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;
|
||||
}
|
198
ext/node/polyfills/_fs/_fs_open.ts
Normal file
198
ext/node/polyfills/_fs/_fs_open.ts
Normal file
|
@ -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<number>)
|
||||
& ((path: string | Buffer | URL, flags: openFlags) => Promise<number>)
|
||||
& ((path: string | Buffer | URL, mode?: number) => Promise<number>)
|
||||
& ((
|
||||
path: string | Buffer | URL,
|
||||
flags?: openFlags,
|
||||
mode?: number,
|
||||
) => Promise<number>)
|
||||
);
|
||||
|
||||
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)
|
||||
))
|
||||
);
|
||||
}
|
89
ext/node/polyfills/_fs/_fs_opendir.ts
Normal file
89
ext/node/polyfills/_fs/_fs_opendir.ts
Normal file
|
@ -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<Dir>;
|
||||
|
||||
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" });
|
||||
}
|
||||
}
|
197
ext/node/polyfills/_fs/_fs_read.ts
Normal file
197
ext/node/polyfills/_fs/_fs_read.ts
Normal file
|
@ -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;
|
||||
}
|
108
ext/node/polyfills/_fs/_fs_readFile.ts
Normal file
108
ext/node/polyfills/_fs/_fs_readFile.ts
Normal file
|
@ -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<string>)
|
||||
& ((path: string | URL, opt?: BinaryOptionsArgument) => Promise<Buffer>)
|
||||
& ((path: string | URL, opt?: FileOptionsArgument) => Promise<Buffer>)
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
142
ext/node/polyfills/_fs/_fs_readdir.ts
Normal file
142
ext/node/polyfills/_fs/_fs_readdir.ts
Normal file
|
@ -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<string | Dirent>]
|
||||
) => 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<string | Dirent> = [];
|
||||
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<Dirent[]>)
|
||||
& ((path: string | Buffer | URL, options?: {
|
||||
withFileTypes?: false;
|
||||
encoding?: string;
|
||||
}) => Promise<string[]>)
|
||||
);
|
||||
|
||||
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<string | Dirent> {
|
||||
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;
|
||||
}
|
89
ext/node/polyfills/_fs/_fs_readlink.ts
Normal file
89
ext/node/polyfills/_fs/_fs_readlink.ts
Normal file
|
@ -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<Error>,
|
||||
linkString: MaybeEmpty<string | Uint8Array>,
|
||||
) => 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<string, Uint8Array | string>(
|
||||
Deno.readLink,
|
||||
(data: string): string | Uint8Array => maybeEncode(data, encoding),
|
||||
cb,
|
||||
path,
|
||||
);
|
||||
}
|
||||
|
||||
export const readlinkPromise = promisify(readlink) as (
|
||||
path: string | URL,
|
||||
opt?: ReadlinkOptions,
|
||||
) => Promise<string | Uint8Array>;
|
||||
|
||||
export function readlinkSync(
|
||||
path: string | URL,
|
||||
opt?: ReadlinkOptions,
|
||||
): string | Uint8Array {
|
||||
path = path instanceof URL ? fromFileUrl(path) : path;
|
||||
|
||||
return maybeEncode(Deno.readLinkSync(path), getEncoding(opt));
|
||||
}
|
35
ext/node/polyfills/_fs/_fs_realpath.ts
Normal file
35
ext/node/polyfills/_fs/_fs_realpath.ts
Normal file
|
@ -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<string>;
|
||||
|
||||
export function realpathSync(path: string): string {
|
||||
return Deno.realPathSync(path);
|
||||
}
|
||||
|
||||
realpathSync.native = realpathSync;
|
28
ext/node/polyfills/_fs/_fs_rename.ts
Normal file
28
ext/node/polyfills/_fs/_fs_rename.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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);
|
||||
}
|
81
ext/node/polyfills/_fs/_fs_rm.ts
Normal file
81
ext/node/polyfills/_fs/_fs_rm.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
108
ext/node/polyfills/_fs/_fs_rmdir.ts
Normal file
108
ext/node/polyfills/_fs/_fs_rmdir.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
314
ext/node/polyfills/_fs/_fs_stat.ts
Normal file
314
ext/node/polyfills/_fs/_fs_stat.ts
Normal file
|
@ -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<Stats>)
|
||||
& ((path: string | URL, options: { bigint: false }) => Promise<Stats>)
|
||||
& ((path: string | URL, options: { bigint: true }) => Promise<BigIntStats>)
|
||||
);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
46
ext/node/polyfills/_fs/_fs_symlink.ts
Normal file
46
ext/node/polyfills/_fs/_fs_symlink.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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 });
|
||||
}
|
33
ext/node/polyfills/_fs/_fs_truncate.ts
Normal file
33
ext/node/polyfills/_fs/_fs_truncate.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
export function truncateSync(path: string | URL, len?: number) {
|
||||
path = path instanceof URL ? fromFileUrl(path) : path;
|
||||
|
||||
Deno.truncateSync(path, len);
|
||||
}
|
15
ext/node/polyfills/_fs/_fs_unlink.ts
Normal file
15
ext/node/polyfills/_fs/_fs_unlink.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
export function unlinkSync(path: string | URL) {
|
||||
Deno.removeSync(path);
|
||||
}
|
61
ext/node/polyfills/_fs/_fs_utimes.ts
Normal file
61
ext/node/polyfills/_fs/_fs_utimes.ts
Normal file
|
@ -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<void>;
|
||||
|
||||
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);
|
||||
}
|
346
ext/node/polyfills/_fs/_fs_watch.ts
Normal file
346
ext/node/polyfills/_fs/_fs_watch.ts
Normal file
|
@ -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<Stats | null> => {
|
||||
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<T>(
|
||||
iterator: AsyncIterableIterator<T>,
|
||||
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<T>(
|
||||
iter: AsyncIterable<T>,
|
||||
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<Deno.FsEvent>(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<FSWatcher>)
|
||||
& ((
|
||||
filename: string | URL,
|
||||
listener: watchListener,
|
||||
) => Promise<FSWatcher>)
|
||||
& ((
|
||||
filename: string | URL,
|
||||
options: watchOptions,
|
||||
) => Promise<FSWatcher>)
|
||||
& ((filename: string | URL) => Promise<FSWatcher>)
|
||||
);
|
||||
|
||||
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<string, StatWatcher>();
|
||||
|
||||
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";
|
||||
}
|
||||
}
|
207
ext/node/polyfills/_fs/_fs_write.d.ts
vendored
Normal file
207
ext/node/polyfills/_fs/_fs_write.d.ts
vendored
Normal file
|
@ -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<TBuffer extends ArrayBufferView>(
|
||||
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<TBuffer extends ArrayBufferView>(
|
||||
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<TBuffer extends ArrayBufferView>(
|
||||
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<TBuffer extends ArrayBufferView>(
|
||||
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__<TBuffer extends ArrayBufferView>(
|
||||
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.
|
||||
*/
|
132
ext/node/polyfills/_fs/_fs_write.mjs
Normal file
132
ext/node/polyfills/_fs/_fs_write.mjs
Normal file
|
@ -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),
|
||||
);
|
||||
}
|
193
ext/node/polyfills/_fs/_fs_writeFile.ts
Normal file
193
ext/node/polyfills/_fs/_fs_writeFile.ts
Normal file
|
@ -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<number>;
|
||||
}
|
||||
|
||||
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<void>;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
65
ext/node/polyfills/_fs/_fs_writev.d.ts
vendored
Normal file
65
ext/node/polyfills/_fs/_fs_writev.d.ts
vendored
Normal file
|
@ -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<ArrayBufferView>,
|
||||
cb: (
|
||||
err: ErrnoException | null,
|
||||
bytesWritten: number,
|
||||
buffers: ArrayBufferView[],
|
||||
) => void,
|
||||
): void;
|
||||
export function writev(
|
||||
fd: number,
|
||||
buffers: ReadonlyArray<ArrayBufferView>,
|
||||
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<ArrayBufferView>,
|
||||
position?: number,
|
||||
): Promise<WriteVResult>;
|
||||
}
|
||||
/**
|
||||
* 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<ArrayBufferView>,
|
||||
position?: number,
|
||||
): number;
|
81
ext/node/polyfills/_fs/_fs_writev.mjs
Normal file
81
ext/node/polyfills/_fs/_fs_writev.mjs
Normal file
|
@ -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);
|
||||
}
|
1
ext/node/polyfills/_fs/testdata/hello.txt
vendored
Normal file
1
ext/node/polyfills/_fs/testdata/hello.txt
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
hello world
|
66
ext/node/polyfills/_global.d.ts
vendored
Normal file
66
ext/node/polyfills/_global.d.ts
vendored
Normal file
|
@ -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<T extends WritableStream>(
|
||||
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<string | Buffer>;
|
||||
}
|
||||
|
||||
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 {}
|
526
ext/node/polyfills/_http_agent.mjs
Normal file
526
ext/node/polyfills/_http_agent.mjs
Normal file
|
@ -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,
|
||||
};
|
29
ext/node/polyfills/_http_common.ts
Normal file
29
ext/node/polyfills/_http_common.ts
Normal file
|
@ -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,
|
||||
};
|
1099
ext/node/polyfills/_http_outgoing.ts
Normal file
1099
ext/node/polyfills/_http_outgoing.ts
Normal file
File diff suppressed because it is too large
Load diff
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue