1
0
Fork 0
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:
Bartek Iwańczuk 2023-02-14 17:38:45 +01:00 committed by GitHub
parent 1d00bbe47e
commit d47147fb6a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
332 changed files with 98173 additions and 8 deletions

54
Cargo.lock generated
View file

@ -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"

View file

@ -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.

View file

@ -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
View 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
View 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()))
}

View file

@ -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",

View file

@ -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 {

View 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).

View 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",
);
},
},
};

View file

@ -0,0 +1,2 @@
This directory contains the libraries ported from
[crypto-browserify](https://github.com/crypto-browserify) organization.

View file

@ -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;
};

View file

@ -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);
};

View file

@ -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;
};

View file

@ -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);

View file

@ -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;
}

View file

@ -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);
};

View file

@ -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;
}

View file

@ -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");
};

View 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,
};

File diff suppressed because it is too large Load diff

View file

@ -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);
};

View file

@ -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;

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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;
}
}
}

View file

@ -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);
}

View file

@ -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);
};

View file

@ -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;
};

View file

@ -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;
};

View file

@ -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;
};

View file

@ -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);
};

View file

@ -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);
};

View file

@ -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];
}

View file

@ -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);
};

View file

@ -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;

View file

@ -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;
}

View 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 { 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;

View 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;

View file

@ -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;

View file

@ -0,0 +1,4 @@
{
"//": "Sets type module to make compat mode interpret .js as ESM",
"type": "module"
}

View 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(),
);
});

View file

@ -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;

View file

@ -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,
};
}

View 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);
}

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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(),
);
}

View file

@ -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;
}

View 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
View 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;

File diff suppressed because it is too large Load diff

View 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;
}
}
}

View 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);
}

View 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;
}
}
}

View 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);
}

View 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);
}

View 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);
}

View 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;

View 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);
}
}

View 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();
}
}
}

View 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;
}
}

View 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;
}
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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);
}

View 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 });
}
}

View 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;
}

View 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)
))
);
}

View 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" });
}
}

View 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;
}

View 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;
}

View 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;
}

View 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));
}

View 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;

View 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);
}

View 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;
}
}
}

View 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);
}
}

View 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;
}
}
}

View 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 });
}

View 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);
}

View 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);
}

View 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);
}

View 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
View 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.
*/

View 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),
);
}

View 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
View 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;

View 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);
}

View file

@ -0,0 +1 @@
hello world

66
ext/node/polyfills/_global.d.ts vendored Normal file
View 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 {}

View 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,
};

View 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,
};

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