mirror of
https://github.com/denoland/deno.git
synced 2024-11-25 15:29:32 -05:00
Port internal TS code to JS (#6793)
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
This commit is contained in:
parent
53adde866d
commit
fa61956f03
204 changed files with 16611 additions and 19384 deletions
152
cli/build.rs
152
cli/build.rs
|
@ -1,9 +1,11 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
||||||
use deno_core::include_crate_modules;
|
|
||||||
|
use deno_core::js_check;
|
||||||
use deno_core::CoreIsolate;
|
use deno_core::CoreIsolate;
|
||||||
use deno_core::StartupData;
|
use deno_core::StartupData;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
|
@ -11,6 +13,67 @@ extern crate winapi;
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
extern crate winres;
|
extern crate winres;
|
||||||
|
|
||||||
|
fn create_snapshot(
|
||||||
|
mut isolate: CoreIsolate,
|
||||||
|
snapshot_path: &Path,
|
||||||
|
files: Vec<String>,
|
||||||
|
) {
|
||||||
|
for file in files {
|
||||||
|
println!("cargo:rerun-if-changed={}", file);
|
||||||
|
js_check(isolate.execute(&file, &std::fs::read_to_string(&file).unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let snapshot = isolate.snapshot();
|
||||||
|
let snapshot_slice: &[u8] = &*snapshot;
|
||||||
|
println!("Snapshot size: {}", snapshot_slice.len());
|
||||||
|
std::fs::write(&snapshot_path, snapshot_slice).unwrap();
|
||||||
|
println!("Snapshot written to: {} ", snapshot_path.display());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_runtime_snapshot(snapshot_path: &Path, files: Vec<String>) {
|
||||||
|
let runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
||||||
|
create_snapshot(runtime_isolate, snapshot_path, files);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_compiler_snapshot(
|
||||||
|
snapshot_path: &Path,
|
||||||
|
files: Vec<String>,
|
||||||
|
cwd: &Path,
|
||||||
|
) {
|
||||||
|
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
||||||
|
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
|
||||||
|
custom_libs.insert(
|
||||||
|
"lib.deno.window.d.ts".to_string(),
|
||||||
|
cwd.join("js2/lib.deno.window.d.ts"),
|
||||||
|
);
|
||||||
|
custom_libs.insert(
|
||||||
|
"lib.deno.worker.d.ts".to_string(),
|
||||||
|
cwd.join("js2/lib.deno.worker.d.ts"),
|
||||||
|
);
|
||||||
|
custom_libs.insert(
|
||||||
|
"lib.deno.shared_globals.d.ts".to_string(),
|
||||||
|
cwd.join("js2/lib.deno.shared_globals.d.ts"),
|
||||||
|
);
|
||||||
|
custom_libs.insert(
|
||||||
|
"lib.deno.ns.d.ts".to_string(),
|
||||||
|
cwd.join("js2/lib.deno.ns.d.ts"),
|
||||||
|
);
|
||||||
|
custom_libs.insert(
|
||||||
|
"lib.deno.unstable.d.ts".to_string(),
|
||||||
|
cwd.join("js2/lib.deno.unstable.d.ts"),
|
||||||
|
);
|
||||||
|
runtime_isolate.register_op(
|
||||||
|
"op_fetch_asset",
|
||||||
|
deno_typescript::op_fetch_asset(custom_libs),
|
||||||
|
);
|
||||||
|
|
||||||
|
js_check(
|
||||||
|
runtime_isolate.execute("typescript.js", deno_typescript::TYPESCRIPT_CODE),
|
||||||
|
);
|
||||||
|
|
||||||
|
create_snapshot(runtime_isolate, snapshot_path, files);
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// Don't build V8 if "cargo doc" is being run. This is to support docs.rs.
|
// Don't build V8 if "cargo doc" is being run. This is to support docs.rs.
|
||||||
if env::var_os("RUSTDOCFLAGS").is_some() {
|
if env::var_os("RUSTDOCFLAGS").is_some() {
|
||||||
|
@ -30,82 +93,31 @@ fn main() {
|
||||||
std::env::var("TARGET").unwrap()
|
std::env::var("TARGET").unwrap()
|
||||||
);
|
);
|
||||||
|
|
||||||
let extern_crate_modules = include_crate_modules![deno_core];
|
|
||||||
|
|
||||||
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
let c = PathBuf::from(env::var_os("CARGO_MANIFEST_DIR").unwrap());
|
||||||
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
|
||||||
// Main snapshot
|
// Main snapshot
|
||||||
let root_names = vec![c.join("js/main.ts")];
|
let runtime_snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
||||||
let bundle_path = o.join("CLI_SNAPSHOT.js");
|
let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
||||||
let snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
|
||||||
|
|
||||||
let main_module_name = deno_typescript::compile_bundle(
|
let mut js_files = std::fs::read_dir("js2/")
|
||||||
&bundle_path,
|
.unwrap()
|
||||||
root_names,
|
.map(|dir_entry| {
|
||||||
Some(extern_crate_modules.clone()),
|
let file = dir_entry.unwrap();
|
||||||
)
|
file.path().to_string_lossy().to_string()
|
||||||
.expect("Bundle compilation failed");
|
})
|
||||||
assert!(bundle_path.exists());
|
.filter(|filename| filename.ends_with(".js"))
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
js_files.sort();
|
||||||
|
|
||||||
deno_typescript::mksnapshot_bundle(
|
let runtime_files = js_files
|
||||||
&mut runtime_isolate,
|
.clone()
|
||||||
&snapshot_path,
|
.into_iter()
|
||||||
&bundle_path,
|
.filter(|filepath| !filepath.ends_with("compiler.js"))
|
||||||
&main_module_name,
|
.collect::<Vec<String>>();
|
||||||
)
|
create_runtime_snapshot(&runtime_snapshot_path, runtime_files);
|
||||||
.expect("Failed to create snapshot");
|
create_compiler_snapshot(&compiler_snapshot_path, js_files, &c);
|
||||||
|
|
||||||
// Compiler snapshot
|
|
||||||
let root_names = vec![c.join("js/compiler.ts")];
|
|
||||||
let bundle_path = o.join("COMPILER_SNAPSHOT.js");
|
|
||||||
let snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
|
||||||
|
|
||||||
let main_module_name = deno_typescript::compile_bundle(
|
|
||||||
&bundle_path,
|
|
||||||
root_names,
|
|
||||||
Some(extern_crate_modules),
|
|
||||||
)
|
|
||||||
.expect("Bundle compilation failed");
|
|
||||||
assert!(bundle_path.exists());
|
|
||||||
|
|
||||||
let mut runtime_isolate = CoreIsolate::new(StartupData::None, true);
|
|
||||||
|
|
||||||
let mut custom_libs: HashMap<String, PathBuf> = HashMap::new();
|
|
||||||
custom_libs.insert(
|
|
||||||
"lib.deno.window.d.ts".to_string(),
|
|
||||||
c.join("js/lib.deno.window.d.ts"),
|
|
||||||
);
|
|
||||||
custom_libs.insert(
|
|
||||||
"lib.deno.worker.d.ts".to_string(),
|
|
||||||
c.join("js/lib.deno.worker.d.ts"),
|
|
||||||
);
|
|
||||||
custom_libs.insert(
|
|
||||||
"lib.deno.shared_globals.d.ts".to_string(),
|
|
||||||
c.join("js/lib.deno.shared_globals.d.ts"),
|
|
||||||
);
|
|
||||||
custom_libs.insert(
|
|
||||||
"lib.deno.ns.d.ts".to_string(),
|
|
||||||
c.join("js/lib.deno.ns.d.ts"),
|
|
||||||
);
|
|
||||||
custom_libs.insert(
|
|
||||||
"lib.deno.unstable.d.ts".to_string(),
|
|
||||||
c.join("js/lib.deno.unstable.d.ts"),
|
|
||||||
);
|
|
||||||
runtime_isolate.register_op(
|
|
||||||
"op_fetch_asset",
|
|
||||||
deno_typescript::op_fetch_asset(custom_libs),
|
|
||||||
);
|
|
||||||
|
|
||||||
deno_typescript::mksnapshot_bundle_ts(
|
|
||||||
&mut runtime_isolate,
|
|
||||||
&snapshot_path,
|
|
||||||
&bundle_path,
|
|
||||||
&main_module_name,
|
|
||||||
)
|
|
||||||
.expect("Failed to create snapshot");
|
|
||||||
|
|
||||||
set_binary_metadata();
|
set_binary_metadata();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1461,8 +1461,8 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
assert!(r.is_err());
|
assert!(r.is_err());
|
||||||
|
|
||||||
let p =
|
let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("js/main.ts");
|
.join("js2/99_main.js");
|
||||||
let specifier =
|
let specifier =
|
||||||
ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap();
|
ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap();
|
||||||
let r = fetcher
|
let r = fetcher
|
||||||
|
@ -1484,8 +1484,8 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
assert!(r.is_err());
|
assert!(r.is_err());
|
||||||
|
|
||||||
let p =
|
let p = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("js/main.ts");
|
.join("js2/99_main.js");
|
||||||
let specifier =
|
let specifier =
|
||||||
ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap();
|
ModuleSpecifier::resolve_url_or_path(p.to_str().unwrap()).unwrap();
|
||||||
let r = fetcher
|
let r = fetcher
|
||||||
|
|
14
cli/js.rs
14
cli/js.rs
|
@ -2,19 +2,13 @@ pub const TS_VERSION: &str = env!("TS_VERSION");
|
||||||
|
|
||||||
pub static CLI_SNAPSHOT: &[u8] =
|
pub static CLI_SNAPSHOT: &[u8] =
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin"));
|
include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.bin"));
|
||||||
pub static CLI_SNAPSHOT_MAP: &[u8] =
|
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/CLI_SNAPSHOT.js.map"));
|
|
||||||
|
|
||||||
pub static COMPILER_SNAPSHOT: &[u8] =
|
pub static COMPILER_SNAPSHOT: &[u8] =
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin"));
|
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.bin"));
|
||||||
pub static COMPILER_SNAPSHOT_MAP: &[u8] =
|
pub static DENO_NS_LIB: &str = include_str!("js2/lib.deno.ns.d.ts");
|
||||||
include_bytes!(concat!(env!("OUT_DIR"), "/COMPILER_SNAPSHOT.js.map"));
|
|
||||||
|
|
||||||
pub static DENO_NS_LIB: &str = include_str!("js/lib.deno.ns.d.ts");
|
|
||||||
pub static SHARED_GLOBALS_LIB: &str =
|
pub static SHARED_GLOBALS_LIB: &str =
|
||||||
include_str!("js/lib.deno.shared_globals.d.ts");
|
include_str!("js2/lib.deno.shared_globals.d.ts");
|
||||||
pub static WINDOW_LIB: &str = include_str!("js/lib.deno.window.d.ts");
|
pub static WINDOW_LIB: &str = include_str!("js2/lib.deno.window.d.ts");
|
||||||
pub static UNSTABLE_NS_LIB: &str = include_str!("js/lib.deno.unstable.d.ts");
|
pub static UNSTABLE_NS_LIB: &str = include_str!("js2/lib.deno.unstable.d.ts");
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cli_snapshot() {
|
fn cli_snapshot() {
|
||||||
|
|
232
cli/js/buffer.ts
232
cli/js/buffer.ts
|
@ -1,232 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This code has been ported almost directly from Go's src/bytes/buffer.go
|
|
||||||
// Copyright 2009 The Go Authors. All rights reserved. BSD license.
|
|
||||||
// https://github.com/golang/go/blob/master/LICENSE
|
|
||||||
|
|
||||||
import type { Reader, Writer, ReaderSync, WriterSync } from "./io.ts";
|
|
||||||
import { assert } from "./util.ts";
|
|
||||||
|
|
||||||
// MIN_READ is the minimum ArrayBuffer size passed to a read call by
|
|
||||||
// buffer.ReadFrom. As long as the Buffer has at least MIN_READ bytes beyond
|
|
||||||
// what is required to hold the contents of r, readFrom() will not grow the
|
|
||||||
// underlying buffer.
|
|
||||||
const MIN_READ = 32 * 1024;
|
|
||||||
const MAX_SIZE = 2 ** 32 - 2;
|
|
||||||
|
|
||||||
// `off` is the offset into `dst` where it will at which to begin writing values
|
|
||||||
// from `src`.
|
|
||||||
// Returns the number of bytes copied.
|
|
||||||
function copyBytes(src: Uint8Array, dst: Uint8Array, off = 0): number {
|
|
||||||
const r = dst.byteLength - off;
|
|
||||||
if (src.byteLength > r) {
|
|
||||||
src = src.subarray(0, r);
|
|
||||||
}
|
|
||||||
dst.set(src, off);
|
|
||||||
return src.byteLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Buffer implements Reader, ReaderSync, Writer, WriterSync {
|
|
||||||
#buf: Uint8Array; // contents are the bytes buf[off : len(buf)]
|
|
||||||
#off = 0; // read at buf[off], write at buf[buf.byteLength]
|
|
||||||
|
|
||||||
constructor(ab?: ArrayBuffer) {
|
|
||||||
if (ab == null) {
|
|
||||||
this.#buf = new Uint8Array(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#buf = new Uint8Array(ab);
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes(options: { copy?: boolean } = { copy: true }): Uint8Array {
|
|
||||||
if (options.copy === false) return this.#buf.subarray(this.#off);
|
|
||||||
return this.#buf.slice(this.#off);
|
|
||||||
}
|
|
||||||
|
|
||||||
empty(): boolean {
|
|
||||||
return this.#buf.byteLength <= this.#off;
|
|
||||||
}
|
|
||||||
|
|
||||||
get length(): number {
|
|
||||||
return this.#buf.byteLength - this.#off;
|
|
||||||
}
|
|
||||||
|
|
||||||
get capacity(): number {
|
|
||||||
return this.#buf.buffer.byteLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
truncate(n: number): void {
|
|
||||||
if (n === 0) {
|
|
||||||
this.reset();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (n < 0 || n > this.length) {
|
|
||||||
throw Error("bytes.Buffer: truncation out of range");
|
|
||||||
}
|
|
||||||
this.#reslice(this.#off + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
reset(): void {
|
|
||||||
this.#reslice(0);
|
|
||||||
this.#off = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#tryGrowByReslice = (n: number): number => {
|
|
||||||
const l = this.#buf.byteLength;
|
|
||||||
if (n <= this.capacity - l) {
|
|
||||||
this.#reslice(l + n);
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
};
|
|
||||||
|
|
||||||
#reslice = (len: number): void => {
|
|
||||||
assert(len <= this.#buf.buffer.byteLength);
|
|
||||||
this.#buf = new Uint8Array(this.#buf.buffer, 0, len);
|
|
||||||
};
|
|
||||||
|
|
||||||
readSync(p: Uint8Array): number | null {
|
|
||||||
if (this.empty()) {
|
|
||||||
// Buffer is empty, reset to recover space.
|
|
||||||
this.reset();
|
|
||||||
if (p.byteLength === 0) {
|
|
||||||
// this edge case is tested in 'bufferReadEmptyAtEOF' test
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const nread = copyBytes(this.#buf.subarray(this.#off), p);
|
|
||||||
this.#off += nread;
|
|
||||||
return nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
read(p: Uint8Array): Promise<number | null> {
|
|
||||||
const rr = this.readSync(p);
|
|
||||||
return Promise.resolve(rr);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSync(p: Uint8Array): number {
|
|
||||||
const m = this.#grow(p.byteLength);
|
|
||||||
return copyBytes(p, this.#buf, m);
|
|
||||||
}
|
|
||||||
|
|
||||||
write(p: Uint8Array): Promise<number> {
|
|
||||||
const n = this.writeSync(p);
|
|
||||||
return Promise.resolve(n);
|
|
||||||
}
|
|
||||||
|
|
||||||
#grow = (n: number): number => {
|
|
||||||
const m = this.length;
|
|
||||||
// If buffer is empty, reset to recover space.
|
|
||||||
if (m === 0 && this.#off !== 0) {
|
|
||||||
this.reset();
|
|
||||||
}
|
|
||||||
// Fast: Try to grow by means of a reslice.
|
|
||||||
const i = this.#tryGrowByReslice(n);
|
|
||||||
if (i >= 0) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
const c = this.capacity;
|
|
||||||
if (n <= Math.floor(c / 2) - m) {
|
|
||||||
// We can slide things down instead of allocating a new
|
|
||||||
// ArrayBuffer. We only need m+n <= c to slide, but
|
|
||||||
// we instead let capacity get twice as large so we
|
|
||||||
// don't spend all our time copying.
|
|
||||||
copyBytes(this.#buf.subarray(this.#off), this.#buf);
|
|
||||||
} else if (c + n > MAX_SIZE) {
|
|
||||||
throw new Error("The buffer cannot be grown beyond the maximum size.");
|
|
||||||
} else {
|
|
||||||
// Not enough space anywhere, we need to allocate.
|
|
||||||
const buf = new Uint8Array(Math.min(2 * c + n, MAX_SIZE));
|
|
||||||
copyBytes(this.#buf.subarray(this.#off), buf);
|
|
||||||
this.#buf = buf;
|
|
||||||
}
|
|
||||||
// Restore this.#off and len(this.#buf).
|
|
||||||
this.#off = 0;
|
|
||||||
this.#reslice(Math.min(m + n, MAX_SIZE));
|
|
||||||
return m;
|
|
||||||
};
|
|
||||||
|
|
||||||
grow(n: number): void {
|
|
||||||
if (n < 0) {
|
|
||||||
throw Error("Buffer.grow: negative count");
|
|
||||||
}
|
|
||||||
const m = this.#grow(n);
|
|
||||||
this.#reslice(m);
|
|
||||||
}
|
|
||||||
|
|
||||||
async readFrom(r: Reader): Promise<number> {
|
|
||||||
let n = 0;
|
|
||||||
const tmp = new Uint8Array(MIN_READ);
|
|
||||||
while (true) {
|
|
||||||
const shouldGrow = this.capacity - this.length < MIN_READ;
|
|
||||||
// read into tmp buffer if there's not enough room
|
|
||||||
// otherwise read directly into the internal buffer
|
|
||||||
const buf = shouldGrow
|
|
||||||
? tmp
|
|
||||||
: new Uint8Array(this.#buf.buffer, this.length);
|
|
||||||
|
|
||||||
const nread = await r.read(buf);
|
|
||||||
if (nread === null) {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write will grow if needed
|
|
||||||
if (shouldGrow) this.writeSync(buf.subarray(0, nread));
|
|
||||||
else this.#reslice(this.length + nread);
|
|
||||||
|
|
||||||
n += nread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readFromSync(r: ReaderSync): number {
|
|
||||||
let n = 0;
|
|
||||||
const tmp = new Uint8Array(MIN_READ);
|
|
||||||
while (true) {
|
|
||||||
const shouldGrow = this.capacity - this.length < MIN_READ;
|
|
||||||
// read into tmp buffer if there's not enough room
|
|
||||||
// otherwise read directly into the internal buffer
|
|
||||||
const buf = shouldGrow
|
|
||||||
? tmp
|
|
||||||
: new Uint8Array(this.#buf.buffer, this.length);
|
|
||||||
|
|
||||||
const nread = r.readSync(buf);
|
|
||||||
if (nread === null) {
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
// write will grow if needed
|
|
||||||
if (shouldGrow) this.writeSync(buf.subarray(0, nread));
|
|
||||||
else this.#reslice(this.length + nread);
|
|
||||||
|
|
||||||
n += nread;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function readAll(r: Reader): Promise<Uint8Array> {
|
|
||||||
const buf = new Buffer();
|
|
||||||
await buf.readFrom(r);
|
|
||||||
return buf.bytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readAllSync(r: ReaderSync): Uint8Array {
|
|
||||||
const buf = new Buffer();
|
|
||||||
buf.readFromSync(r);
|
|
||||||
return buf.bytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function writeAll(w: Writer, arr: Uint8Array): Promise<void> {
|
|
||||||
let nwritten = 0;
|
|
||||||
while (nwritten < arr.length) {
|
|
||||||
nwritten += await w.write(arr.subarray(nwritten));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeAllSync(w: WriterSync, arr: Uint8Array): void {
|
|
||||||
let nwritten = 0;
|
|
||||||
while (nwritten < arr.length) {
|
|
||||||
nwritten += w.writeSync(arr.subarray(nwritten));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
export const build = {
|
|
||||||
target: "unknown",
|
|
||||||
arch: "unknown",
|
|
||||||
os: "unknown",
|
|
||||||
vendor: "unknown",
|
|
||||||
env: undefined as string | undefined,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function setBuildInfo(target: string): void {
|
|
||||||
const [arch, vendor, os, env] = target.split("-", 4);
|
|
||||||
build.target = target;
|
|
||||||
build.arch = arch;
|
|
||||||
build.vendor = vendor;
|
|
||||||
build.os = os;
|
|
||||||
build.env = env;
|
|
||||||
Object.freeze(build);
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
interface Code {
|
|
||||||
open: string;
|
|
||||||
close: string;
|
|
||||||
regexp: RegExp;
|
|
||||||
}
|
|
||||||
|
|
||||||
function code(open: number, close: number): Code {
|
|
||||||
return {
|
|
||||||
open: `\x1b[${open}m`,
|
|
||||||
close: `\x1b[${close}m`,
|
|
||||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function run(str: string, code: Code): string {
|
|
||||||
return !globalThis || !globalThis.Deno || globalThis.Deno.noColor
|
|
||||||
? str
|
|
||||||
: `${code.open}${str.replace(code.regexp, code.open)}${code.close}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bold(str: string): string {
|
|
||||||
return run(str, code(1, 22));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function italic(str: string): string {
|
|
||||||
return run(str, code(3, 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function yellow(str: string): string {
|
|
||||||
return run(str, code(33, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cyan(str: string): string {
|
|
||||||
return run(str, code(36, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function red(str: string): string {
|
|
||||||
return run(str, code(31, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function green(str: string): string {
|
|
||||||
return run(str, code(32, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bgRed(str: string): string {
|
|
||||||
return run(str, code(41, 49));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function white(str: string): string {
|
|
||||||
return run(str, code(37, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function gray(str: string): string {
|
|
||||||
return run(str, code(90, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function magenta(str: string): string {
|
|
||||||
return run(str, code(35, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dim(str: string): string {
|
|
||||||
return run(str, code(2, 22));
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/chalk/ansi-regex/blob/2b56fb0c7a07108e5b54241e8faec160d393aedb/index.js
|
|
||||||
const ANSI_PATTERN = new RegExp(
|
|
||||||
[
|
|
||||||
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
|
||||||
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))",
|
|
||||||
].join("|"),
|
|
||||||
"g",
|
|
||||||
);
|
|
||||||
|
|
||||||
export function stripColor(string: string): string {
|
|
||||||
return string.replace(ANSI_PATTERN, "");
|
|
||||||
}
|
|
1838
cli/js/compiler.ts
1838
cli/js/compiler.ts
File diff suppressed because it is too large
Load diff
|
@ -1,86 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This file contains the runtime APIs which will dispatch work to the internal
|
|
||||||
// compiler within Deno.
|
|
||||||
|
|
||||||
import type { DiagnosticItem } from "./diagnostics.ts";
|
|
||||||
import * as util from "./util.ts";
|
|
||||||
import * as runtimeCompilerOps from "./ops/runtime_compiler.ts";
|
|
||||||
import type { TranspileOnlyResult } from "./ops/runtime_compiler.ts";
|
|
||||||
import type { CompilerOptions } from "./compiler_options.ts";
|
|
||||||
|
|
||||||
function checkRelative(specifier: string): string {
|
|
||||||
return specifier.match(/^([\.\/\\]|https?:\/{2}|file:\/{2})/)
|
|
||||||
? specifier
|
|
||||||
: `./${specifier}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bartlomieju): change return type to interface?
|
|
||||||
export function transpileOnly(
|
|
||||||
sources: Record<string, string>,
|
|
||||||
options: CompilerOptions = {},
|
|
||||||
): Promise<Record<string, TranspileOnlyResult>> {
|
|
||||||
util.log("Deno.transpileOnly", { sources: Object.keys(sources), options });
|
|
||||||
const payload = {
|
|
||||||
sources,
|
|
||||||
options: JSON.stringify(options),
|
|
||||||
};
|
|
||||||
return runtimeCompilerOps.transpile(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bartlomieju): change return type to interface?
|
|
||||||
export async function compile(
|
|
||||||
rootName: string,
|
|
||||||
sources?: Record<string, string>,
|
|
||||||
options: CompilerOptions = {},
|
|
||||||
): Promise<[DiagnosticItem[] | undefined, Record<string, string>]> {
|
|
||||||
const payload = {
|
|
||||||
rootName: sources ? rootName : checkRelative(rootName),
|
|
||||||
sources,
|
|
||||||
options: JSON.stringify(options),
|
|
||||||
bundle: false,
|
|
||||||
};
|
|
||||||
util.log("Deno.compile", {
|
|
||||||
rootName: payload.rootName,
|
|
||||||
sources: !!sources,
|
|
||||||
options,
|
|
||||||
});
|
|
||||||
const result = await runtimeCompilerOps.compile(payload);
|
|
||||||
util.assert(result.emitMap);
|
|
||||||
const maybeDiagnostics = result.diagnostics.length === 0
|
|
||||||
? undefined
|
|
||||||
: result.diagnostics;
|
|
||||||
|
|
||||||
const emitMap: Record<string, string> = {};
|
|
||||||
|
|
||||||
for (const [key, emittedSource] of Object.entries(result.emitMap)) {
|
|
||||||
emitMap[key] = emittedSource.contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [maybeDiagnostics, emitMap];
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bartlomieju): change return type to interface?
|
|
||||||
export async function bundle(
|
|
||||||
rootName: string,
|
|
||||||
sources?: Record<string, string>,
|
|
||||||
options: CompilerOptions = {},
|
|
||||||
): Promise<[DiagnosticItem[] | undefined, string]> {
|
|
||||||
const payload = {
|
|
||||||
rootName: sources ? rootName : checkRelative(rootName),
|
|
||||||
sources,
|
|
||||||
options: JSON.stringify(options),
|
|
||||||
bundle: true,
|
|
||||||
};
|
|
||||||
util.log("Deno.bundle", {
|
|
||||||
rootName: payload.rootName,
|
|
||||||
sources: !!sources,
|
|
||||||
options,
|
|
||||||
});
|
|
||||||
const result = await runtimeCompilerOps.compile(payload);
|
|
||||||
util.assert(result.output);
|
|
||||||
const maybeDiagnostics = result.diagnostics.length === 0
|
|
||||||
? undefined
|
|
||||||
: result.diagnostics;
|
|
||||||
return [maybeDiagnostics, result.output];
|
|
||||||
}
|
|
|
@ -1,133 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
export interface CompilerOptions {
|
|
||||||
allowJs?: boolean;
|
|
||||||
|
|
||||||
allowSyntheticDefaultImports?: boolean;
|
|
||||||
|
|
||||||
allowUmdGlobalAccess?: boolean;
|
|
||||||
|
|
||||||
allowUnreachableCode?: boolean;
|
|
||||||
|
|
||||||
allowUnusedLabels?: boolean;
|
|
||||||
|
|
||||||
alwaysStrict?: boolean;
|
|
||||||
|
|
||||||
baseUrl?: string;
|
|
||||||
|
|
||||||
checkJs?: boolean;
|
|
||||||
|
|
||||||
declaration?: boolean;
|
|
||||||
|
|
||||||
declarationDir?: string;
|
|
||||||
|
|
||||||
declarationMap?: boolean;
|
|
||||||
|
|
||||||
downlevelIteration?: boolean;
|
|
||||||
|
|
||||||
emitBOM?: boolean;
|
|
||||||
|
|
||||||
emitDeclarationOnly?: boolean;
|
|
||||||
|
|
||||||
emitDecoratorMetadata?: boolean;
|
|
||||||
|
|
||||||
esModuleInterop?: boolean;
|
|
||||||
|
|
||||||
experimentalDecorators?: boolean;
|
|
||||||
|
|
||||||
inlineSourceMap?: boolean;
|
|
||||||
|
|
||||||
inlineSources?: boolean;
|
|
||||||
|
|
||||||
isolatedModules?: boolean;
|
|
||||||
|
|
||||||
jsx?: "react" | "preserve" | "react-native";
|
|
||||||
|
|
||||||
jsxFactory?: string;
|
|
||||||
|
|
||||||
keyofStringsOnly?: string;
|
|
||||||
|
|
||||||
useDefineForClassFields?: boolean;
|
|
||||||
|
|
||||||
lib?: string[];
|
|
||||||
|
|
||||||
locale?: string;
|
|
||||||
|
|
||||||
mapRoot?: string;
|
|
||||||
|
|
||||||
module?:
|
|
||||||
| "none"
|
|
||||||
| "commonjs"
|
|
||||||
| "amd"
|
|
||||||
| "system"
|
|
||||||
| "umd"
|
|
||||||
| "es6"
|
|
||||||
| "es2015"
|
|
||||||
| "esnext";
|
|
||||||
|
|
||||||
noEmitHelpers?: boolean;
|
|
||||||
|
|
||||||
noFallthroughCasesInSwitch?: boolean;
|
|
||||||
|
|
||||||
noImplicitAny?: boolean;
|
|
||||||
|
|
||||||
noImplicitReturns?: boolean;
|
|
||||||
|
|
||||||
noImplicitThis?: boolean;
|
|
||||||
|
|
||||||
noImplicitUseStrict?: boolean;
|
|
||||||
|
|
||||||
noResolve?: boolean;
|
|
||||||
|
|
||||||
noStrictGenericChecks?: boolean;
|
|
||||||
|
|
||||||
noUnusedLocals?: boolean;
|
|
||||||
|
|
||||||
noUnusedParameters?: boolean;
|
|
||||||
|
|
||||||
outDir?: string;
|
|
||||||
|
|
||||||
paths?: Record<string, string[]>;
|
|
||||||
|
|
||||||
preserveConstEnums?: boolean;
|
|
||||||
|
|
||||||
removeComments?: boolean;
|
|
||||||
|
|
||||||
resolveJsonModule?: boolean;
|
|
||||||
|
|
||||||
rootDir?: string;
|
|
||||||
|
|
||||||
rootDirs?: string[];
|
|
||||||
|
|
||||||
sourceMap?: boolean;
|
|
||||||
|
|
||||||
sourceRoot?: string;
|
|
||||||
|
|
||||||
strict?: boolean;
|
|
||||||
|
|
||||||
strictBindCallApply?: boolean;
|
|
||||||
|
|
||||||
strictFunctionTypes?: boolean;
|
|
||||||
|
|
||||||
strictPropertyInitialization?: boolean;
|
|
||||||
|
|
||||||
strictNullChecks?: boolean;
|
|
||||||
|
|
||||||
suppressExcessPropertyErrors?: boolean;
|
|
||||||
|
|
||||||
suppressImplicitAnyIndexErrors?: boolean;
|
|
||||||
|
|
||||||
target?:
|
|
||||||
| "es3"
|
|
||||||
| "es5"
|
|
||||||
| "es6"
|
|
||||||
| "es2015"
|
|
||||||
| "es2016"
|
|
||||||
| "es2017"
|
|
||||||
| "es2018"
|
|
||||||
| "es2019"
|
|
||||||
| "es2020"
|
|
||||||
| "esnext";
|
|
||||||
|
|
||||||
types?: string[];
|
|
||||||
}
|
|
|
@ -1,5 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This allows us to access core in API even if we
|
|
||||||
// dispose window.Deno
|
|
||||||
export const core = globalThis.Deno.core as DenoCore;
|
|
|
@ -1,91 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This module exports stable Deno APIs.
|
|
||||||
|
|
||||||
export {
|
|
||||||
Buffer,
|
|
||||||
readAll,
|
|
||||||
readAllSync,
|
|
||||||
writeAll,
|
|
||||||
writeAllSync,
|
|
||||||
} from "./buffer.ts";
|
|
||||||
export { build } from "./build.ts";
|
|
||||||
export { chmodSync, chmod } from "./ops/fs/chmod.ts";
|
|
||||||
export { chownSync, chown } from "./ops/fs/chown.ts";
|
|
||||||
export { customInspect, inspect } from "./web/console.ts";
|
|
||||||
export { copyFileSync, copyFile } from "./ops/fs/copy_file.ts";
|
|
||||||
export { chdir, cwd } from "./ops/fs/dir.ts";
|
|
||||||
export { errors } from "./errors.ts";
|
|
||||||
export {
|
|
||||||
File,
|
|
||||||
open,
|
|
||||||
openSync,
|
|
||||||
create,
|
|
||||||
createSync,
|
|
||||||
stdin,
|
|
||||||
stdout,
|
|
||||||
stderr,
|
|
||||||
seek,
|
|
||||||
seekSync,
|
|
||||||
} from "./files.ts";
|
|
||||||
export type { OpenOptions } from "./files.ts";
|
|
||||||
export { read, readSync, write, writeSync } from "./ops/io.ts";
|
|
||||||
export { watchFs } from "./ops/fs_events.ts";
|
|
||||||
export type { FsEvent } from "./ops/fs_events.ts";
|
|
||||||
export { internalSymbol as internal } from "./internals.ts";
|
|
||||||
export { copy, iter, iterSync } from "./io.ts";
|
|
||||||
export { SeekMode } from "./io.ts";
|
|
||||||
export type {
|
|
||||||
Reader,
|
|
||||||
ReaderSync,
|
|
||||||
Writer,
|
|
||||||
WriterSync,
|
|
||||||
Closer,
|
|
||||||
Seeker,
|
|
||||||
} from "./io.ts";
|
|
||||||
export {
|
|
||||||
makeTempDirSync,
|
|
||||||
makeTempDir,
|
|
||||||
makeTempFileSync,
|
|
||||||
makeTempFile,
|
|
||||||
} from "./ops/fs/make_temp.ts";
|
|
||||||
export type { MakeTempOptions } from "./ops/fs/make_temp.ts";
|
|
||||||
export { metrics } from "./ops/runtime.ts";
|
|
||||||
export type { Metrics } from "./ops/runtime.ts";
|
|
||||||
export { mkdirSync, mkdir } from "./ops/fs/mkdir.ts";
|
|
||||||
export type { MkdirOptions } from "./ops/fs/mkdir.ts";
|
|
||||||
export { connect, listen } from "./net.ts";
|
|
||||||
export type { Listener, Conn } from "./net.ts";
|
|
||||||
export { env, exit, execPath } from "./ops/os.ts";
|
|
||||||
export { Process, run } from "./process.ts";
|
|
||||||
export type { RunOptions, ProcessStatus } from "./process.ts";
|
|
||||||
export { readDirSync, readDir } from "./ops/fs/read_dir.ts";
|
|
||||||
export type { DirEntry } from "./ops/fs/read_dir.ts";
|
|
||||||
export { readFileSync, readFile } from "./read_file.ts";
|
|
||||||
export { readTextFileSync, readTextFile } from "./read_text_file.ts";
|
|
||||||
export { readLinkSync, readLink } from "./ops/fs/read_link.ts";
|
|
||||||
export { realPathSync, realPath } from "./ops/fs/real_path.ts";
|
|
||||||
export { removeSync, remove } from "./ops/fs/remove.ts";
|
|
||||||
export type { RemoveOptions } from "./ops/fs/remove.ts";
|
|
||||||
export { renameSync, rename } from "./ops/fs/rename.ts";
|
|
||||||
export { resources, close } from "./ops/resources.ts";
|
|
||||||
export { statSync, lstatSync, stat, lstat } from "./ops/fs/stat.ts";
|
|
||||||
export type { FileInfo } from "./ops/fs/stat.ts";
|
|
||||||
export { connectTls, listenTls } from "./tls.ts";
|
|
||||||
export { truncateSync, truncate } from "./ops/fs/truncate.ts";
|
|
||||||
export { isatty } from "./ops/tty.ts";
|
|
||||||
export { version } from "./version.ts";
|
|
||||||
export { writeFileSync, writeFile } from "./write_file.ts";
|
|
||||||
export type { WriteFileOptions } from "./write_file.ts";
|
|
||||||
export { writeTextFileSync, writeTextFile } from "./write_text_file.ts";
|
|
||||||
export const args: string[] = [];
|
|
||||||
export { test } from "./testing.ts";
|
|
||||||
export type { TestDefinition } from "./testing.ts";
|
|
||||||
|
|
||||||
// These are internal Deno APIs. We are marking them as internal so they do not
|
|
||||||
// appear in the runtime type library.
|
|
||||||
export { core } from "./core.ts";
|
|
||||||
|
|
||||||
export let pid: number;
|
|
||||||
|
|
||||||
export let noColor: boolean;
|
|
|
@ -1,30 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This module exports unstable Deno APIs.
|
|
||||||
|
|
||||||
export { umask } from "./ops/fs/umask.ts";
|
|
||||||
export { linkSync, link } from "./ops/fs/link.ts";
|
|
||||||
export { fstatSync, fstat } from "./ops/fs/stat.ts";
|
|
||||||
export { fdatasyncSync, fdatasync, fsyncSync, fsync } from "./ops/fs/sync.ts";
|
|
||||||
export { symlinkSync, symlink } from "./ops/fs/symlink.ts";
|
|
||||||
export { loadavg, osRelease, hostname } from "./ops/os.ts";
|
|
||||||
export { openPlugin } from "./ops/plugins.ts";
|
|
||||||
export { transpileOnly, compile, bundle } from "./compiler_api.ts";
|
|
||||||
export { applySourceMap, formatDiagnostics } from "./ops/errors.ts";
|
|
||||||
export { signal, signals, Signal, SignalStream } from "./signals.ts";
|
|
||||||
export { setRaw, consoleSize } from "./ops/tty.ts";
|
|
||||||
export { utimeSync, utime } from "./ops/fs/utime.ts";
|
|
||||||
export { ftruncateSync, ftruncate } from "./ops/fs/truncate.ts";
|
|
||||||
export { shutdown, ShutdownMode } from "./net.ts";
|
|
||||||
export { listen, listenDatagram, connect } from "./net_unstable.ts";
|
|
||||||
export { startTls } from "./tls.ts";
|
|
||||||
export { kill } from "./ops/process.ts";
|
|
||||||
export { permissions, Permissions } from "./permissions.ts";
|
|
||||||
export { PermissionStatus } from "./permissions.ts";
|
|
||||||
export type { PermissionName, PermissionState } from "./permissions.ts";
|
|
||||||
export { DiagnosticCategory } from "./diagnostics.ts";
|
|
||||||
export type {
|
|
||||||
Diagnostic,
|
|
||||||
DiagnosticItem,
|
|
||||||
DiagnosticMessageChain,
|
|
||||||
} from "./diagnostics.ts";
|
|
|
@ -1,51 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Diagnostic provides an abstraction for advice/errors received from a
|
|
||||||
// compiler, which is strongly influenced by the format of TypeScript
|
|
||||||
// diagnostics.
|
|
||||||
|
|
||||||
export enum DiagnosticCategory {
|
|
||||||
Log = 0,
|
|
||||||
Debug = 1,
|
|
||||||
Info = 2,
|
|
||||||
Error = 3,
|
|
||||||
Warning = 4,
|
|
||||||
Suggestion = 5,
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DiagnosticMessageChain {
|
|
||||||
message: string;
|
|
||||||
category: DiagnosticCategory;
|
|
||||||
code: number;
|
|
||||||
next?: DiagnosticMessageChain[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DiagnosticItem {
|
|
||||||
message: string;
|
|
||||||
|
|
||||||
messageChain?: DiagnosticMessageChain;
|
|
||||||
|
|
||||||
relatedInformation?: DiagnosticItem[];
|
|
||||||
|
|
||||||
sourceLine?: string;
|
|
||||||
|
|
||||||
lineNumber?: number;
|
|
||||||
|
|
||||||
scriptResourceName?: string;
|
|
||||||
|
|
||||||
startPosition?: number;
|
|
||||||
|
|
||||||
endPosition?: number;
|
|
||||||
|
|
||||||
category: DiagnosticCategory;
|
|
||||||
|
|
||||||
code: number;
|
|
||||||
|
|
||||||
startColumn?: number;
|
|
||||||
|
|
||||||
endColumn?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Diagnostic {
|
|
||||||
items: DiagnosticItem[];
|
|
||||||
}
|
|
|
@ -1,252 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// These utilities are used by compiler.ts to format TypeScript diagnostics
|
|
||||||
// into Deno Diagnostics.
|
|
||||||
|
|
||||||
import {
|
|
||||||
Diagnostic,
|
|
||||||
DiagnosticCategory,
|
|
||||||
DiagnosticMessageChain,
|
|
||||||
DiagnosticItem,
|
|
||||||
} from "./diagnostics.ts";
|
|
||||||
|
|
||||||
const unstableDenoGlobalProperties = [
|
|
||||||
"umask",
|
|
||||||
"linkSync",
|
|
||||||
"link",
|
|
||||||
"symlinkSync",
|
|
||||||
"symlink",
|
|
||||||
"loadavg",
|
|
||||||
"osRelease",
|
|
||||||
"openPlugin",
|
|
||||||
"DiagnosticCategory",
|
|
||||||
"DiagnosticMessageChain",
|
|
||||||
"DiagnosticItem",
|
|
||||||
"Diagnostic",
|
|
||||||
"formatDiagnostics",
|
|
||||||
"CompilerOptions",
|
|
||||||
"TranspileOnlyResult",
|
|
||||||
"transpileOnly",
|
|
||||||
"compile",
|
|
||||||
"bundle",
|
|
||||||
"Location",
|
|
||||||
"applySourceMap",
|
|
||||||
"LinuxSignal",
|
|
||||||
"MacOSSignal",
|
|
||||||
"Signal",
|
|
||||||
"SignalStream",
|
|
||||||
"signal",
|
|
||||||
"signals",
|
|
||||||
"setRaw",
|
|
||||||
"utimeSync",
|
|
||||||
"utime",
|
|
||||||
"ShutdownMode",
|
|
||||||
"shutdown",
|
|
||||||
"DatagramConn",
|
|
||||||
"UnixListenOptions",
|
|
||||||
"listen",
|
|
||||||
"listenDatagram",
|
|
||||||
"UnixConnectOptions",
|
|
||||||
"connect",
|
|
||||||
"StartTlsOptions",
|
|
||||||
"startTls",
|
|
||||||
"kill",
|
|
||||||
"PermissionName",
|
|
||||||
"PermissionState",
|
|
||||||
"RunPermissionDescriptor",
|
|
||||||
"ReadPermissionDescriptor",
|
|
||||||
"WritePermissionDescriptor",
|
|
||||||
"NetPermissionDescriptor",
|
|
||||||
"EnvPermissionDescriptor",
|
|
||||||
"PluginPermissionDescriptor",
|
|
||||||
"HrtimePermissionDescriptor",
|
|
||||||
"PermissionDescriptor",
|
|
||||||
"Permissions",
|
|
||||||
"PermissionStatus",
|
|
||||||
"hostname",
|
|
||||||
"ppid",
|
|
||||||
];
|
|
||||||
|
|
||||||
function transformMessageText(messageText: string, code: number): string {
|
|
||||||
switch (code) {
|
|
||||||
case 2339: {
|
|
||||||
const property = messageText
|
|
||||||
.replace(/^Property '/, "")
|
|
||||||
.replace(/' does not exist on type 'typeof Deno'\./, "");
|
|
||||||
|
|
||||||
if (
|
|
||||||
messageText.endsWith("on type 'typeof Deno'.") &&
|
|
||||||
unstableDenoGlobalProperties.includes(property)
|
|
||||||
) {
|
|
||||||
return `${messageText} 'Deno.${property}' is an unstable API. Did you forget to run with the '--unstable' flag?`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 2551: {
|
|
||||||
const suggestionMessagePattern = / Did you mean '(.+)'\?$/;
|
|
||||||
const property = messageText
|
|
||||||
.replace(/^Property '/, "")
|
|
||||||
.replace(/' does not exist on type 'typeof Deno'\./, "")
|
|
||||||
.replace(suggestionMessagePattern, "");
|
|
||||||
const suggestion = messageText.match(suggestionMessagePattern);
|
|
||||||
const replacedMessageText = messageText.replace(
|
|
||||||
suggestionMessagePattern,
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
if (suggestion && unstableDenoGlobalProperties.includes(property)) {
|
|
||||||
const suggestedProperty = suggestion[1];
|
|
||||||
return `${replacedMessageText} 'Deno.${property}' is an unstable API. Did you forget to run with the '--unstable' flag, or did you mean '${suggestedProperty}'?`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return messageText;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface SourceInformation {
|
|
||||||
sourceLine: string;
|
|
||||||
lineNumber: number;
|
|
||||||
scriptResourceName: string;
|
|
||||||
startColumn: number;
|
|
||||||
endColumn: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromDiagnosticCategory(
|
|
||||||
category: ts.DiagnosticCategory,
|
|
||||||
): DiagnosticCategory {
|
|
||||||
switch (category) {
|
|
||||||
case ts.DiagnosticCategory.Error:
|
|
||||||
return DiagnosticCategory.Error;
|
|
||||||
case ts.DiagnosticCategory.Message:
|
|
||||||
return DiagnosticCategory.Info;
|
|
||||||
case ts.DiagnosticCategory.Suggestion:
|
|
||||||
return DiagnosticCategory.Suggestion;
|
|
||||||
case ts.DiagnosticCategory.Warning:
|
|
||||||
return DiagnosticCategory.Warning;
|
|
||||||
default:
|
|
||||||
throw new Error(
|
|
||||||
`Unexpected DiagnosticCategory: "${category}"/"${
|
|
||||||
ts.DiagnosticCategory[category]
|
|
||||||
}"`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSourceInformation(
|
|
||||||
sourceFile: ts.SourceFile,
|
|
||||||
start: number,
|
|
||||||
length: number,
|
|
||||||
): SourceInformation {
|
|
||||||
const scriptResourceName = sourceFile.fileName;
|
|
||||||
const {
|
|
||||||
line: lineNumber,
|
|
||||||
character: startColumn,
|
|
||||||
} = sourceFile.getLineAndCharacterOfPosition(start);
|
|
||||||
const endPosition = sourceFile.getLineAndCharacterOfPosition(start + length);
|
|
||||||
const endColumn = lineNumber === endPosition.line
|
|
||||||
? endPosition.character
|
|
||||||
: startColumn;
|
|
||||||
const lastLineInFile = sourceFile.getLineAndCharacterOfPosition(
|
|
||||||
sourceFile.text.length,
|
|
||||||
).line;
|
|
||||||
const lineStart = sourceFile.getPositionOfLineAndCharacter(lineNumber, 0);
|
|
||||||
const lineEnd = lineNumber < lastLineInFile
|
|
||||||
? sourceFile.getPositionOfLineAndCharacter(lineNumber + 1, 0)
|
|
||||||
: sourceFile.text.length;
|
|
||||||
const sourceLine = sourceFile.text
|
|
||||||
.slice(lineStart, lineEnd)
|
|
||||||
.replace(/\s+$/g, "")
|
|
||||||
.replace("\t", " ");
|
|
||||||
return {
|
|
||||||
sourceLine,
|
|
||||||
lineNumber,
|
|
||||||
scriptResourceName,
|
|
||||||
startColumn,
|
|
||||||
endColumn,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromDiagnosticMessageChain(
|
|
||||||
messageChain: ts.DiagnosticMessageChain[] | undefined,
|
|
||||||
): DiagnosticMessageChain[] | undefined {
|
|
||||||
if (!messageChain) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
return messageChain.map(({ messageText, code, category, next }) => {
|
|
||||||
const message = transformMessageText(messageText, code);
|
|
||||||
return {
|
|
||||||
message,
|
|
||||||
code,
|
|
||||||
category: fromDiagnosticCategory(category),
|
|
||||||
next: fromDiagnosticMessageChain(next),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseDiagnostic(
|
|
||||||
item: ts.Diagnostic | ts.DiagnosticRelatedInformation,
|
|
||||||
): DiagnosticItem {
|
|
||||||
const {
|
|
||||||
messageText,
|
|
||||||
category: sourceCategory,
|
|
||||||
code,
|
|
||||||
file,
|
|
||||||
start: startPosition,
|
|
||||||
length,
|
|
||||||
} = item;
|
|
||||||
const sourceInfo = file && startPosition && length
|
|
||||||
? getSourceInformation(file, startPosition, length)
|
|
||||||
: undefined;
|
|
||||||
const endPosition = startPosition && length
|
|
||||||
? startPosition + length
|
|
||||||
: undefined;
|
|
||||||
const category = fromDiagnosticCategory(sourceCategory);
|
|
||||||
|
|
||||||
let message: string;
|
|
||||||
let messageChain: DiagnosticMessageChain | undefined;
|
|
||||||
if (typeof messageText === "string") {
|
|
||||||
message = transformMessageText(messageText, code);
|
|
||||||
} else {
|
|
||||||
message = transformMessageText(messageText.messageText, messageText.code);
|
|
||||||
messageChain = fromDiagnosticMessageChain([messageText])![0];
|
|
||||||
}
|
|
||||||
|
|
||||||
const base = {
|
|
||||||
message,
|
|
||||||
messageChain,
|
|
||||||
code,
|
|
||||||
category,
|
|
||||||
startPosition,
|
|
||||||
endPosition,
|
|
||||||
};
|
|
||||||
|
|
||||||
return sourceInfo ? { ...base, ...sourceInfo } : base;
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseRelatedInformation(
|
|
||||||
relatedInformation: readonly ts.DiagnosticRelatedInformation[],
|
|
||||||
): DiagnosticItem[] {
|
|
||||||
const result: DiagnosticItem[] = [];
|
|
||||||
for (const item of relatedInformation) {
|
|
||||||
result.push(parseDiagnostic(item));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fromTypeScriptDiagnostic(
|
|
||||||
diagnostics: readonly ts.Diagnostic[],
|
|
||||||
): Diagnostic {
|
|
||||||
const items: DiagnosticItem[] = [];
|
|
||||||
for (const sourceDiagnostic of diagnostics) {
|
|
||||||
const item: DiagnosticItem = parseDiagnostic(sourceDiagnostic);
|
|
||||||
if (sourceDiagnostic.relatedInformation) {
|
|
||||||
item.relatedInformation = parseRelatedInformation(
|
|
||||||
sourceDiagnostic.relatedInformation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
items.push(item);
|
|
||||||
}
|
|
||||||
return { items };
|
|
||||||
}
|
|
|
@ -1,265 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Some of the code here is adapted directly from V8 and licensed under a BSD
|
|
||||||
// style license available here: https://github.com/v8/v8/blob/24886f2d1c565287d33d71e4109a53bf0b54b75c/LICENSE.v8
|
|
||||||
import * as colors from "./colors.ts";
|
|
||||||
import { applySourceMap, Location } from "./ops/errors.ts";
|
|
||||||
import { assert } from "./util.ts";
|
|
||||||
import { exposeForTest } from "./internals.ts";
|
|
||||||
|
|
||||||
function patchCallSite(callSite: CallSite, location: Location): CallSite {
|
|
||||||
return {
|
|
||||||
getThis(): unknown {
|
|
||||||
return callSite.getThis();
|
|
||||||
},
|
|
||||||
getTypeName(): string | null {
|
|
||||||
return callSite.getTypeName();
|
|
||||||
},
|
|
||||||
getFunction(): Function | null {
|
|
||||||
return callSite.getFunction();
|
|
||||||
},
|
|
||||||
getFunctionName(): string | null {
|
|
||||||
return callSite.getFunctionName();
|
|
||||||
},
|
|
||||||
getMethodName(): string | null {
|
|
||||||
return callSite.getMethodName();
|
|
||||||
},
|
|
||||||
getFileName(): string | null {
|
|
||||||
return location.fileName;
|
|
||||||
},
|
|
||||||
getLineNumber(): number {
|
|
||||||
return location.lineNumber;
|
|
||||||
},
|
|
||||||
getColumnNumber(): number {
|
|
||||||
return location.columnNumber;
|
|
||||||
},
|
|
||||||
getEvalOrigin(): string | null {
|
|
||||||
return callSite.getEvalOrigin();
|
|
||||||
},
|
|
||||||
isToplevel(): boolean | null {
|
|
||||||
return callSite.isToplevel();
|
|
||||||
},
|
|
||||||
isEval(): boolean {
|
|
||||||
return callSite.isEval();
|
|
||||||
},
|
|
||||||
isNative(): boolean {
|
|
||||||
return callSite.isNative();
|
|
||||||
},
|
|
||||||
isConstructor(): boolean {
|
|
||||||
return callSite.isConstructor();
|
|
||||||
},
|
|
||||||
isAsync(): boolean {
|
|
||||||
return callSite.isAsync();
|
|
||||||
},
|
|
||||||
isPromiseAll(): boolean {
|
|
||||||
return callSite.isPromiseAll();
|
|
||||||
},
|
|
||||||
getPromiseIndex(): number | null {
|
|
||||||
return callSite.getPromiseIndex();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMethodCall(callSite: CallSite): string {
|
|
||||||
let result = "";
|
|
||||||
|
|
||||||
const typeName = callSite.getTypeName();
|
|
||||||
const methodName = callSite.getMethodName();
|
|
||||||
const functionName = callSite.getFunctionName();
|
|
||||||
|
|
||||||
if (functionName) {
|
|
||||||
if (typeName) {
|
|
||||||
const startsWithTypeName = functionName.startsWith(typeName);
|
|
||||||
if (!startsWithTypeName) {
|
|
||||||
result += `${typeName}.`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
result += functionName;
|
|
||||||
|
|
||||||
if (methodName) {
|
|
||||||
if (!functionName.endsWith(methodName)) {
|
|
||||||
result += ` [as ${methodName}]`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (typeName) {
|
|
||||||
result += `${typeName}.`;
|
|
||||||
}
|
|
||||||
if (methodName) {
|
|
||||||
result += methodName;
|
|
||||||
} else {
|
|
||||||
result += "<anonymous>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileLocation(callSite: CallSite, internal = false): string {
|
|
||||||
const cyan = internal ? colors.gray : colors.cyan;
|
|
||||||
const yellow = internal ? colors.gray : colors.yellow;
|
|
||||||
const black = internal ? colors.gray : (s: string): string => s;
|
|
||||||
if (callSite.isNative()) {
|
|
||||||
return cyan("native");
|
|
||||||
}
|
|
||||||
|
|
||||||
let result = "";
|
|
||||||
|
|
||||||
const fileName = callSite.getFileName();
|
|
||||||
if (!fileName && callSite.isEval()) {
|
|
||||||
const evalOrigin = callSite.getEvalOrigin();
|
|
||||||
assert(evalOrigin != null);
|
|
||||||
result += cyan(`${evalOrigin}, `);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fileName) {
|
|
||||||
result += cyan(fileName);
|
|
||||||
} else {
|
|
||||||
result += cyan("<anonymous>");
|
|
||||||
}
|
|
||||||
|
|
||||||
const lineNumber = callSite.getLineNumber();
|
|
||||||
if (lineNumber != null) {
|
|
||||||
result += `${black(":")}${yellow(lineNumber.toString())}`;
|
|
||||||
|
|
||||||
const columnNumber = callSite.getColumnNumber();
|
|
||||||
if (columnNumber != null) {
|
|
||||||
result += `${black(":")}${yellow(columnNumber.toString())}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function callSiteToString(callSite: CallSite, internal = false): string {
|
|
||||||
const cyan = internal ? colors.gray : colors.cyan;
|
|
||||||
const black = internal ? colors.gray : (s: string): string => s;
|
|
||||||
|
|
||||||
let result = "";
|
|
||||||
const functionName = callSite.getFunctionName();
|
|
||||||
|
|
||||||
const isTopLevel = callSite.isToplevel();
|
|
||||||
const isAsync = callSite.isAsync();
|
|
||||||
const isPromiseAll = callSite.isPromiseAll();
|
|
||||||
const isConstructor = callSite.isConstructor();
|
|
||||||
const isMethodCall = !(isTopLevel || isConstructor);
|
|
||||||
|
|
||||||
if (isAsync) {
|
|
||||||
result += colors.gray("async ");
|
|
||||||
}
|
|
||||||
if (isPromiseAll) {
|
|
||||||
result += colors.bold(
|
|
||||||
colors.italic(black(`Promise.all (index ${callSite.getPromiseIndex()})`)),
|
|
||||||
);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
if (isMethodCall) {
|
|
||||||
result += colors.bold(colors.italic(black(getMethodCall(callSite))));
|
|
||||||
} else if (isConstructor) {
|
|
||||||
result += colors.gray("new ");
|
|
||||||
if (functionName) {
|
|
||||||
result += colors.bold(colors.italic(black(functionName)));
|
|
||||||
} else {
|
|
||||||
result += cyan("<anonymous>");
|
|
||||||
}
|
|
||||||
} else if (functionName) {
|
|
||||||
result += colors.bold(colors.italic(black(functionName)));
|
|
||||||
} else {
|
|
||||||
result += getFileLocation(callSite, internal);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result += ` ${black("(")}${getFileLocation(callSite, internal)}${black(")")}`;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CallSiteEval {
|
|
||||||
this: unknown;
|
|
||||||
typeName: string | null;
|
|
||||||
function: Function | null;
|
|
||||||
functionName: string | null;
|
|
||||||
methodName: string | null;
|
|
||||||
fileName: string | null;
|
|
||||||
lineNumber: number | null;
|
|
||||||
columnNumber: number | null;
|
|
||||||
evalOrigin: string | null;
|
|
||||||
isToplevel: boolean | null;
|
|
||||||
isEval: boolean;
|
|
||||||
isNative: boolean;
|
|
||||||
isConstructor: boolean;
|
|
||||||
isAsync: boolean;
|
|
||||||
isPromiseAll: boolean;
|
|
||||||
promiseIndex: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function evaluateCallSite(callSite: CallSite): CallSiteEval {
|
|
||||||
return {
|
|
||||||
this: callSite.getThis(),
|
|
||||||
typeName: callSite.getTypeName(),
|
|
||||||
function: callSite.getFunction(),
|
|
||||||
functionName: callSite.getFunctionName(),
|
|
||||||
methodName: callSite.getMethodName(),
|
|
||||||
fileName: callSite.getFileName(),
|
|
||||||
lineNumber: callSite.getLineNumber(),
|
|
||||||
columnNumber: callSite.getColumnNumber(),
|
|
||||||
evalOrigin: callSite.getEvalOrigin(),
|
|
||||||
isToplevel: callSite.isToplevel(),
|
|
||||||
isEval: callSite.isEval(),
|
|
||||||
isNative: callSite.isNative(),
|
|
||||||
isConstructor: callSite.isConstructor(),
|
|
||||||
isAsync: callSite.isAsync(),
|
|
||||||
isPromiseAll: callSite.isPromiseAll(),
|
|
||||||
promiseIndex: callSite.getPromiseIndex(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function prepareStackTrace(
|
|
||||||
error: Error & {
|
|
||||||
__callSiteEvals: CallSiteEval[];
|
|
||||||
__formattedFrames: string[];
|
|
||||||
},
|
|
||||||
callSites: CallSite[],
|
|
||||||
): string {
|
|
||||||
const mappedCallSites = callSites.map(
|
|
||||||
(callSite): CallSite => {
|
|
||||||
const fileName = callSite.getFileName();
|
|
||||||
const lineNumber = callSite.getLineNumber();
|
|
||||||
const columnNumber = callSite.getColumnNumber();
|
|
||||||
if (fileName && lineNumber != null && columnNumber != null) {
|
|
||||||
return patchCallSite(
|
|
||||||
callSite,
|
|
||||||
applySourceMap({
|
|
||||||
fileName,
|
|
||||||
lineNumber,
|
|
||||||
columnNumber,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return callSite;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Object.defineProperties(error, {
|
|
||||||
__callSiteEvals: { value: [], configurable: true },
|
|
||||||
__formattedFrames: { value: [], configurable: true },
|
|
||||||
});
|
|
||||||
for (const callSite of mappedCallSites) {
|
|
||||||
error.__callSiteEvals.push(Object.freeze(evaluateCallSite(callSite)));
|
|
||||||
const isInternal = callSite.getFileName()?.startsWith("$deno$") ?? false;
|
|
||||||
error.__formattedFrames.push(callSiteToString(callSite, isInternal));
|
|
||||||
}
|
|
||||||
Object.freeze(error.__callSiteEvals);
|
|
||||||
Object.freeze(error.__formattedFrames);
|
|
||||||
return (
|
|
||||||
`${error.name}: ${error.message}\n` +
|
|
||||||
error.__formattedFrames
|
|
||||||
.map((s: string) => ` at ${colors.stripColor(s)}`)
|
|
||||||
.join("\n")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function setPrepareStackTrace(ErrorConstructor: typeof Error): void {
|
|
||||||
ErrorConstructor.prepareStackTrace = prepareStackTrace;
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeForTest("setPrepareStackTrace", setPrepareStackTrace);
|
|
225
cli/js/errors.ts
225
cli/js/errors.ts
|
@ -1,225 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Warning! The values in this enum are duplicated in cli/op_error.rs
|
|
||||||
// Update carefully!
|
|
||||||
export enum ErrorKind {
|
|
||||||
NotFound = 1,
|
|
||||||
PermissionDenied = 2,
|
|
||||||
ConnectionRefused = 3,
|
|
||||||
ConnectionReset = 4,
|
|
||||||
ConnectionAborted = 5,
|
|
||||||
NotConnected = 6,
|
|
||||||
AddrInUse = 7,
|
|
||||||
AddrNotAvailable = 8,
|
|
||||||
BrokenPipe = 9,
|
|
||||||
AlreadyExists = 10,
|
|
||||||
InvalidData = 13,
|
|
||||||
TimedOut = 14,
|
|
||||||
Interrupted = 15,
|
|
||||||
WriteZero = 16,
|
|
||||||
UnexpectedEof = 17,
|
|
||||||
BadResource = 18,
|
|
||||||
Http = 19,
|
|
||||||
URIError = 20,
|
|
||||||
TypeError = 21,
|
|
||||||
Other = 22,
|
|
||||||
Busy = 23,
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ErrorClass {
|
|
||||||
new (msg: string): Error;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getErrorClass(kind: ErrorKind): ErrorClass {
|
|
||||||
switch (kind) {
|
|
||||||
case ErrorKind.TypeError:
|
|
||||||
return TypeError;
|
|
||||||
case ErrorKind.Other:
|
|
||||||
return Error;
|
|
||||||
case ErrorKind.URIError:
|
|
||||||
return URIError;
|
|
||||||
case ErrorKind.NotFound:
|
|
||||||
return NotFound;
|
|
||||||
case ErrorKind.PermissionDenied:
|
|
||||||
return PermissionDenied;
|
|
||||||
case ErrorKind.ConnectionRefused:
|
|
||||||
return ConnectionRefused;
|
|
||||||
case ErrorKind.ConnectionReset:
|
|
||||||
return ConnectionReset;
|
|
||||||
case ErrorKind.ConnectionAborted:
|
|
||||||
return ConnectionAborted;
|
|
||||||
case ErrorKind.NotConnected:
|
|
||||||
return NotConnected;
|
|
||||||
case ErrorKind.AddrInUse:
|
|
||||||
return AddrInUse;
|
|
||||||
case ErrorKind.AddrNotAvailable:
|
|
||||||
return AddrNotAvailable;
|
|
||||||
case ErrorKind.BrokenPipe:
|
|
||||||
return BrokenPipe;
|
|
||||||
case ErrorKind.AlreadyExists:
|
|
||||||
return AlreadyExists;
|
|
||||||
case ErrorKind.InvalidData:
|
|
||||||
return InvalidData;
|
|
||||||
case ErrorKind.TimedOut:
|
|
||||||
return TimedOut;
|
|
||||||
case ErrorKind.Interrupted:
|
|
||||||
return Interrupted;
|
|
||||||
case ErrorKind.WriteZero:
|
|
||||||
return WriteZero;
|
|
||||||
case ErrorKind.UnexpectedEof:
|
|
||||||
return UnexpectedEof;
|
|
||||||
case ErrorKind.BadResource:
|
|
||||||
return BadResource;
|
|
||||||
case ErrorKind.Http:
|
|
||||||
return Http;
|
|
||||||
case ErrorKind.Busy:
|
|
||||||
return Busy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NotFound extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "NotFound";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PermissionDenied extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "PermissionDenied";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectionRefused extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "ConnectionRefused";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectionReset extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "ConnectionReset";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ConnectionAborted extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "ConnectionAborted";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class NotConnected extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "NotConnected";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddrInUse extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "AddrInUse";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AddrNotAvailable extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "AddrNotAvailable";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BrokenPipe extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "BrokenPipe";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AlreadyExists extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "AlreadyExists";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InvalidData extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "InvalidData";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TimedOut extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "TimedOut";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Interrupted extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "Interrupted";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class WriteZero extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "WriteZero";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UnexpectedEof extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "UnexpectedEof";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class BadResource extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "BadResource";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Http extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "Http";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Busy extends Error {
|
|
||||||
constructor(msg: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "Busy";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const errors = {
|
|
||||||
NotFound,
|
|
||||||
PermissionDenied,
|
|
||||||
ConnectionRefused,
|
|
||||||
ConnectionReset,
|
|
||||||
ConnectionAborted,
|
|
||||||
NotConnected,
|
|
||||||
AddrInUse,
|
|
||||||
AddrNotAvailable,
|
|
||||||
BrokenPipe,
|
|
||||||
AlreadyExists,
|
|
||||||
InvalidData,
|
|
||||||
TimedOut,
|
|
||||||
Interrupted,
|
|
||||||
WriteZero,
|
|
||||||
UnexpectedEof,
|
|
||||||
BadResource,
|
|
||||||
Http,
|
|
||||||
Busy,
|
|
||||||
};
|
|
169
cli/js/files.ts
169
cli/js/files.ts
|
@ -1,169 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import type {
|
|
||||||
Reader,
|
|
||||||
Writer,
|
|
||||||
Seeker,
|
|
||||||
Closer,
|
|
||||||
SeekMode,
|
|
||||||
ReaderSync,
|
|
||||||
WriterSync,
|
|
||||||
SeekerSync,
|
|
||||||
} from "./io.ts";
|
|
||||||
import { close } from "./ops/resources.ts";
|
|
||||||
import { read, readSync, write, writeSync } from "./ops/io.ts";
|
|
||||||
import { seek, seekSync } from "./ops/fs/seek.ts";
|
|
||||||
export { seek, seekSync } from "./ops/fs/seek.ts";
|
|
||||||
import {
|
|
||||||
open as opOpen,
|
|
||||||
openSync as opOpenSync,
|
|
||||||
OpenOptions,
|
|
||||||
} from "./ops/fs/open.ts";
|
|
||||||
export type { OpenOptions } from "./ops/fs/open.ts";
|
|
||||||
|
|
||||||
export function openSync(
|
|
||||||
path: string | URL,
|
|
||||||
options: OpenOptions = { read: true },
|
|
||||||
): File {
|
|
||||||
checkOpenOptions(options);
|
|
||||||
const rid = opOpenSync(path, options);
|
|
||||||
return new File(rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function open(
|
|
||||||
path: string | URL,
|
|
||||||
options: OpenOptions = { read: true },
|
|
||||||
): Promise<File> {
|
|
||||||
checkOpenOptions(options);
|
|
||||||
const rid = await opOpen(path, options);
|
|
||||||
return new File(rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createSync(path: string | URL): File {
|
|
||||||
return openSync(path, {
|
|
||||||
read: true,
|
|
||||||
write: true,
|
|
||||||
truncate: true,
|
|
||||||
create: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function create(path: string | URL): Promise<File> {
|
|
||||||
return open(path, {
|
|
||||||
read: true,
|
|
||||||
write: true,
|
|
||||||
truncate: true,
|
|
||||||
create: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export class File
|
|
||||||
implements
|
|
||||||
Reader,
|
|
||||||
ReaderSync,
|
|
||||||
Writer,
|
|
||||||
WriterSync,
|
|
||||||
Seeker,
|
|
||||||
SeekerSync,
|
|
||||||
Closer {
|
|
||||||
constructor(readonly rid: number) {}
|
|
||||||
|
|
||||||
write(p: Uint8Array): Promise<number> {
|
|
||||||
return write(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSync(p: Uint8Array): number {
|
|
||||||
return writeSync(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
read(p: Uint8Array): Promise<number | null> {
|
|
||||||
return read(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
readSync(p: Uint8Array): number | null {
|
|
||||||
return readSync(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
seek(offset: number, whence: SeekMode): Promise<number> {
|
|
||||||
return seek(this.rid, offset, whence);
|
|
||||||
}
|
|
||||||
|
|
||||||
seekSync(offset: number, whence: SeekMode): number {
|
|
||||||
return seekSync(this.rid, offset, whence);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Stdin implements Reader, ReaderSync, Closer {
|
|
||||||
readonly rid = 0;
|
|
||||||
|
|
||||||
read(p: Uint8Array): Promise<number | null> {
|
|
||||||
return read(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
readSync(p: Uint8Array): number | null {
|
|
||||||
return readSync(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Stdout implements Writer, WriterSync, Closer {
|
|
||||||
readonly rid = 1;
|
|
||||||
|
|
||||||
write(p: Uint8Array): Promise<number> {
|
|
||||||
return write(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSync(p: Uint8Array): number {
|
|
||||||
return writeSync(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Stderr implements Writer, WriterSync, Closer {
|
|
||||||
readonly rid = 2;
|
|
||||||
|
|
||||||
write(p: Uint8Array): Promise<number> {
|
|
||||||
return write(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeSync(p: Uint8Array): number {
|
|
||||||
return writeSync(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const stdin = new Stdin();
|
|
||||||
export const stdout = new Stdout();
|
|
||||||
export const stderr = new Stderr();
|
|
||||||
|
|
||||||
function checkOpenOptions(options: OpenOptions): void {
|
|
||||||
if (Object.values(options).filter((val) => val === true).length === 0) {
|
|
||||||
throw new Error("OpenOptions requires at least one option to be true");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.truncate && !options.write) {
|
|
||||||
throw new Error("'truncate' option requires 'write' option");
|
|
||||||
}
|
|
||||||
|
|
||||||
const createOrCreateNewWithoutWriteOrAppend =
|
|
||||||
(options.create || options.createNew) && !(options.write || options.append);
|
|
||||||
|
|
||||||
if (createOrCreateNewWithoutWriteOrAppend) {
|
|
||||||
throw new Error(
|
|
||||||
"'create' or 'createNew' options require 'write' or 'append' option",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,263 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import "./lib.deno.shared_globals.d.ts";
|
|
||||||
|
|
||||||
import * as abortController from "./web/abort_controller.ts";
|
|
||||||
import * as abortSignal from "./web/abort_signal.ts";
|
|
||||||
import * as blob from "./web/blob.ts";
|
|
||||||
import * as consoleTypes from "./web/console.ts";
|
|
||||||
import * as csprng from "./ops/get_random_values.ts";
|
|
||||||
import type * as promiseTypes from "./web/promise.ts";
|
|
||||||
import * as customEvent from "./web/custom_event.ts";
|
|
||||||
import * as domException from "./web/dom_exception.ts";
|
|
||||||
import * as domFile from "./web/dom_file.ts";
|
|
||||||
import * as errorEvent from "./web/error_event.ts";
|
|
||||||
import * as event from "./web/event.ts";
|
|
||||||
import * as eventTarget from "./web/event_target.ts";
|
|
||||||
import * as formData from "./web/form_data.ts";
|
|
||||||
import * as fetchTypes from "./web/fetch.ts";
|
|
||||||
import * as headers from "./web/headers.ts";
|
|
||||||
import * as textEncoding from "./web/text_encoding.ts";
|
|
||||||
import * as timers from "./web/timers.ts";
|
|
||||||
import * as url from "./web/url.ts";
|
|
||||||
import * as urlSearchParams from "./web/url_search_params.ts";
|
|
||||||
import * as workers from "./web/workers.ts";
|
|
||||||
import * as performance from "./web/performance.ts";
|
|
||||||
import * as request from "./web/request.ts";
|
|
||||||
import * as readableStream from "./web/streams/readable_stream.ts";
|
|
||||||
import * as transformStream from "./web/streams/transform_stream.ts";
|
|
||||||
import * as queuingStrategy from "./web/streams/queuing_strategy.ts";
|
|
||||||
import * as writableStream from "./web/streams/writable_stream.ts";
|
|
||||||
|
|
||||||
// These imports are not exposed and therefore are fine to just import the
|
|
||||||
// symbols required.
|
|
||||||
import { core } from "./core.ts";
|
|
||||||
|
|
||||||
// This global augmentation is just enough types to be able to build Deno,
|
|
||||||
// the runtime types are fully defined in `lib.deno.*.d.ts`.
|
|
||||||
declare global {
|
|
||||||
interface CallSite {
|
|
||||||
getThis(): unknown;
|
|
||||||
getTypeName(): string | null;
|
|
||||||
getFunction(): Function | null;
|
|
||||||
getFunctionName(): string | null;
|
|
||||||
getMethodName(): string | null;
|
|
||||||
getFileName(): string | null;
|
|
||||||
getLineNumber(): number | null;
|
|
||||||
getColumnNumber(): number | null;
|
|
||||||
getEvalOrigin(): string | null;
|
|
||||||
isToplevel(): boolean | null;
|
|
||||||
isEval(): boolean;
|
|
||||||
isNative(): boolean;
|
|
||||||
isConstructor(): boolean;
|
|
||||||
isAsync(): boolean;
|
|
||||||
isPromiseAll(): boolean;
|
|
||||||
getPromiseIndex(): number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ErrorConstructor {
|
|
||||||
prepareStackTrace(error: Error, structuredStackTrace: CallSite[]): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Object {
|
|
||||||
[consoleTypes.customInspect]?(): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EvalErrorInfo {
|
|
||||||
isNativeError: boolean;
|
|
||||||
isCompileError: boolean;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
thrown: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ImportMeta {
|
|
||||||
url: string;
|
|
||||||
main: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface DenoCore {
|
|
||||||
print(s: string, isErr?: boolean): void;
|
|
||||||
dispatch(opId: number, ...zeroCopy: ArrayBufferView[]): Uint8Array | null;
|
|
||||||
dispatchByName(
|
|
||||||
opName: string,
|
|
||||||
...zeroCopy: ArrayBufferView[]
|
|
||||||
): Uint8Array | null;
|
|
||||||
setAsyncHandler(opId: number, cb: (msg: Uint8Array) => void): void;
|
|
||||||
sharedQueue: {
|
|
||||||
head(): number;
|
|
||||||
numRecords(): number;
|
|
||||||
size(): number;
|
|
||||||
push(buf: Uint8Array): boolean;
|
|
||||||
reset(): void;
|
|
||||||
shift(): Uint8Array | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
ops(): Record<string, number>;
|
|
||||||
|
|
||||||
recv(cb: (opId: number, msg: Uint8Array) => void): void;
|
|
||||||
|
|
||||||
send(opId: number, ...data: ArrayBufferView[]): null | Uint8Array;
|
|
||||||
|
|
||||||
setMacrotaskCallback(cb: () => boolean): void;
|
|
||||||
|
|
||||||
shared: SharedArrayBuffer;
|
|
||||||
|
|
||||||
evalContext(
|
|
||||||
code: string,
|
|
||||||
scriptName?: string,
|
|
||||||
): [unknown, EvalErrorInfo | null];
|
|
||||||
|
|
||||||
formatError: (e: Error) => string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get promise details as two elements array.
|
|
||||||
*
|
|
||||||
* First element is the `PromiseState`.
|
|
||||||
* If promise isn't pending, second element would be the result of the promise.
|
|
||||||
* Otherwise, second element would be undefined.
|
|
||||||
*
|
|
||||||
* Throws `TypeError` if argument isn't a promise
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
getPromiseDetails<T>(promise: Promise<T>): promiseTypes.PromiseDetails<T>;
|
|
||||||
|
|
||||||
decode(bytes: Uint8Array): string;
|
|
||||||
encode(text: string): Uint8Array;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only `var` variables show up in the `globalThis` type when doing a global
|
|
||||||
// scope augmentation.
|
|
||||||
/* eslint-disable no-var */
|
|
||||||
|
|
||||||
// Assigned to `window` global - main runtime
|
|
||||||
var Deno: {
|
|
||||||
core: DenoCore;
|
|
||||||
noColor: boolean;
|
|
||||||
};
|
|
||||||
var onload: ((e: Event) => void) | undefined;
|
|
||||||
var onunload: ((e: Event) => void) | undefined;
|
|
||||||
|
|
||||||
// These methods are used to prepare different runtime
|
|
||||||
// environments. After bootrapping, this namespace
|
|
||||||
// should be removed from global scope.
|
|
||||||
var bootstrap: {
|
|
||||||
mainRuntime: (() => void) | undefined;
|
|
||||||
// Assigned to `self` global - worker runtime and compiler
|
|
||||||
workerRuntime: ((name: string) => Promise<void> | void) | undefined;
|
|
||||||
// Assigned to `self` global - compiler
|
|
||||||
tsCompilerRuntime: (() => void) | undefined;
|
|
||||||
};
|
|
||||||
|
|
||||||
var onerror:
|
|
||||||
| ((
|
|
||||||
msg: string,
|
|
||||||
source: string,
|
|
||||||
lineno: number,
|
|
||||||
colno: number,
|
|
||||||
e: Event,
|
|
||||||
) => boolean | void)
|
|
||||||
| undefined;
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
var onmessage: ((e: { data: any }) => Promise<void> | void) | undefined;
|
|
||||||
// Called in compiler
|
|
||||||
var close: () => void;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
var postMessage: (msg: any) => void;
|
|
||||||
/* eslint-enable */
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writable(value: unknown): PropertyDescriptor {
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
writable: true,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function nonEnumerable(value: unknown): PropertyDescriptor {
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
writable: true,
|
|
||||||
configurable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readOnly(value: unknown): PropertyDescriptor {
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
enumerable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function getterOnly(getter: () => any): PropertyDescriptor {
|
|
||||||
return {
|
|
||||||
get: getter,
|
|
||||||
enumerable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope
|
|
||||||
export const windowOrWorkerGlobalScopeMethods = {
|
|
||||||
atob: writable(textEncoding.atob),
|
|
||||||
btoa: writable(textEncoding.btoa),
|
|
||||||
clearInterval: writable(timers.clearInterval),
|
|
||||||
clearTimeout: writable(timers.clearTimeout),
|
|
||||||
fetch: writable(fetchTypes.fetch),
|
|
||||||
// queueMicrotask is bound in Rust
|
|
||||||
setInterval: writable(timers.setInterval),
|
|
||||||
setTimeout: writable(timers.setTimeout),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Other properties shared between WindowScope and WorkerGlobalScope
|
|
||||||
export const windowOrWorkerGlobalScopeProperties = {
|
|
||||||
console: writable(new consoleTypes.Console(core.print)),
|
|
||||||
AbortController: nonEnumerable(abortController.AbortControllerImpl),
|
|
||||||
AbortSignal: nonEnumerable(abortSignal.AbortSignalImpl),
|
|
||||||
Blob: nonEnumerable(blob.DenoBlob),
|
|
||||||
ByteLengthQueuingStrategy: nonEnumerable(
|
|
||||||
queuingStrategy.ByteLengthQueuingStrategyImpl,
|
|
||||||
),
|
|
||||||
CountQueuingStrategy: nonEnumerable(queuingStrategy.CountQueuingStrategyImpl),
|
|
||||||
crypto: readOnly(csprng),
|
|
||||||
File: nonEnumerable(domFile.DomFileImpl),
|
|
||||||
CustomEvent: nonEnumerable(customEvent.CustomEventImpl),
|
|
||||||
DOMException: nonEnumerable(domException.DOMExceptionImpl),
|
|
||||||
ErrorEvent: nonEnumerable(errorEvent.ErrorEventImpl),
|
|
||||||
Event: nonEnumerable(event.EventImpl),
|
|
||||||
EventTarget: nonEnumerable(eventTarget.EventTargetImpl),
|
|
||||||
Headers: nonEnumerable(headers.HeadersImpl),
|
|
||||||
FormData: nonEnumerable(formData.FormDataImpl),
|
|
||||||
ReadableStream: nonEnumerable(readableStream.ReadableStreamImpl),
|
|
||||||
Request: nonEnumerable(request.Request),
|
|
||||||
Response: nonEnumerable(fetchTypes.Response),
|
|
||||||
performance: writable(new performance.PerformanceImpl()),
|
|
||||||
Performance: nonEnumerable(performance.PerformanceImpl),
|
|
||||||
PerformanceEntry: nonEnumerable(performance.PerformanceEntryImpl),
|
|
||||||
PerformanceMark: nonEnumerable(performance.PerformanceMarkImpl),
|
|
||||||
PerformanceMeasure: nonEnumerable(performance.PerformanceMeasureImpl),
|
|
||||||
TextDecoder: nonEnumerable(textEncoding.TextDecoder),
|
|
||||||
TextEncoder: nonEnumerable(textEncoding.TextEncoder),
|
|
||||||
TransformStream: nonEnumerable(transformStream.TransformStreamImpl),
|
|
||||||
URL: nonEnumerable(url.URLImpl),
|
|
||||||
URLSearchParams: nonEnumerable(urlSearchParams.URLSearchParamsImpl),
|
|
||||||
Worker: nonEnumerable(workers.WorkerImpl),
|
|
||||||
WritableStream: nonEnumerable(writableStream.WritableStreamImpl),
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function setEventTargetData(value: any): void {
|
|
||||||
eventTarget.eventTargetData.set(value, eventTarget.getDefaultTargetData());
|
|
||||||
}
|
|
||||||
|
|
||||||
export const eventTargetProperties = {
|
|
||||||
addEventListener: readOnly(
|
|
||||||
eventTarget.EventTargetImpl.prototype.addEventListener,
|
|
||||||
),
|
|
||||||
dispatchEvent: readOnly(eventTarget.EventTargetImpl.prototype.dispatchEvent),
|
|
||||||
removeEventListener: readOnly(
|
|
||||||
eventTarget.EventTargetImpl.prototype.removeEventListener,
|
|
||||||
),
|
|
||||||
};
|
|
|
@ -1,5 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
export const unstableMethods = {};
|
|
||||||
|
|
||||||
export const unstableProperties = {};
|
|
|
@ -1,17 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
export const internalSymbol = Symbol("Deno.internal");
|
|
||||||
|
|
||||||
// The object where all the internal fields for testing will be living.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export const internalObject: Record<string, any> = {};
|
|
||||||
|
|
||||||
// Register a field to internalObject for test access,
|
|
||||||
// through Deno[Deno.internal][name].
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export function exposeForTest(name: string, value: any): void {
|
|
||||||
Object.defineProperty(internalObject, name, {
|
|
||||||
value,
|
|
||||||
enumerable: false,
|
|
||||||
});
|
|
||||||
}
|
|
113
cli/js/io.ts
113
cli/js/io.ts
|
@ -1,113 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Interfaces 100% copied from Go.
|
|
||||||
// Documentation liberally lifted from them too.
|
|
||||||
// Thank you! We love Go! <3
|
|
||||||
|
|
||||||
const DEFAULT_BUFFER_SIZE = 32 * 1024;
|
|
||||||
|
|
||||||
// Seek whence values.
|
|
||||||
// https://golang.org/pkg/io/#pkg-constants
|
|
||||||
export enum SeekMode {
|
|
||||||
Start = 0,
|
|
||||||
Current = 1,
|
|
||||||
End = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reader is the interface that wraps the basic read() method.
|
|
||||||
// https://golang.org/pkg/io/#Reader
|
|
||||||
export interface Reader {
|
|
||||||
read(p: Uint8Array): Promise<number | null>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ReaderSync {
|
|
||||||
readSync(p: Uint8Array): number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writer is the interface that wraps the basic write() method.
|
|
||||||
// https://golang.org/pkg/io/#Writer
|
|
||||||
export interface Writer {
|
|
||||||
write(p: Uint8Array): Promise<number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WriterSync {
|
|
||||||
writeSync(p: Uint8Array): number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://golang.org/pkg/io/#Closer
|
|
||||||
export interface Closer {
|
|
||||||
// The behavior of Close after the first call is undefined. Specific
|
|
||||||
// implementations may document their own behavior.
|
|
||||||
close(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://golang.org/pkg/io/#Seeker
|
|
||||||
export interface Seeker {
|
|
||||||
seek(offset: number, whence: SeekMode): Promise<number>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SeekerSync {
|
|
||||||
seekSync(offset: number, whence: SeekMode): number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function copy(
|
|
||||||
src: Reader,
|
|
||||||
dst: Writer,
|
|
||||||
options?: {
|
|
||||||
bufSize?: number;
|
|
||||||
},
|
|
||||||
): Promise<number> {
|
|
||||||
let n = 0;
|
|
||||||
const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE;
|
|
||||||
const b = new Uint8Array(bufSize);
|
|
||||||
let gotEOF = false;
|
|
||||||
while (gotEOF === false) {
|
|
||||||
const result = await src.read(b);
|
|
||||||
if (result === null) {
|
|
||||||
gotEOF = true;
|
|
||||||
} else {
|
|
||||||
let nwritten = 0;
|
|
||||||
while (nwritten < result) {
|
|
||||||
nwritten += await dst.write(b.subarray(nwritten, result));
|
|
||||||
}
|
|
||||||
n += nwritten;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function* iter(
|
|
||||||
r: Reader,
|
|
||||||
options?: {
|
|
||||||
bufSize?: number;
|
|
||||||
},
|
|
||||||
): AsyncIterableIterator<Uint8Array> {
|
|
||||||
const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE;
|
|
||||||
const b = new Uint8Array(bufSize);
|
|
||||||
while (true) {
|
|
||||||
const result = await r.read(b);
|
|
||||||
if (result === null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield b.subarray(0, result);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function* iterSync(
|
|
||||||
r: ReaderSync,
|
|
||||||
options?: {
|
|
||||||
bufSize?: number;
|
|
||||||
},
|
|
||||||
): IterableIterator<Uint8Array> {
|
|
||||||
const bufSize = options?.bufSize ?? DEFAULT_BUFFER_SIZE;
|
|
||||||
const b = new Uint8Array(bufSize);
|
|
||||||
while (true) {
|
|
||||||
const result = r.readSync(b);
|
|
||||||
if (result === null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
yield b.subarray(0, result);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { bootstrapMainRuntime } from "./runtime_main.ts";
|
|
||||||
import { bootstrapWorkerRuntime } from "./runtime_worker.ts";
|
|
||||||
|
|
||||||
// Removes the `__proto__` for security reasons. This intentionally makes
|
|
||||||
// Deno non compliant with ECMA-262 Annex B.2.2.1
|
|
||||||
//
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
delete (Object.prototype as any).__proto__;
|
|
||||||
|
|
||||||
Object.defineProperties(globalThis, {
|
|
||||||
bootstrap: {
|
|
||||||
value: {
|
|
||||||
mainRuntime: bootstrapMainRuntime,
|
|
||||||
workerRuntime: bootstrapWorkerRuntime,
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
},
|
|
||||||
});
|
|
192
cli/js/net.ts
192
cli/js/net.ts
|
@ -1,192 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { errors } from "./errors.ts";
|
|
||||||
import type { Reader, Writer, Closer } from "./io.ts";
|
|
||||||
import { read, write } from "./ops/io.ts";
|
|
||||||
import { close } from "./ops/resources.ts";
|
|
||||||
import * as netOps from "./ops/net.ts";
|
|
||||||
import type { Addr } from "./ops/net.ts";
|
|
||||||
export type { ShutdownMode, NetAddr, UnixAddr } from "./ops/net.ts";
|
|
||||||
export { shutdown } from "./ops/net.ts";
|
|
||||||
|
|
||||||
export interface DatagramConn extends AsyncIterable<[Uint8Array, Addr]> {
|
|
||||||
receive(p?: Uint8Array): Promise<[Uint8Array, Addr]>;
|
|
||||||
|
|
||||||
send(p: Uint8Array, addr: Addr): Promise<number>;
|
|
||||||
|
|
||||||
close(): void;
|
|
||||||
|
|
||||||
addr: Addr;
|
|
||||||
|
|
||||||
[Symbol.asyncIterator](): AsyncIterableIterator<[Uint8Array, Addr]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Listener extends AsyncIterable<Conn> {
|
|
||||||
accept(): Promise<Conn>;
|
|
||||||
|
|
||||||
close(): void;
|
|
||||||
|
|
||||||
addr: Addr;
|
|
||||||
|
|
||||||
rid: number;
|
|
||||||
|
|
||||||
[Symbol.asyncIterator](): AsyncIterableIterator<Conn>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ConnImpl implements Conn {
|
|
||||||
constructor(
|
|
||||||
readonly rid: number,
|
|
||||||
readonly remoteAddr: Addr,
|
|
||||||
readonly localAddr: Addr,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
write(p: Uint8Array): Promise<number> {
|
|
||||||
return write(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
read(p: Uint8Array): Promise<number | null> {
|
|
||||||
return read(this.rid, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(lucacasonato): make this unavailable in stable
|
|
||||||
closeWrite(): void {
|
|
||||||
netOps.shutdown(this.rid, netOps.ShutdownMode.Write);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ListenerImpl implements Listener {
|
|
||||||
constructor(readonly rid: number, readonly addr: Addr) {}
|
|
||||||
|
|
||||||
async accept(): Promise<Conn> {
|
|
||||||
const res = await netOps.accept(this.rid, this.addr.transport);
|
|
||||||
return new ConnImpl(res.rid, res.remoteAddr, res.localAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
async next(): Promise<IteratorResult<Conn>> {
|
|
||||||
let conn: Conn;
|
|
||||||
try {
|
|
||||||
conn = await this.accept();
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof errors.BadResource) {
|
|
||||||
return { value: undefined, done: true };
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return { value: conn!, done: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
return(value?: Conn): Promise<IteratorResult<Conn>> {
|
|
||||||
this.close();
|
|
||||||
return Promise.resolve({ value, done: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Symbol.asyncIterator](): AsyncIterableIterator<Conn> {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class DatagramImpl implements DatagramConn {
|
|
||||||
constructor(
|
|
||||||
readonly rid: number,
|
|
||||||
readonly addr: Addr,
|
|
||||||
public bufSize: number = 1024,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async receive(p?: Uint8Array): Promise<[Uint8Array, Addr]> {
|
|
||||||
const buf = p || new Uint8Array(this.bufSize);
|
|
||||||
const { size, remoteAddr } = await netOps.receive(
|
|
||||||
this.rid,
|
|
||||||
this.addr.transport,
|
|
||||||
buf,
|
|
||||||
);
|
|
||||||
const sub = buf.subarray(0, size);
|
|
||||||
return [sub, remoteAddr];
|
|
||||||
}
|
|
||||||
|
|
||||||
send(p: Uint8Array, addr: Addr): Promise<number> {
|
|
||||||
const remote = { hostname: "127.0.0.1", ...addr };
|
|
||||||
|
|
||||||
const args = { ...remote, rid: this.rid };
|
|
||||||
return netOps.send(args as netOps.SendRequest, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
async *[Symbol.asyncIterator](): AsyncIterableIterator<[Uint8Array, Addr]> {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
yield await this.receive();
|
|
||||||
} catch (err) {
|
|
||||||
if (err instanceof errors.BadResource) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Conn extends Reader, Writer, Closer {
|
|
||||||
localAddr: Addr;
|
|
||||||
remoteAddr: Addr;
|
|
||||||
rid: number;
|
|
||||||
closeWrite(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListenOptions {
|
|
||||||
port: number;
|
|
||||||
hostname?: string;
|
|
||||||
transport?: "tcp";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listen(
|
|
||||||
options: ListenOptions & { transport?: "tcp" },
|
|
||||||
): Listener;
|
|
||||||
export function listen(options: ListenOptions): Listener {
|
|
||||||
const res = netOps.listen({
|
|
||||||
transport: "tcp",
|
|
||||||
hostname: "0.0.0.0",
|
|
||||||
...(options as ListenOptions),
|
|
||||||
});
|
|
||||||
|
|
||||||
return new ListenerImpl(res.rid, res.localAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConnectOptions {
|
|
||||||
port: number;
|
|
||||||
hostname?: string;
|
|
||||||
transport?: "tcp";
|
|
||||||
}
|
|
||||||
export interface UnixConnectOptions {
|
|
||||||
transport: "unix";
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
export async function connect(options: UnixConnectOptions): Promise<Conn>;
|
|
||||||
export async function connect(options: ConnectOptions): Promise<Conn>;
|
|
||||||
export async function connect(
|
|
||||||
options: ConnectOptions | UnixConnectOptions,
|
|
||||||
): Promise<Conn> {
|
|
||||||
let res;
|
|
||||||
|
|
||||||
if (options.transport === "unix") {
|
|
||||||
res = await netOps.connect(options);
|
|
||||||
} else {
|
|
||||||
res = await netOps.connect({
|
|
||||||
transport: "tcp",
|
|
||||||
hostname: "127.0.0.1",
|
|
||||||
...options,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import * as netOps from "./ops/net.ts";
|
|
||||||
import {
|
|
||||||
Listener,
|
|
||||||
DatagramConn,
|
|
||||||
ListenerImpl,
|
|
||||||
DatagramImpl,
|
|
||||||
ConnectOptions,
|
|
||||||
Conn,
|
|
||||||
ConnImpl,
|
|
||||||
listen as stableListen,
|
|
||||||
connect as stableConnect,
|
|
||||||
} from "./net.ts";
|
|
||||||
|
|
||||||
export interface ListenOptions {
|
|
||||||
port: number;
|
|
||||||
hostname?: string;
|
|
||||||
transport?: "tcp" | "udp";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnixListenOptions {
|
|
||||||
transport: "unix" | "unixpacket";
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnixConnectOptions {
|
|
||||||
transport: "unix";
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listen(
|
|
||||||
options: ListenOptions & { transport?: "tcp" },
|
|
||||||
): Listener;
|
|
||||||
export function listen(
|
|
||||||
options: UnixListenOptions & { transport: "unix" },
|
|
||||||
): Listener;
|
|
||||||
export function listen(options: ListenOptions | UnixListenOptions): Listener {
|
|
||||||
if (options.transport === "unix") {
|
|
||||||
const res = netOps.listen(options);
|
|
||||||
return new ListenerImpl(res.rid, res.localAddr);
|
|
||||||
} else {
|
|
||||||
return stableListen(options as ListenOptions & { transport?: "tcp" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listenDatagram(
|
|
||||||
options: ListenOptions & { transport: "udp" },
|
|
||||||
): DatagramConn;
|
|
||||||
export function listenDatagram(
|
|
||||||
options: UnixListenOptions & { transport: "unixpacket" },
|
|
||||||
): DatagramConn;
|
|
||||||
export function listenDatagram(
|
|
||||||
options: ListenOptions | UnixListenOptions,
|
|
||||||
): DatagramConn {
|
|
||||||
let res;
|
|
||||||
if (options.transport === "unixpacket") {
|
|
||||||
res = netOps.listen(options);
|
|
||||||
} else {
|
|
||||||
res = netOps.listen({
|
|
||||||
transport: "udp",
|
|
||||||
hostname: "127.0.0.1",
|
|
||||||
...(options as ListenOptions),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DatagramImpl(res.rid, res.localAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function connect(
|
|
||||||
options: ConnectOptions | UnixConnectOptions,
|
|
||||||
): Promise<Conn> {
|
|
||||||
if (options.transport === "unix") {
|
|
||||||
const res = await netOps.connect(options);
|
|
||||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
|
||||||
} else {
|
|
||||||
return stableConnect(options as ConnectOptions);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,94 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import * as util from "../util.ts";
|
|
||||||
import { core } from "../core.ts";
|
|
||||||
import { ErrorKind, getErrorClass } from "../errors.ts";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
type Ok = any;
|
|
||||||
|
|
||||||
interface JsonError {
|
|
||||||
kind: ErrorKind;
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface JsonResponse {
|
|
||||||
ok?: Ok;
|
|
||||||
err?: JsonError;
|
|
||||||
promiseId?: number; // Only present in async messages.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using an object without a prototype because `Map` was causing GC problems.
|
|
||||||
const promiseTable: Record<
|
|
||||||
number,
|
|
||||||
util.Resolvable<JsonResponse>
|
|
||||||
> = Object.create(null);
|
|
||||||
let _nextPromiseId = 1;
|
|
||||||
|
|
||||||
function nextPromiseId(): number {
|
|
||||||
return _nextPromiseId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
function decode(ui8: Uint8Array): JsonResponse {
|
|
||||||
return JSON.parse(core.decode(ui8));
|
|
||||||
}
|
|
||||||
|
|
||||||
function encode(args: object): Uint8Array {
|
|
||||||
return core.encode(JSON.stringify(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
function unwrapResponse(res: JsonResponse): Ok {
|
|
||||||
if (res.err != null) {
|
|
||||||
throw new (getErrorClass(res.err.kind))(res.err.message);
|
|
||||||
}
|
|
||||||
util.assert(res.ok != null);
|
|
||||||
return res.ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function asyncMsgFromRust(resUi8: Uint8Array): void {
|
|
||||||
const res = decode(resUi8);
|
|
||||||
util.assert(res.promiseId != null);
|
|
||||||
|
|
||||||
const promise = promiseTable[res.promiseId!];
|
|
||||||
util.assert(promise != null);
|
|
||||||
delete promiseTable[res.promiseId!];
|
|
||||||
promise.resolve(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendSync(
|
|
||||||
opName: string,
|
|
||||||
args: object = {},
|
|
||||||
...zeroCopy: Uint8Array[]
|
|
||||||
): Ok {
|
|
||||||
util.log("sendSync", opName);
|
|
||||||
const argsUi8 = encode(args);
|
|
||||||
const resUi8 = core.dispatchByName(opName, argsUi8, ...zeroCopy);
|
|
||||||
util.assert(resUi8 != null);
|
|
||||||
const res = decode(resUi8);
|
|
||||||
util.assert(res.promiseId == null);
|
|
||||||
return unwrapResponse(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function sendAsync(
|
|
||||||
opName: string,
|
|
||||||
args: object = {},
|
|
||||||
...zeroCopy: Uint8Array[]
|
|
||||||
): Promise<Ok> {
|
|
||||||
util.log("sendAsync", opName);
|
|
||||||
const promiseId = nextPromiseId();
|
|
||||||
args = Object.assign(args, { promiseId });
|
|
||||||
const promise = util.createResolvable<Ok>();
|
|
||||||
const argsUi8 = encode(args);
|
|
||||||
const buf = core.dispatchByName(opName, argsUi8, ...zeroCopy);
|
|
||||||
if (buf != null) {
|
|
||||||
// Sync result.
|
|
||||||
const res = decode(buf);
|
|
||||||
promise.resolve(res);
|
|
||||||
} else {
|
|
||||||
// Async result.
|
|
||||||
promiseTable[promiseId] = promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await promise;
|
|
||||||
return unwrapResponse(res);
|
|
||||||
}
|
|
|
@ -1,121 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import * as util from "../util.ts";
|
|
||||||
import { core } from "../core.ts";
|
|
||||||
import { TextDecoder } from "../web/text_encoding.ts";
|
|
||||||
import { ErrorKind, errors, getErrorClass } from "../errors.ts";
|
|
||||||
|
|
||||||
// Using an object without a prototype because `Map` was causing GC problems.
|
|
||||||
const promiseTableMin: Record<
|
|
||||||
number,
|
|
||||||
util.Resolvable<RecordMinimal>
|
|
||||||
> = Object.create(null);
|
|
||||||
|
|
||||||
// Note it's important that promiseId starts at 1 instead of 0, because sync
|
|
||||||
// messages are indicated with promiseId 0. If we ever add wrap around logic for
|
|
||||||
// overflows, this should be taken into account.
|
|
||||||
let _nextPromiseId = 1;
|
|
||||||
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
|
|
||||||
function nextPromiseId(): number {
|
|
||||||
return _nextPromiseId++;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RecordMinimal {
|
|
||||||
promiseId: number;
|
|
||||||
arg: number;
|
|
||||||
result: number;
|
|
||||||
err?: {
|
|
||||||
kind: ErrorKind;
|
|
||||||
message: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function recordFromBufMinimal(ui8: Uint8Array): RecordMinimal {
|
|
||||||
const header = ui8.subarray(0, 12);
|
|
||||||
const buf32 = new Int32Array(
|
|
||||||
header.buffer,
|
|
||||||
header.byteOffset,
|
|
||||||
header.byteLength / 4,
|
|
||||||
);
|
|
||||||
const promiseId = buf32[0];
|
|
||||||
const arg = buf32[1];
|
|
||||||
const result = buf32[2];
|
|
||||||
let err;
|
|
||||||
|
|
||||||
if (arg < 0) {
|
|
||||||
const kind = result as ErrorKind;
|
|
||||||
const message = decoder.decode(ui8.subarray(12));
|
|
||||||
err = { kind, message };
|
|
||||||
} else if (ui8.length != 12) {
|
|
||||||
throw new errors.InvalidData("BadMessage");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
promiseId,
|
|
||||||
arg,
|
|
||||||
result,
|
|
||||||
err,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function unwrapResponse(res: RecordMinimal): number {
|
|
||||||
if (res.err != null) {
|
|
||||||
throw new (getErrorClass(res.err.kind))(res.err.message);
|
|
||||||
}
|
|
||||||
return res.result;
|
|
||||||
}
|
|
||||||
|
|
||||||
const scratch32 = new Int32Array(3);
|
|
||||||
const scratchBytes = new Uint8Array(
|
|
||||||
scratch32.buffer,
|
|
||||||
scratch32.byteOffset,
|
|
||||||
scratch32.byteLength,
|
|
||||||
);
|
|
||||||
util.assert(scratchBytes.byteLength === scratch32.length * 4);
|
|
||||||
|
|
||||||
export function asyncMsgFromRust(ui8: Uint8Array): void {
|
|
||||||
const record = recordFromBufMinimal(ui8);
|
|
||||||
const { promiseId } = record;
|
|
||||||
const promise = promiseTableMin[promiseId];
|
|
||||||
delete promiseTableMin[promiseId];
|
|
||||||
util.assert(promise);
|
|
||||||
promise.resolve(record);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function sendAsyncMinimal(
|
|
||||||
opName: string,
|
|
||||||
arg: number,
|
|
||||||
zeroCopy: Uint8Array,
|
|
||||||
): Promise<number> {
|
|
||||||
const promiseId = nextPromiseId(); // AKA cmdId
|
|
||||||
scratch32[0] = promiseId;
|
|
||||||
scratch32[1] = arg;
|
|
||||||
scratch32[2] = 0; // result
|
|
||||||
const promise = util.createResolvable<RecordMinimal>();
|
|
||||||
const buf = core.dispatchByName(opName, scratchBytes, zeroCopy);
|
|
||||||
if (buf != null) {
|
|
||||||
const record = recordFromBufMinimal(buf);
|
|
||||||
// Sync result.
|
|
||||||
promise.resolve(record);
|
|
||||||
} else {
|
|
||||||
// Async result.
|
|
||||||
promiseTableMin[promiseId] = promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await promise;
|
|
||||||
return unwrapResponse(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function sendSyncMinimal(
|
|
||||||
opName: string,
|
|
||||||
arg: number,
|
|
||||||
zeroCopy: Uint8Array,
|
|
||||||
): number {
|
|
||||||
scratch32[0] = 0; // promiseId 0 indicates sync
|
|
||||||
scratch32[1] = arg;
|
|
||||||
const res = core.dispatchByName(opName, scratchBytes, zeroCopy)!;
|
|
||||||
const resRecord = recordFromBufMinimal(res);
|
|
||||||
return unwrapResponse(resRecord);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import type { DiagnosticItem } from "../diagnostics.ts";
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export function formatDiagnostics(items: DiagnosticItem[]): string {
|
|
||||||
return sendSync("op_format_diagnostic", { items });
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Location {
|
|
||||||
fileName: string;
|
|
||||||
lineNumber: number;
|
|
||||||
columnNumber: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function applySourceMap(location: Location): Location {
|
|
||||||
const res = sendSync("op_apply_source_map", location);
|
|
||||||
return {
|
|
||||||
fileName: res.fileName,
|
|
||||||
lineNumber: res.lineNumber,
|
|
||||||
columnNumber: res.columnNumber,
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendAsync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
interface FetchRequest {
|
|
||||||
url: string;
|
|
||||||
method: string | null;
|
|
||||||
headers: Array<[string, string]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FetchResponse {
|
|
||||||
bodyRid: number;
|
|
||||||
status: number;
|
|
||||||
statusText: string;
|
|
||||||
headers: Array<[string, string]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fetch(
|
|
||||||
args: FetchRequest,
|
|
||||||
body?: ArrayBufferView,
|
|
||||||
): Promise<FetchResponse> {
|
|
||||||
let zeroCopy;
|
|
||||||
if (body != null) {
|
|
||||||
zeroCopy = new Uint8Array(body.buffer, body.byteOffset, body.byteLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sendAsync("op_fetch", args, ...(zeroCopy ? [zeroCopy] : []));
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import { pathFromURL } from "../../util.ts";
|
|
||||||
|
|
||||||
export function chmodSync(path: string | URL, mode: number): void {
|
|
||||||
sendSync("op_chmod", { path: pathFromURL(path), mode });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function chmod(path: string | URL, mode: number): Promise<void> {
|
|
||||||
await sendAsync("op_chmod", { path: pathFromURL(path), mode });
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import { pathFromURL } from "../../util.ts";
|
|
||||||
|
|
||||||
export function chownSync(
|
|
||||||
path: string | URL,
|
|
||||||
uid: number | null,
|
|
||||||
gid: number | null,
|
|
||||||
): void {
|
|
||||||
sendSync("op_chown", { path: pathFromURL(path), uid, gid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function chown(
|
|
||||||
path: string | URL,
|
|
||||||
uid: number | null,
|
|
||||||
gid: number | null,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAsync("op_chown", { path: pathFromURL(path), uid, gid });
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import { pathFromURL } from "../../util.ts";
|
|
||||||
|
|
||||||
export function copyFileSync(
|
|
||||||
fromPath: string | URL,
|
|
||||||
toPath: string | URL,
|
|
||||||
): void {
|
|
||||||
sendSync("op_copy_file", {
|
|
||||||
from: pathFromURL(fromPath),
|
|
||||||
to: pathFromURL(toPath),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function copyFile(
|
|
||||||
fromPath: string | URL,
|
|
||||||
toPath: string | URL,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAsync("op_copy_file", {
|
|
||||||
from: pathFromURL(fromPath),
|
|
||||||
to: pathFromURL(toPath),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export function cwd(): string {
|
|
||||||
return sendSync("op_cwd");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function chdir(directory: string): void {
|
|
||||||
sendSync("op_chdir", { directory });
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export function linkSync(oldpath: string, newpath: string): void {
|
|
||||||
sendSync("op_link", { oldpath, newpath });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function link(oldpath: string, newpath: string): Promise<void> {
|
|
||||||
await sendAsync("op_link", { oldpath, newpath });
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export interface MakeTempOptions {
|
|
||||||
dir?: string;
|
|
||||||
prefix?: string;
|
|
||||||
suffix?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeTempDirSync(options: MakeTempOptions = {}): string {
|
|
||||||
return sendSync("op_make_temp_dir", options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeTempDir(options: MakeTempOptions = {}): Promise<string> {
|
|
||||||
return sendAsync("op_make_temp_dir", options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeTempFileSync(options: MakeTempOptions = {}): string {
|
|
||||||
return sendSync("op_make_temp_file", options);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeTempFile(options: MakeTempOptions = {}): Promise<string> {
|
|
||||||
return sendAsync("op_make_temp_file", options);
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export interface MkdirOptions {
|
|
||||||
recursive?: boolean;
|
|
||||||
mode?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MkdirArgs {
|
|
||||||
path: string;
|
|
||||||
recursive: boolean;
|
|
||||||
mode?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mkdirArgs(path: string, options?: MkdirOptions): MkdirArgs {
|
|
||||||
const args: MkdirArgs = { path, recursive: false };
|
|
||||||
if (options != null) {
|
|
||||||
if (typeof options.recursive == "boolean") {
|
|
||||||
args.recursive = options.recursive;
|
|
||||||
}
|
|
||||||
if (options.mode) {
|
|
||||||
args.mode = options.mode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function mkdirSync(path: string, options?: MkdirOptions): void {
|
|
||||||
sendSync("op_mkdir", mkdirArgs(path, options));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function mkdir(
|
|
||||||
path: string,
|
|
||||||
options?: MkdirOptions,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAsync("op_mkdir", mkdirArgs(path, options));
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import { pathFromURL } from "../../util.ts";
|
|
||||||
|
|
||||||
export interface OpenOptions {
|
|
||||||
read?: boolean;
|
|
||||||
write?: boolean;
|
|
||||||
append?: boolean;
|
|
||||||
truncate?: boolean;
|
|
||||||
create?: boolean;
|
|
||||||
createNew?: boolean;
|
|
||||||
/** Permissions to use if creating the file (defaults to `0o666`, before
|
|
||||||
* the process's umask).
|
|
||||||
* It's an error to specify mode without also setting create or createNew to `true`.
|
|
||||||
* Ignored on Windows. */
|
|
||||||
mode?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function openSync(path: string | URL, options: OpenOptions): number {
|
|
||||||
const mode: number | undefined = options?.mode;
|
|
||||||
return sendSync("op_open", { path: pathFromURL(path), options, mode });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function open(
|
|
||||||
path: string | URL,
|
|
||||||
options: OpenOptions,
|
|
||||||
): Promise<number> {
|
|
||||||
const mode: number | undefined = options?.mode;
|
|
||||||
return sendAsync("op_open", { path: pathFromURL(path), options, mode });
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import { pathFromURL } from "../../util.ts";
|
|
||||||
|
|
||||||
export interface DirEntry {
|
|
||||||
name: string;
|
|
||||||
isFile: boolean;
|
|
||||||
isDirectory: boolean;
|
|
||||||
isSymlink: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ReadDirResponse {
|
|
||||||
entries: DirEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
function res(response: ReadDirResponse): DirEntry[] {
|
|
||||||
return response.entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readDirSync(path: string | URL): Iterable<DirEntry> {
|
|
||||||
return res(sendSync("op_read_dir", { path: pathFromURL(path) }))[
|
|
||||||
Symbol.iterator
|
|
||||||
]();
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readDir(path: string | URL): AsyncIterable<DirEntry> {
|
|
||||||
const array = sendAsync("op_read_dir", { path: pathFromURL(path) }).then(res);
|
|
||||||
return {
|
|
||||||
async *[Symbol.asyncIterator](): AsyncIterableIterator<DirEntry> {
|
|
||||||
yield* await array;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export function readLinkSync(path: string): string {
|
|
||||||
return sendSync("op_read_link", { path });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readLink(path: string): Promise<string> {
|
|
||||||
return sendAsync("op_read_link", { path });
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export function realPathSync(path: string): string {
|
|
||||||
return sendSync("op_realpath", { path });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function realPath(path: string): Promise<string> {
|
|
||||||
return sendAsync("op_realpath", { path });
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import { pathFromURL } from "../../util.ts";
|
|
||||||
|
|
||||||
export interface RemoveOptions {
|
|
||||||
recursive?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function removeSync(
|
|
||||||
path: string | URL,
|
|
||||||
options: RemoveOptions = {},
|
|
||||||
): void {
|
|
||||||
sendSync("op_remove", {
|
|
||||||
path: pathFromURL(path),
|
|
||||||
recursive: !!options.recursive,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function remove(
|
|
||||||
path: string | URL,
|
|
||||||
options: RemoveOptions = {},
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAsync("op_remove", {
|
|
||||||
path: pathFromURL(path),
|
|
||||||
recursive: !!options.recursive,
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export function renameSync(oldpath: string, newpath: string): void {
|
|
||||||
sendSync("op_rename", { oldpath, newpath });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function rename(oldpath: string, newpath: string): Promise<void> {
|
|
||||||
await sendAsync("op_rename", { oldpath, newpath });
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import type { SeekMode } from "../../io.ts";
|
|
||||||
|
|
||||||
export function seekSync(
|
|
||||||
rid: number,
|
|
||||||
offset: number,
|
|
||||||
whence: SeekMode,
|
|
||||||
): number {
|
|
||||||
return sendSync("op_seek", { rid, offset, whence });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function seek(
|
|
||||||
rid: number,
|
|
||||||
offset: number,
|
|
||||||
whence: SeekMode,
|
|
||||||
): Promise<number> {
|
|
||||||
return sendAsync("op_seek", { rid, offset, whence });
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
import { build } from "../../build.ts";
|
|
||||||
import { pathFromURL } from "../../util.ts";
|
|
||||||
|
|
||||||
export interface FileInfo {
|
|
||||||
size: number;
|
|
||||||
mtime: Date | null;
|
|
||||||
atime: Date | null;
|
|
||||||
birthtime: Date | null;
|
|
||||||
dev: number | null;
|
|
||||||
ino: number | null;
|
|
||||||
mode: number | null;
|
|
||||||
nlink: number | null;
|
|
||||||
uid: number | null;
|
|
||||||
gid: number | null;
|
|
||||||
rdev: number | null;
|
|
||||||
blksize: number | null;
|
|
||||||
blocks: number | null;
|
|
||||||
isFile: boolean;
|
|
||||||
isDirectory: boolean;
|
|
||||||
isSymlink: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StatResponse {
|
|
||||||
isFile: boolean;
|
|
||||||
isDirectory: boolean;
|
|
||||||
isSymlink: boolean;
|
|
||||||
size: number;
|
|
||||||
mtime: number | null;
|
|
||||||
atime: number | null;
|
|
||||||
birthtime: number | null;
|
|
||||||
// Unix only members
|
|
||||||
dev: number;
|
|
||||||
ino: number;
|
|
||||||
mode: number;
|
|
||||||
nlink: number;
|
|
||||||
uid: number;
|
|
||||||
gid: number;
|
|
||||||
rdev: number;
|
|
||||||
blksize: number;
|
|
||||||
blocks: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function parseFileInfo(response: StatResponse): FileInfo {
|
|
||||||
const unix = build.os === "darwin" || build.os === "linux";
|
|
||||||
return {
|
|
||||||
isFile: response.isFile,
|
|
||||||
isDirectory: response.isDirectory,
|
|
||||||
isSymlink: response.isSymlink,
|
|
||||||
size: response.size,
|
|
||||||
mtime: response.mtime != null ? new Date(response.mtime) : null,
|
|
||||||
atime: response.atime != null ? new Date(response.atime) : null,
|
|
||||||
birthtime: response.birthtime != null ? new Date(response.birthtime) : null,
|
|
||||||
// Only non-null if on Unix
|
|
||||||
dev: unix ? response.dev : null,
|
|
||||||
ino: unix ? response.ino : null,
|
|
||||||
mode: unix ? response.mode : null,
|
|
||||||
nlink: unix ? response.nlink : null,
|
|
||||||
uid: unix ? response.uid : null,
|
|
||||||
gid: unix ? response.gid : null,
|
|
||||||
rdev: unix ? response.rdev : null,
|
|
||||||
blksize: unix ? response.blksize : null,
|
|
||||||
blocks: unix ? response.blocks : null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fstatSync(rid: number): FileInfo {
|
|
||||||
return parseFileInfo(sendSync("op_fstat", { rid }));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fstat(rid: number): Promise<FileInfo> {
|
|
||||||
return parseFileInfo(await sendAsync("op_fstat", { rid }));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function lstat(path: string | URL): Promise<FileInfo> {
|
|
||||||
const res = await sendAsync("op_stat", {
|
|
||||||
path: pathFromURL(path),
|
|
||||||
lstat: true,
|
|
||||||
});
|
|
||||||
return parseFileInfo(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function lstatSync(path: string | URL): FileInfo {
|
|
||||||
const res = sendSync("op_stat", {
|
|
||||||
path: pathFromURL(path),
|
|
||||||
lstat: true,
|
|
||||||
});
|
|
||||||
return parseFileInfo(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function stat(path: string | URL): Promise<FileInfo> {
|
|
||||||
const res = await sendAsync("op_stat", {
|
|
||||||
path: pathFromURL(path),
|
|
||||||
lstat: false,
|
|
||||||
});
|
|
||||||
return parseFileInfo(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function statSync(path: string | URL): FileInfo {
|
|
||||||
const res = sendSync("op_stat", {
|
|
||||||
path: pathFromURL(path),
|
|
||||||
lstat: false,
|
|
||||||
});
|
|
||||||
return parseFileInfo(res);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export interface SymlinkOptions {
|
|
||||||
type: "file" | "dir";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function symlinkSync(
|
|
||||||
oldpath: string,
|
|
||||||
newpath: string,
|
|
||||||
options?: SymlinkOptions,
|
|
||||||
): void {
|
|
||||||
sendSync("op_symlink", { oldpath, newpath, options });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function symlink(
|
|
||||||
oldpath: string,
|
|
||||||
newpath: string,
|
|
||||||
options?: SymlinkOptions,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAsync("op_symlink", { oldpath, newpath, options });
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export function fdatasyncSync(rid: number): void {
|
|
||||||
sendSync("op_fdatasync", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fdatasync(rid: number): Promise<void> {
|
|
||||||
await sendAsync("op_fdatasync", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fsyncSync(rid: number): void {
|
|
||||||
sendSync("op_fsync", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fsync(rid: number): Promise<void> {
|
|
||||||
await sendAsync("op_fsync", { rid });
|
|
||||||
}
|
|
|
@ -1,27 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
function coerceLen(len?: number): number {
|
|
||||||
if (len == null || len < 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function ftruncateSync(rid: number, len?: number): void {
|
|
||||||
sendSync("op_ftruncate", { rid, len: coerceLen(len) });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function ftruncate(rid: number, len?: number): Promise<void> {
|
|
||||||
await sendAsync("op_ftruncate", { rid, len: coerceLen(len) });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function truncateSync(path: string, len?: number): void {
|
|
||||||
sendSync("op_truncate", { path, len: coerceLen(len) });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function truncate(path: string, len?: number): Promise<void> {
|
|
||||||
await sendAsync("op_truncate", { path, len: coerceLen(len) });
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
export function umask(mask?: number): number {
|
|
||||||
return sendSync("op_umask", { mask });
|
|
||||||
}
|
|
|
@ -1,33 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "../dispatch_json.ts";
|
|
||||||
|
|
||||||
function toSecondsFromEpoch(v: number | Date): number {
|
|
||||||
return v instanceof Date ? Math.trunc(v.valueOf() / 1000) : v;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function utimeSync(
|
|
||||||
path: string,
|
|
||||||
atime: number | Date,
|
|
||||||
mtime: number | Date,
|
|
||||||
): void {
|
|
||||||
sendSync("op_utime", {
|
|
||||||
path,
|
|
||||||
// TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
|
|
||||||
atime: toSecondsFromEpoch(atime),
|
|
||||||
mtime: toSecondsFromEpoch(mtime),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function utime(
|
|
||||||
path: string,
|
|
||||||
atime: number | Date,
|
|
||||||
mtime: number | Date,
|
|
||||||
): Promise<void> {
|
|
||||||
await sendAsync("op_utime", {
|
|
||||||
path,
|
|
||||||
// TODO(ry) split atime, mtime into [seconds, nanoseconds] tuple
|
|
||||||
atime: toSecondsFromEpoch(atime),
|
|
||||||
mtime: toSecondsFromEpoch(mtime),
|
|
||||||
});
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
|
||||||
import { close } from "./resources.ts";
|
|
||||||
|
|
||||||
export interface FsEvent {
|
|
||||||
kind: "any" | "access" | "create" | "modify" | "remove";
|
|
||||||
paths: string[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface FsWatcherOptions {
|
|
||||||
recursive: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
class FsWatcher implements AsyncIterableIterator<FsEvent> {
|
|
||||||
readonly rid: number;
|
|
||||||
|
|
||||||
constructor(paths: string[], options: FsWatcherOptions) {
|
|
||||||
const { recursive } = options;
|
|
||||||
this.rid = sendSync("op_fs_events_open", { recursive, paths });
|
|
||||||
}
|
|
||||||
|
|
||||||
next(): Promise<IteratorResult<FsEvent>> {
|
|
||||||
return sendAsync("op_fs_events_poll", {
|
|
||||||
rid: this.rid,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return(value?: FsEvent): Promise<IteratorResult<FsEvent>> {
|
|
||||||
close(this.rid);
|
|
||||||
return Promise.resolve({ value, done: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
[Symbol.asyncIterator](): AsyncIterableIterator<FsEvent> {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function watchFs(
|
|
||||||
paths: string | string[],
|
|
||||||
options: FsWatcherOptions = { recursive: true },
|
|
||||||
): AsyncIterableIterator<FsEvent> {
|
|
||||||
return new FsWatcher(Array.isArray(paths) ? paths : [paths], options);
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
import { assert } from "../util.ts";
|
|
||||||
|
|
||||||
export function getRandomValues<
|
|
||||||
T extends
|
|
||||||
| Int8Array
|
|
||||||
| Uint8Array
|
|
||||||
| Uint8ClampedArray
|
|
||||||
| Int16Array
|
|
||||||
| Uint16Array
|
|
||||||
| Int32Array
|
|
||||||
| Uint32Array,
|
|
||||||
>(typedArray: T): T {
|
|
||||||
assert(typedArray !== null, "Input must not be null");
|
|
||||||
assert(typedArray.length <= 65536, "Input must not be longer than 65536");
|
|
||||||
const ui8 = new Uint8Array(
|
|
||||||
typedArray.buffer,
|
|
||||||
typedArray.byteOffset,
|
|
||||||
typedArray.byteLength,
|
|
||||||
);
|
|
||||||
sendSync("op_get_random_values", {}, ui8);
|
|
||||||
return typedArray;
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
/** https://url.spec.whatwg.org/#idna */
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export function domainToAscii(
|
|
||||||
domain: string,
|
|
||||||
{ beStrict = false }: { beStrict?: boolean } = {},
|
|
||||||
): string {
|
|
||||||
return sendSync("op_domain_to_ascii", { domain, beStrict });
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendAsyncMinimal, sendSyncMinimal } from "./dispatch_minimal.ts";
|
|
||||||
|
|
||||||
export function readSync(rid: number, buffer: Uint8Array): number | null {
|
|
||||||
if (buffer.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nread = sendSyncMinimal("op_read", rid, buffer);
|
|
||||||
if (nread < 0) {
|
|
||||||
throw new Error("read error");
|
|
||||||
}
|
|
||||||
|
|
||||||
return nread === 0 ? null : nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function read(
|
|
||||||
rid: number,
|
|
||||||
buffer: Uint8Array,
|
|
||||||
): Promise<number | null> {
|
|
||||||
if (buffer.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const nread = await sendAsyncMinimal("op_read", rid, buffer);
|
|
||||||
if (nread < 0) {
|
|
||||||
throw new Error("read error");
|
|
||||||
}
|
|
||||||
|
|
||||||
return nread === 0 ? null : nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function writeSync(rid: number, data: Uint8Array): number {
|
|
||||||
const result = sendSyncMinimal("op_write", rid, data);
|
|
||||||
if (result < 0) {
|
|
||||||
throw new Error("write error");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function write(rid: number, data: Uint8Array): Promise<number> {
|
|
||||||
const result = await sendAsyncMinimal("op_write", rid, data);
|
|
||||||
if (result < 0) {
|
|
||||||
throw new Error("write error");
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export interface NetAddr {
|
|
||||||
transport: "tcp" | "udp";
|
|
||||||
hostname: string;
|
|
||||||
port: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UnixAddr {
|
|
||||||
transport: "unix" | "unixpacket";
|
|
||||||
path: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Addr = NetAddr | UnixAddr;
|
|
||||||
|
|
||||||
export enum ShutdownMode {
|
|
||||||
// See http://man7.org/linux/man-pages/man2/shutdown.2.html
|
|
||||||
// Corresponding to SHUT_RD, SHUT_WR, SHUT_RDWR
|
|
||||||
Read = 0,
|
|
||||||
Write = 1,
|
|
||||||
ReadWrite, // unused
|
|
||||||
}
|
|
||||||
|
|
||||||
export function shutdown(rid: number, how: ShutdownMode): Promise<void> {
|
|
||||||
sendSync("op_shutdown", { rid, how });
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AcceptResponse {
|
|
||||||
rid: number;
|
|
||||||
localAddr: Addr;
|
|
||||||
remoteAddr: Addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function accept(
|
|
||||||
rid: number,
|
|
||||||
transport: string,
|
|
||||||
): Promise<AcceptResponse> {
|
|
||||||
return sendAsync("op_accept", { rid, transport });
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ListenRequest = Addr;
|
|
||||||
|
|
||||||
interface ListenResponse {
|
|
||||||
rid: number;
|
|
||||||
localAddr: Addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listen(args: ListenRequest): ListenResponse {
|
|
||||||
return sendSync("op_listen", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ConnectResponse {
|
|
||||||
rid: number;
|
|
||||||
localAddr: Addr;
|
|
||||||
remoteAddr: Addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ConnectRequest = Addr;
|
|
||||||
|
|
||||||
export function connect(args: ConnectRequest): Promise<ConnectResponse> {
|
|
||||||
return sendAsync("op_connect", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ReceiveResponse {
|
|
||||||
size: number;
|
|
||||||
remoteAddr: Addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function receive(
|
|
||||||
rid: number,
|
|
||||||
transport: string,
|
|
||||||
zeroCopy: Uint8Array,
|
|
||||||
): Promise<ReceiveResponse> {
|
|
||||||
return sendAsync("op_datagram_receive", { rid, transport }, zeroCopy);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type SendRequest = {
|
|
||||||
rid: number;
|
|
||||||
} & Addr;
|
|
||||||
|
|
||||||
export function send(args: SendRequest, zeroCopy: Uint8Array): Promise<number> {
|
|
||||||
return sendAsync("op_datagram_send", args, zeroCopy);
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export function loadavg(): number[] {
|
|
||||||
return sendSync("op_loadavg");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hostname(): string {
|
|
||||||
return sendSync("op_hostname");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function osRelease(): string {
|
|
||||||
return sendSync("op_os_release");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function exit(code = 0): never {
|
|
||||||
sendSync("op_exit", { code });
|
|
||||||
throw new Error("Code not reachable");
|
|
||||||
}
|
|
||||||
|
|
||||||
function setEnv(key: string, value: string): void {
|
|
||||||
sendSync("op_set_env", { key, value });
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEnv(key: string): string | undefined {
|
|
||||||
return sendSync("op_get_env", { key })[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
function deleteEnv(key: string): void {
|
|
||||||
sendSync("op_delete_env", { key });
|
|
||||||
}
|
|
||||||
|
|
||||||
export const env = {
|
|
||||||
get: getEnv,
|
|
||||||
toObject(): Record<string, string> {
|
|
||||||
return sendSync("op_env");
|
|
||||||
},
|
|
||||||
set: setEnv,
|
|
||||||
delete: deleteEnv,
|
|
||||||
};
|
|
||||||
|
|
||||||
export function execPath(): string {
|
|
||||||
return sendSync("op_exec_path");
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
import type { PermissionState } from "../permissions.ts";
|
|
||||||
|
|
||||||
interface PermissionRequest {
|
|
||||||
name: string;
|
|
||||||
url?: string;
|
|
||||||
path?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function query(desc: PermissionRequest): PermissionState {
|
|
||||||
return sendSync("op_query_permission", desc).state;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function revoke(desc: PermissionRequest): PermissionState {
|
|
||||||
return sendSync("op_revoke_permission", desc).state;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function request(desc: PermissionRequest): PermissionState {
|
|
||||||
return sendSync("op_request_permission", desc).state;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export function openPlugin(filename: string): number {
|
|
||||||
return sendSync("op_open_plugin", { filename });
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
|
||||||
import { assert } from "../util.ts";
|
|
||||||
|
|
||||||
export function kill(pid: number, signo: number): void {
|
|
||||||
sendSync("op_kill", { pid, signo });
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RunStatusResponse {
|
|
||||||
gotSignal: boolean;
|
|
||||||
exitCode: number;
|
|
||||||
exitSignal: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function runStatus(rid: number): Promise<RunStatusResponse> {
|
|
||||||
return sendAsync("op_run_status", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RunRequest {
|
|
||||||
cmd: string[];
|
|
||||||
cwd?: string;
|
|
||||||
env?: Array<[string, string]>;
|
|
||||||
stdin: string;
|
|
||||||
stdout: string;
|
|
||||||
stderr: string;
|
|
||||||
stdinRid: number;
|
|
||||||
stdoutRid: number;
|
|
||||||
stderrRid: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RunResponse {
|
|
||||||
rid: number;
|
|
||||||
pid: number;
|
|
||||||
stdinRid: number | null;
|
|
||||||
stdoutRid: number | null;
|
|
||||||
stderrRid: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function run(request: RunRequest): RunResponse {
|
|
||||||
assert(request.cmd.length > 0);
|
|
||||||
return sendSync("op_run", request);
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export function startRepl(historyFile: string): number {
|
|
||||||
return sendSync("op_repl_start", { historyFile });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function readline(rid: number, prompt: string): Promise<string> {
|
|
||||||
return sendAsync("op_repl_readline", { rid, prompt });
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export type ResourceMap = Record<number, string>;
|
|
||||||
|
|
||||||
export function resources(): ResourceMap {
|
|
||||||
const res = sendSync("op_resources") as Array<[number, string]>;
|
|
||||||
const resources: ResourceMap = {};
|
|
||||||
for (const resourceTuple of res) {
|
|
||||||
resources[resourceTuple[0]] = resourceTuple[1];
|
|
||||||
}
|
|
||||||
return resources;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function close(rid: number): void {
|
|
||||||
sendSync("op_close", { rid });
|
|
||||||
}
|
|
|
@ -1,45 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export interface Start {
|
|
||||||
args: string[];
|
|
||||||
cwd: string;
|
|
||||||
debugFlag: boolean;
|
|
||||||
denoVersion: string;
|
|
||||||
noColor: boolean;
|
|
||||||
pid: number;
|
|
||||||
ppid: number;
|
|
||||||
repl: boolean;
|
|
||||||
target: string;
|
|
||||||
tsVersion: string;
|
|
||||||
unstableFlag: boolean;
|
|
||||||
v8Version: string;
|
|
||||||
versionFlag: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function opStart(): Start {
|
|
||||||
return sendSync("op_start");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function opMainModule(): string {
|
|
||||||
return sendSync("op_main_module");
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Metrics {
|
|
||||||
opsDispatched: number;
|
|
||||||
opsDispatchedSync: number;
|
|
||||||
opsDispatchedAsync: number;
|
|
||||||
opsDispatchedAsyncUnref: number;
|
|
||||||
opsCompleted: number;
|
|
||||||
opsCompletedSync: number;
|
|
||||||
opsCompletedAsync: number;
|
|
||||||
opsCompletedAsyncUnref: number;
|
|
||||||
bytesSentControl: number;
|
|
||||||
bytesSentData: number;
|
|
||||||
bytesReceived: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function metrics(): Metrics {
|
|
||||||
return sendSync("op_metrics");
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendAsync } from "./dispatch_json.ts";
|
|
||||||
import type { DiagnosticItem } from "../diagnostics.ts";
|
|
||||||
|
|
||||||
interface CompileRequest {
|
|
||||||
rootName: string;
|
|
||||||
sources?: Record<string, string>;
|
|
||||||
options?: string;
|
|
||||||
bundle: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CompileResponse {
|
|
||||||
diagnostics: DiagnosticItem[];
|
|
||||||
output?: string;
|
|
||||||
emitMap?: Record<string, Record<string, string>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function compile(request: CompileRequest): Promise<CompileResponse> {
|
|
||||||
return sendAsync("op_compile", request);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TranspileRequest {
|
|
||||||
sources: Record<string, string>;
|
|
||||||
options?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TranspileOnlyResult {
|
|
||||||
source: string;
|
|
||||||
map?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function transpile(
|
|
||||||
request: TranspileRequest,
|
|
||||||
): Promise<Record<string, TranspileOnlyResult>> {
|
|
||||||
return sendAsync("op_transpile", request);
|
|
||||||
}
|
|
|
@ -1,23 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
interface BindSignalResponse {
|
|
||||||
rid: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PollSignalResponse {
|
|
||||||
done: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function bindSignal(signo: number): BindSignalResponse {
|
|
||||||
return sendSync("op_signal_bind", { signo });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pollSignal(rid: number): Promise<PollSignalResponse> {
|
|
||||||
return sendAsync("op_signal_poll", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function unbindSignal(rid: number): void {
|
|
||||||
sendSync("op_signal_unbind", { rid });
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync, sendAsync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
interface NowResponse {
|
|
||||||
seconds: number;
|
|
||||||
subsecNanos: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function stopGlobalTimer(): void {
|
|
||||||
sendSync("op_global_timer_stop");
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function startGlobalTimer(timeout: number): Promise<void> {
|
|
||||||
await sendAsync("op_global_timer", { timeout });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function now(): NowResponse {
|
|
||||||
return sendSync("op_now");
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendAsync, sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export interface ConnectTLSRequest {
|
|
||||||
transport: "tcp";
|
|
||||||
hostname: string;
|
|
||||||
port: number;
|
|
||||||
certFile?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EstablishTLSResponse {
|
|
||||||
rid: number;
|
|
||||||
localAddr: {
|
|
||||||
hostname: string;
|
|
||||||
port: number;
|
|
||||||
transport: "tcp";
|
|
||||||
};
|
|
||||||
remoteAddr: {
|
|
||||||
hostname: string;
|
|
||||||
port: number;
|
|
||||||
transport: "tcp";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function connectTls(
|
|
||||||
args: ConnectTLSRequest,
|
|
||||||
): Promise<EstablishTLSResponse> {
|
|
||||||
return sendAsync("op_connect_tls", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AcceptTLSResponse {
|
|
||||||
rid: number;
|
|
||||||
localAddr: {
|
|
||||||
hostname: string;
|
|
||||||
port: number;
|
|
||||||
transport: "tcp";
|
|
||||||
};
|
|
||||||
remoteAddr: {
|
|
||||||
hostname: string;
|
|
||||||
port: number;
|
|
||||||
transport: "tcp";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function acceptTLS(rid: number): Promise<AcceptTLSResponse> {
|
|
||||||
return sendAsync("op_accept_tls", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListenTLSRequest {
|
|
||||||
port: number;
|
|
||||||
hostname: string;
|
|
||||||
transport: "tcp";
|
|
||||||
certFile: string;
|
|
||||||
keyFile: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ListenTLSResponse {
|
|
||||||
rid: number;
|
|
||||||
localAddr: {
|
|
||||||
hostname: string;
|
|
||||||
port: number;
|
|
||||||
transport: "tcp";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listenTls(args: ListenTLSRequest): ListenTLSResponse {
|
|
||||||
return sendSync("op_listen_tls", args);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StartTLSRequest {
|
|
||||||
rid: number;
|
|
||||||
hostname: string;
|
|
||||||
certFile?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function startTls(args: StartTLSRequest): Promise<EstablishTLSResponse> {
|
|
||||||
return sendAsync("op_start_tls", args);
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export function consoleSize(rid: number): [number, number] {
|
|
||||||
return sendSync("op_console_size", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isatty(rid: number): boolean {
|
|
||||||
return sendSync("op_isatty", { rid });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setRaw(rid: number, mode: boolean): void {
|
|
||||||
sendSync("op_set_raw", { rid, mode });
|
|
||||||
}
|
|
|
@ -1,11 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
export function postMessage(data: Uint8Array): void {
|
|
||||||
sendSync("op_worker_post_message", {}, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function close(): void {
|
|
||||||
sendSync("op_worker_close");
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { sendAsync, sendSync } from "./dispatch_json.ts";
|
|
||||||
|
|
||||||
interface CreateWorkerResponse {
|
|
||||||
id: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createWorker(
|
|
||||||
specifier: string,
|
|
||||||
hasSourceCode: boolean,
|
|
||||||
sourceCode: string,
|
|
||||||
useDenoNamespace: boolean,
|
|
||||||
name?: string,
|
|
||||||
): CreateWorkerResponse {
|
|
||||||
return sendSync("op_create_worker", {
|
|
||||||
specifier,
|
|
||||||
hasSourceCode,
|
|
||||||
sourceCode,
|
|
||||||
name,
|
|
||||||
useDenoNamespace,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hostTerminateWorker(id: number): void {
|
|
||||||
sendSync("op_host_terminate_worker", { id });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hostPostMessage(id: number, data: Uint8Array): void {
|
|
||||||
sendSync("op_host_post_message", { id }, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hostGetMessage(id: number): Promise<any> {
|
|
||||||
return sendAsync("op_host_get_message", { id });
|
|
||||||
}
|
|
|
@ -1,79 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import * as permissionsOps from "./ops/permissions.ts";
|
|
||||||
|
|
||||||
export type PermissionName =
|
|
||||||
| "read"
|
|
||||||
| "write"
|
|
||||||
| "net"
|
|
||||||
| "env"
|
|
||||||
| "run"
|
|
||||||
| "plugin"
|
|
||||||
| "hrtime";
|
|
||||||
// NOTE: Keep in sync with cli/permissions.rs
|
|
||||||
|
|
||||||
export type PermissionState = "granted" | "denied" | "prompt";
|
|
||||||
|
|
||||||
export interface RunPermissionDescriptor {
|
|
||||||
name: "run";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ReadPermissionDescriptor {
|
|
||||||
name: "read";
|
|
||||||
path?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface WritePermissionDescriptor {
|
|
||||||
name: "write";
|
|
||||||
path?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface NetPermissionDescriptor {
|
|
||||||
name: "net";
|
|
||||||
url?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EnvPermissionDescriptor {
|
|
||||||
name: "env";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PluginPermissionDescriptor {
|
|
||||||
name: "plugin";
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HrtimePermissionDescriptor {
|
|
||||||
name: "hrtime";
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PermissionDescriptor =
|
|
||||||
| RunPermissionDescriptor
|
|
||||||
| ReadPermissionDescriptor
|
|
||||||
| WritePermissionDescriptor
|
|
||||||
| NetPermissionDescriptor
|
|
||||||
| EnvPermissionDescriptor
|
|
||||||
| PluginPermissionDescriptor
|
|
||||||
| HrtimePermissionDescriptor;
|
|
||||||
|
|
||||||
export class PermissionStatus {
|
|
||||||
constructor(public state: PermissionState) {}
|
|
||||||
// TODO(kt3k): implement onchange handler
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Permissions {
|
|
||||||
query(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
|
||||||
const state = permissionsOps.query(desc);
|
|
||||||
return Promise.resolve(new PermissionStatus(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
revoke(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
|
||||||
const state = permissionsOps.revoke(desc);
|
|
||||||
return Promise.resolve(new PermissionStatus(state));
|
|
||||||
}
|
|
||||||
|
|
||||||
request(desc: PermissionDescriptor): Promise<PermissionStatus> {
|
|
||||||
const state = permissionsOps.request(desc);
|
|
||||||
return Promise.resolve(new PermissionStatus(state));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const permissions = new Permissions();
|
|
|
@ -1,136 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { File } from "./files.ts";
|
|
||||||
import { close } from "./ops/resources.ts";
|
|
||||||
import type { Closer, Reader, Writer } from "./io.ts";
|
|
||||||
import { readAll } from "./buffer.ts";
|
|
||||||
import { kill, runStatus as runStatusOp, run as runOp } from "./ops/process.ts";
|
|
||||||
|
|
||||||
// TODO Maybe extend VSCode's 'CommandOptions'?
|
|
||||||
// See https://code.visualstudio.com/docs/editor/tasks-appendix#_schema-for-tasksjson
|
|
||||||
export interface RunOptions {
|
|
||||||
cmd: string[];
|
|
||||||
cwd?: string;
|
|
||||||
env?: Record<string, string>;
|
|
||||||
stdout?: "inherit" | "piped" | "null" | number;
|
|
||||||
stderr?: "inherit" | "piped" | "null" | number;
|
|
||||||
stdin?: "inherit" | "piped" | "null" | number;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runStatus(rid: number): Promise<ProcessStatus> {
|
|
||||||
const res = await runStatusOp(rid);
|
|
||||||
|
|
||||||
if (res.gotSignal) {
|
|
||||||
const signal = res.exitSignal;
|
|
||||||
return { success: false, code: 128 + signal, signal };
|
|
||||||
} else if (res.exitCode != 0) {
|
|
||||||
return { success: false, code: res.exitCode };
|
|
||||||
} else {
|
|
||||||
return { success: true, code: 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Process<T extends RunOptions = RunOptions> {
|
|
||||||
readonly rid: number;
|
|
||||||
readonly pid: number;
|
|
||||||
readonly stdin!: T["stdin"] extends "piped" ? Writer & Closer
|
|
||||||
: (Writer & Closer) | null;
|
|
||||||
readonly stdout!: T["stdout"] extends "piped" ? Reader & Closer
|
|
||||||
: (Reader & Closer) | null;
|
|
||||||
readonly stderr!: T["stderr"] extends "piped" ? Reader & Closer
|
|
||||||
: (Reader & Closer) | null;
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
constructor(res: RunResponse) {
|
|
||||||
this.rid = res.rid;
|
|
||||||
this.pid = res.pid;
|
|
||||||
|
|
||||||
if (res.stdinRid && res.stdinRid > 0) {
|
|
||||||
this.stdin = (new File(res.stdinRid) as unknown) as Process<T>["stdin"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.stdoutRid && res.stdoutRid > 0) {
|
|
||||||
this.stdout = (new File(res.stdoutRid) as unknown) as Process<
|
|
||||||
T
|
|
||||||
>["stdout"];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (res.stderrRid && res.stderrRid > 0) {
|
|
||||||
this.stderr = (new File(res.stderrRid) as unknown) as Process<
|
|
||||||
T
|
|
||||||
>["stderr"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
status(): Promise<ProcessStatus> {
|
|
||||||
return runStatus(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
async output(): Promise<Uint8Array> {
|
|
||||||
if (!this.stdout) {
|
|
||||||
throw new TypeError("stdout was not piped");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return await readAll(this.stdout as Reader & Closer);
|
|
||||||
} finally {
|
|
||||||
(this.stdout as Reader & Closer).close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async stderrOutput(): Promise<Uint8Array> {
|
|
||||||
if (!this.stderr) {
|
|
||||||
throw new TypeError("stderr was not piped");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return await readAll(this.stderr as Reader & Closer);
|
|
||||||
} finally {
|
|
||||||
(this.stderr as Reader & Closer).close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(): void {
|
|
||||||
close(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
kill(signo: number): void {
|
|
||||||
kill(this.pid, signo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ProcessStatus =
|
|
||||||
| { success: true; code: 0; signal?: undefined }
|
|
||||||
| { success: false; code: number; signal?: number };
|
|
||||||
|
|
||||||
function isRid(arg: unknown): arg is number {
|
|
||||||
return !isNaN(arg as number);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface RunResponse {
|
|
||||||
rid: number;
|
|
||||||
pid: number;
|
|
||||||
stdinRid: number | null;
|
|
||||||
stdoutRid: number | null;
|
|
||||||
stderrRid: number | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function run<T extends RunOptions = RunOptions>({
|
|
||||||
cmd,
|
|
||||||
cwd = undefined,
|
|
||||||
env = {},
|
|
||||||
stdout = "inherit",
|
|
||||||
stderr = "inherit",
|
|
||||||
stdin = "inherit",
|
|
||||||
}: T): Process<T> {
|
|
||||||
const res = runOp({
|
|
||||||
cmd: cmd.map(String),
|
|
||||||
cwd,
|
|
||||||
env: Object.entries(env),
|
|
||||||
stdin: isRid(stdin) ? "" : stdin,
|
|
||||||
stdout: isRid(stdout) ? "" : stdout,
|
|
||||||
stderr: isRid(stderr) ? "" : stderr,
|
|
||||||
stdinRid: isRid(stdin) ? stdin : 0,
|
|
||||||
stdoutRid: isRid(stdout) ? stdout : 0,
|
|
||||||
stderrRid: isRid(stderr) ? stderr : 0,
|
|
||||||
}) as RunResponse;
|
|
||||||
return new Process<T>(res);
|
|
||||||
}
|
|
252
cli/js/rbtree.ts
252
cli/js/rbtree.ts
|
@ -1,252 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Derived from https://github.com/vadimg/js_bintrees. MIT Licensed.
|
|
||||||
|
|
||||||
import { assert } from "./util.ts";
|
|
||||||
|
|
||||||
class RBNode<T> {
|
|
||||||
public left: this | null;
|
|
||||||
public right: this | null;
|
|
||||||
public red: boolean;
|
|
||||||
|
|
||||||
constructor(public data: T) {
|
|
||||||
this.left = null;
|
|
||||||
this.right = null;
|
|
||||||
this.red = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
getChild(dir: boolean | number): this | null {
|
|
||||||
return dir ? this.right : this.left;
|
|
||||||
}
|
|
||||||
|
|
||||||
setChild(dir: boolean | number, val: this | null): void {
|
|
||||||
if (dir) {
|
|
||||||
this.right = val;
|
|
||||||
} else {
|
|
||||||
this.left = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RBTree<T> {
|
|
||||||
readonly #comparator: (a: T, b: T) => number;
|
|
||||||
#root: RBNode<T> | null;
|
|
||||||
|
|
||||||
constructor(comparator: (a: T, b: T) => number) {
|
|
||||||
this.#comparator = comparator;
|
|
||||||
this.#root = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns `null` if tree is empty. */
|
|
||||||
min(): T | null {
|
|
||||||
let res = this.#root;
|
|
||||||
if (res === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
while (res.left !== null) {
|
|
||||||
res = res.left;
|
|
||||||
}
|
|
||||||
return res.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns node `data` if found, `null` otherwise. */
|
|
||||||
find(data: T): T | null {
|
|
||||||
let res = this.#root;
|
|
||||||
while (res !== null) {
|
|
||||||
const c = this.#comparator(data, res.data);
|
|
||||||
if (c === 0) {
|
|
||||||
return res.data;
|
|
||||||
} else {
|
|
||||||
res = res.getChild(c > 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** returns `true` if inserted, `false` if duplicate. */
|
|
||||||
insert(data: T): boolean {
|
|
||||||
let ret = false;
|
|
||||||
|
|
||||||
if (this.#root === null) {
|
|
||||||
// empty tree
|
|
||||||
this.#root = new RBNode(data);
|
|
||||||
ret = true;
|
|
||||||
} else {
|
|
||||||
const head = new RBNode((null as unknown) as T); // fake tree root
|
|
||||||
|
|
||||||
let dir = 0;
|
|
||||||
let last = 0;
|
|
||||||
|
|
||||||
// setup
|
|
||||||
let gp = null; // grandparent
|
|
||||||
let ggp = head; // grand-grand-parent
|
|
||||||
let p: RBNode<T> | null = null; // parent
|
|
||||||
let node: RBNode<T> | null = this.#root;
|
|
||||||
ggp.right = this.#root;
|
|
||||||
|
|
||||||
// search down
|
|
||||||
while (true) {
|
|
||||||
if (node === null) {
|
|
||||||
// insert new node at the bottom
|
|
||||||
node = new RBNode(data);
|
|
||||||
p!.setChild(dir, node);
|
|
||||||
ret = true;
|
|
||||||
} else if (isRed(node.left) && isRed(node.right)) {
|
|
||||||
// color flip
|
|
||||||
node.red = true;
|
|
||||||
node.left!.red = false;
|
|
||||||
node.right!.red = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// fix red violation
|
|
||||||
if (isRed(node) && isRed(p)) {
|
|
||||||
const dir2 = ggp.right === gp;
|
|
||||||
|
|
||||||
assert(gp);
|
|
||||||
if (node === p!.getChild(last)) {
|
|
||||||
ggp.setChild(dir2, singleRotate(gp, !last));
|
|
||||||
} else {
|
|
||||||
ggp.setChild(dir2, doubleRotate(gp, !last));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const cmp = this.#comparator(node.data, data);
|
|
||||||
|
|
||||||
// stop if found
|
|
||||||
if (cmp === 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
last = dir;
|
|
||||||
dir = Number(cmp < 0); // Fix type
|
|
||||||
|
|
||||||
// update helpers
|
|
||||||
if (gp !== null) {
|
|
||||||
ggp = gp;
|
|
||||||
}
|
|
||||||
gp = p;
|
|
||||||
p = node;
|
|
||||||
node = node.getChild(dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
// update root
|
|
||||||
this.#root = head.right;
|
|
||||||
}
|
|
||||||
|
|
||||||
// make root black
|
|
||||||
this.#root!.red = false;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns `true` if removed, `false` if not found. */
|
|
||||||
remove(data: T): boolean {
|
|
||||||
if (this.#root === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const head = new RBNode((null as unknown) as T); // fake tree root
|
|
||||||
let node = head;
|
|
||||||
node.right = this.#root;
|
|
||||||
let p = null; // parent
|
|
||||||
let gp = null; // grand parent
|
|
||||||
let found = null; // found item
|
|
||||||
let dir: boolean | number = 1;
|
|
||||||
|
|
||||||
while (node.getChild(dir) !== null) {
|
|
||||||
const last = dir;
|
|
||||||
|
|
||||||
// update helpers
|
|
||||||
gp = p;
|
|
||||||
p = node;
|
|
||||||
node = node.getChild(dir)!;
|
|
||||||
|
|
||||||
const cmp = this.#comparator(data, node.data);
|
|
||||||
|
|
||||||
dir = cmp > 0;
|
|
||||||
|
|
||||||
// save found node
|
|
||||||
if (cmp === 0) {
|
|
||||||
found = node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// push the red node down
|
|
||||||
if (!isRed(node) && !isRed(node.getChild(dir))) {
|
|
||||||
if (isRed(node.getChild(!dir))) {
|
|
||||||
const sr = singleRotate(node, dir);
|
|
||||||
p.setChild(last, sr);
|
|
||||||
p = sr;
|
|
||||||
} else if (!isRed(node.getChild(!dir))) {
|
|
||||||
const sibling = p.getChild(!last);
|
|
||||||
if (sibling !== null) {
|
|
||||||
if (
|
|
||||||
!isRed(sibling.getChild(!last)) &&
|
|
||||||
!isRed(sibling.getChild(last))
|
|
||||||
) {
|
|
||||||
// color flip
|
|
||||||
p.red = false;
|
|
||||||
sibling.red = true;
|
|
||||||
node.red = true;
|
|
||||||
} else {
|
|
||||||
assert(gp);
|
|
||||||
const dir2 = gp.right === p;
|
|
||||||
|
|
||||||
if (isRed(sibling.getChild(last))) {
|
|
||||||
gp!.setChild(dir2, doubleRotate(p, last));
|
|
||||||
} else if (isRed(sibling.getChild(!last))) {
|
|
||||||
gp!.setChild(dir2, singleRotate(p, last));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ensure correct coloring
|
|
||||||
const gpc = gp.getChild(dir2);
|
|
||||||
assert(gpc);
|
|
||||||
gpc.red = true;
|
|
||||||
node.red = true;
|
|
||||||
assert(gpc.left);
|
|
||||||
gpc.left.red = false;
|
|
||||||
assert(gpc.right);
|
|
||||||
gpc.right.red = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// replace and remove if found
|
|
||||||
if (found !== null) {
|
|
||||||
found.data = node.data;
|
|
||||||
assert(p);
|
|
||||||
p.setChild(p.right === node, node.getChild(node.left === null));
|
|
||||||
}
|
|
||||||
|
|
||||||
// update root and make it black
|
|
||||||
this.#root = head.right;
|
|
||||||
if (this.#root !== null) {
|
|
||||||
this.#root.red = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return found !== null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRed<T>(node: RBNode<T> | null): boolean {
|
|
||||||
return node !== null && node.red;
|
|
||||||
}
|
|
||||||
|
|
||||||
function singleRotate<T>(root: RBNode<T>, dir: boolean | number): RBNode<T> {
|
|
||||||
const save = root.getChild(!dir);
|
|
||||||
assert(save);
|
|
||||||
|
|
||||||
root.setChild(!dir, save.getChild(dir));
|
|
||||||
save.setChild(dir, root);
|
|
||||||
|
|
||||||
root.red = true;
|
|
||||||
save.red = false;
|
|
||||||
|
|
||||||
return save;
|
|
||||||
}
|
|
||||||
|
|
||||||
function doubleRotate<T>(root: RBNode<T>, dir: boolean | number): RBNode<T> {
|
|
||||||
root.setChild(!dir, singleRotate(root.getChild(!dir)!, !dir));
|
|
||||||
return singleRotate(root, dir);
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { open, openSync } from "./files.ts";
|
|
||||||
import { readAll, readAllSync } from "./buffer.ts";
|
|
||||||
|
|
||||||
export function readFileSync(path: string | URL): Uint8Array {
|
|
||||||
const file = openSync(path);
|
|
||||||
const contents = readAllSync(file);
|
|
||||||
file.close();
|
|
||||||
return contents;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function readFile(path: string | URL): Promise<Uint8Array> {
|
|
||||||
const file = await open(path);
|
|
||||||
const contents = await readAll(file);
|
|
||||||
file.close();
|
|
||||||
return contents;
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { open, openSync } from "./files.ts";
|
|
||||||
import { readAll, readAllSync } from "./buffer.ts";
|
|
||||||
|
|
||||||
export function readTextFileSync(path: string | URL): string {
|
|
||||||
const file = openSync(path);
|
|
||||||
const contents = readAllSync(file);
|
|
||||||
file.close();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
return decoder.decode(contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function readTextFile(path: string | URL): Promise<string> {
|
|
||||||
const file = await open(path);
|
|
||||||
const contents = await readAll(file);
|
|
||||||
file.close();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
return decoder.decode(contents);
|
|
||||||
}
|
|
177
cli/js/repl.ts
177
cli/js/repl.ts
|
@ -1,177 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { exit } from "./ops/os.ts";
|
|
||||||
import { core } from "./core.ts";
|
|
||||||
import { version } from "./version.ts";
|
|
||||||
import { inspectArgs } from "./web/console.ts";
|
|
||||||
import { startRepl, readline } from "./ops/repl.ts";
|
|
||||||
import { close } from "./ops/resources.ts";
|
|
||||||
|
|
||||||
function replLog(...args: unknown[]): void {
|
|
||||||
core.print(inspectArgs(args) + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
function replError(...args: unknown[]): void {
|
|
||||||
core.print(inspectArgs(args) + "\n", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error messages that allow users to continue input
|
|
||||||
// instead of throwing an error to REPL
|
|
||||||
// ref: https://github.com/v8/v8/blob/master/src/message-template.h
|
|
||||||
// TODO(kevinkassimo): this list might not be comprehensive
|
|
||||||
const recoverableErrorMessages = [
|
|
||||||
"Unexpected end of input", // { or [ or (
|
|
||||||
"Missing initializer in const declaration", // const a
|
|
||||||
"Missing catch or finally after try", // try {}
|
|
||||||
"missing ) after argument list", // console.log(1
|
|
||||||
"Unterminated template literal", // `template
|
|
||||||
// TODO(kevinkassimo): need a parser to handling errors such as:
|
|
||||||
// "Missing } in template expression" // `${ or `${ a 123 }`
|
|
||||||
];
|
|
||||||
|
|
||||||
function isRecoverableError(e: Error): boolean {
|
|
||||||
return recoverableErrorMessages.includes(e.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns `true` if `close()` is called in REPL.
|
|
||||||
// We should quit the REPL when this function returns `true`.
|
|
||||||
function isCloseCalled(): boolean {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
return (globalThis as any).closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
type Value = any;
|
|
||||||
|
|
||||||
let lastEvalResult: Value = undefined;
|
|
||||||
let lastThrownError: Value = undefined;
|
|
||||||
|
|
||||||
// Evaluate code.
|
|
||||||
// Returns true if code is consumed (no error/irrecoverable error).
|
|
||||||
// Returns false if error is recoverable
|
|
||||||
function evaluate(code: string): boolean {
|
|
||||||
// each evalContext is a separate function body, and we want strict mode to
|
|
||||||
// work, so we should ensure that the code starts with "use strict"
|
|
||||||
const [result, errInfo] = core.evalContext(`"use strict";\n\n${code}`);
|
|
||||||
if (!errInfo) {
|
|
||||||
// when a function is eval'ed with just "use strict" sometimes the result
|
|
||||||
// is "use strict" which should be discarded
|
|
||||||
lastEvalResult = typeof result === "string" && result === "use strict"
|
|
||||||
? undefined
|
|
||||||
: result;
|
|
||||||
if (!isCloseCalled()) {
|
|
||||||
replLog(lastEvalResult);
|
|
||||||
}
|
|
||||||
} else if (errInfo.isCompileError && isRecoverableError(errInfo.thrown)) {
|
|
||||||
// Recoverable compiler error
|
|
||||||
return false; // don't consume code.
|
|
||||||
} else {
|
|
||||||
lastThrownError = errInfo.thrown;
|
|
||||||
if (errInfo.isNativeError) {
|
|
||||||
const formattedError = core.formatError(errInfo.thrown as Error);
|
|
||||||
replError(formattedError);
|
|
||||||
} else {
|
|
||||||
replError("Thrown:", errInfo.thrown);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export async function replLoop(): Promise<void> {
|
|
||||||
const { console } = globalThis;
|
|
||||||
|
|
||||||
const historyFile = "deno_history.txt";
|
|
||||||
const rid = startRepl(historyFile);
|
|
||||||
|
|
||||||
const quitRepl = (exitCode: number): void => {
|
|
||||||
// Special handling in case user calls deno.close(3).
|
|
||||||
try {
|
|
||||||
close(rid); // close signals Drop on REPL and saves history.
|
|
||||||
} catch {}
|
|
||||||
exit(exitCode);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Configure globalThis._ to give the last evaluation result.
|
|
||||||
Object.defineProperty(globalThis, "_", {
|
|
||||||
configurable: true,
|
|
||||||
get: (): Value => lastEvalResult,
|
|
||||||
set: (value: Value): Value => {
|
|
||||||
Object.defineProperty(globalThis, "_", {
|
|
||||||
value: value,
|
|
||||||
writable: true,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
console.log("Last evaluation result is no longer saved to _.");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
// Configure globalThis._error to give the last thrown error.
|
|
||||||
Object.defineProperty(globalThis, "_error", {
|
|
||||||
configurable: true,
|
|
||||||
get: (): Value => lastThrownError,
|
|
||||||
set: (value: Value): Value => {
|
|
||||||
Object.defineProperty(globalThis, "_error", {
|
|
||||||
value: value,
|
|
||||||
writable: true,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
console.log("Last thrown error is no longer saved to _error.");
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
replLog(`Deno ${version.deno}`);
|
|
||||||
replLog("exit using ctrl+d or close()");
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
if (isCloseCalled()) {
|
|
||||||
quitRepl(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let code = "";
|
|
||||||
// Top level read
|
|
||||||
try {
|
|
||||||
code = await readline(rid, "> ");
|
|
||||||
if (code.trim() === "") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
if (err.message === "EOF") {
|
|
||||||
quitRepl(0);
|
|
||||||
} else {
|
|
||||||
// If interrupted, don't print error.
|
|
||||||
if (err.message !== "Interrupted") {
|
|
||||||
// e.g. this happens when we have deno.close(3).
|
|
||||||
// We want to display the problem.
|
|
||||||
const formattedError = core.formatError(err);
|
|
||||||
replError(formattedError);
|
|
||||||
}
|
|
||||||
// Quit REPL anyways.
|
|
||||||
quitRepl(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Start continued read
|
|
||||||
while (!evaluate(code)) {
|
|
||||||
code += "\n";
|
|
||||||
try {
|
|
||||||
code += await readline(rid, " ");
|
|
||||||
} catch (err) {
|
|
||||||
// If interrupted on continued read,
|
|
||||||
// abort this read instead of quitting.
|
|
||||||
if (err.message === "Interrupted") {
|
|
||||||
break;
|
|
||||||
} else if (err.message === "EOF") {
|
|
||||||
quitRepl(0);
|
|
||||||
} else {
|
|
||||||
// e.g. this happens when we have deno.close(3).
|
|
||||||
// We want to display the problem.
|
|
||||||
const formattedError = core.formatError(err);
|
|
||||||
replError(formattedError);
|
|
||||||
quitRepl(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { core } from "./core.ts";
|
|
||||||
import * as dispatchMinimal from "./ops/dispatch_minimal.ts";
|
|
||||||
import * as dispatchJson from "./ops/dispatch_json.ts";
|
|
||||||
import * as util from "./util.ts";
|
|
||||||
import { setBuildInfo } from "./build.ts";
|
|
||||||
import { setVersions } from "./version.ts";
|
|
||||||
import { setPrepareStackTrace } from "./error_stack.ts";
|
|
||||||
import { Start, opStart } from "./ops/runtime.ts";
|
|
||||||
import { handleTimerMacrotask } from "./web/timers.ts";
|
|
||||||
|
|
||||||
function getAsyncHandler(opName: string): (msg: Uint8Array) => void {
|
|
||||||
switch (opName) {
|
|
||||||
case "op_write":
|
|
||||||
case "op_read":
|
|
||||||
return dispatchMinimal.asyncMsgFromRust;
|
|
||||||
default:
|
|
||||||
return dispatchJson.asyncMsgFromRust;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(bartlomieju): temporary solution, must be fixed when moving
|
|
||||||
// dispatches to separate crates
|
|
||||||
export function initOps(): void {
|
|
||||||
const opsMap = core.ops();
|
|
||||||
for (const [name, opId] of Object.entries(opsMap)) {
|
|
||||||
core.setAsyncHandler(opId, getAsyncHandler(name));
|
|
||||||
}
|
|
||||||
core.setMacrotaskCallback(handleTimerMacrotask);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function start(source?: string): Start {
|
|
||||||
initOps();
|
|
||||||
// First we send an empty `Start` message to let the privileged side know we
|
|
||||||
// are ready. The response should be a `StartRes` message containing the CLI
|
|
||||||
// args and other info.
|
|
||||||
const s = opStart();
|
|
||||||
setVersions(s.denoVersion, s.v8Version, s.tsVersion);
|
|
||||||
setBuildInfo(s.target);
|
|
||||||
util.setLogDebug(s.debugFlag, source);
|
|
||||||
setPrepareStackTrace(Error);
|
|
||||||
return s;
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This module is the entry point for "main" isolate, ie. the one
|
|
||||||
// that is created when you run "deno" executable.
|
|
||||||
//
|
|
||||||
// It provides a single function that should be called by Rust:
|
|
||||||
// - `bootstrapMainRuntime` - must be called once, when Isolate is created.
|
|
||||||
// It sets up runtime by providing globals for `WindowScope` and adds `Deno` global.
|
|
||||||
|
|
||||||
import * as denoNs from "./deno.ts";
|
|
||||||
import * as denoUnstableNs from "./deno_unstable.ts";
|
|
||||||
import { opMainModule } from "./ops/runtime.ts";
|
|
||||||
import { exit } from "./ops/os.ts";
|
|
||||||
import {
|
|
||||||
readOnly,
|
|
||||||
getterOnly,
|
|
||||||
writable,
|
|
||||||
windowOrWorkerGlobalScopeMethods,
|
|
||||||
windowOrWorkerGlobalScopeProperties,
|
|
||||||
eventTargetProperties,
|
|
||||||
setEventTargetData,
|
|
||||||
} from "./globals.ts";
|
|
||||||
import { unstableMethods, unstableProperties } from "./globals_unstable.ts";
|
|
||||||
import { internalObject, internalSymbol } from "./internals.ts";
|
|
||||||
import { setSignals } from "./signals.ts";
|
|
||||||
import { replLoop } from "./repl.ts";
|
|
||||||
import { setTimeout } from "./web/timers.ts";
|
|
||||||
import * as runtime from "./runtime.ts";
|
|
||||||
import { log, immutableDefine } from "./util.ts";
|
|
||||||
|
|
||||||
// TODO: factor out `Deno` global assignment to separate function
|
|
||||||
// Add internal object to Deno object.
|
|
||||||
// This is not exposed as part of the Deno types.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(denoNs as any)[internalSymbol] = internalObject;
|
|
||||||
|
|
||||||
let windowIsClosing = false;
|
|
||||||
|
|
||||||
function windowClose(): void {
|
|
||||||
if (!windowIsClosing) {
|
|
||||||
windowIsClosing = true;
|
|
||||||
// Push a macrotask to exit after a promise resolve.
|
|
||||||
// This is not perfect, but should be fine for first pass.
|
|
||||||
Promise.resolve().then(() =>
|
|
||||||
setTimeout.call(
|
|
||||||
null,
|
|
||||||
() => {
|
|
||||||
// This should be fine, since only Window/MainWorker has .close()
|
|
||||||
exit(0);
|
|
||||||
},
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const mainRuntimeGlobalProperties = {
|
|
||||||
window: readOnly(globalThis),
|
|
||||||
self: readOnly(globalThis),
|
|
||||||
// TODO(bartlomieju): from MDN docs (https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope)
|
|
||||||
// it seems those two properties should be available to workers as well
|
|
||||||
onload: writable(null),
|
|
||||||
onunload: writable(null),
|
|
||||||
close: writable(windowClose),
|
|
||||||
closed: getterOnly(() => windowIsClosing),
|
|
||||||
};
|
|
||||||
|
|
||||||
let hasBootstrapped = false;
|
|
||||||
|
|
||||||
export function bootstrapMainRuntime(): void {
|
|
||||||
if (hasBootstrapped) {
|
|
||||||
throw new Error("Worker runtime already bootstrapped");
|
|
||||||
}
|
|
||||||
// Remove bootstrapping methods from global scope
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(globalThis as any).bootstrap = undefined;
|
|
||||||
log("bootstrapMainRuntime");
|
|
||||||
hasBootstrapped = true;
|
|
||||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeMethods);
|
|
||||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeProperties);
|
|
||||||
Object.defineProperties(globalThis, eventTargetProperties);
|
|
||||||
Object.defineProperties(globalThis, mainRuntimeGlobalProperties);
|
|
||||||
setEventTargetData(globalThis);
|
|
||||||
// Registers the handler for window.onload function.
|
|
||||||
globalThis.addEventListener("load", (e) => {
|
|
||||||
const { onload } = globalThis;
|
|
||||||
if (typeof onload === "function") {
|
|
||||||
onload(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// Registers the handler for window.onunload function.
|
|
||||||
globalThis.addEventListener("unload", (e) => {
|
|
||||||
const { onunload } = globalThis;
|
|
||||||
if (typeof onunload === "function") {
|
|
||||||
onunload(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const { args, cwd, noColor, pid, ppid, repl, unstableFlag } = runtime.start();
|
|
||||||
|
|
||||||
Object.defineProperties(denoNs, {
|
|
||||||
pid: readOnly(pid),
|
|
||||||
ppid: readOnly(ppid),
|
|
||||||
noColor: readOnly(noColor),
|
|
||||||
args: readOnly(Object.freeze(args)),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (unstableFlag) {
|
|
||||||
Object.defineProperties(globalThis, unstableMethods);
|
|
||||||
Object.defineProperties(globalThis, unstableProperties);
|
|
||||||
Object.defineProperty(denoNs, "mainModule", getterOnly(opMainModule));
|
|
||||||
Object.assign(denoNs, denoUnstableNs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup `Deno` global - we're actually overriding already
|
|
||||||
// existing global `Deno` with `Deno` namespace from "./deno.ts".
|
|
||||||
immutableDefine(globalThis, "Deno", denoNs);
|
|
||||||
Object.freeze(globalThis.Deno);
|
|
||||||
Object.freeze(globalThis.Deno.core);
|
|
||||||
Object.freeze(globalThis.Deno.core.sharedQueue);
|
|
||||||
setSignals();
|
|
||||||
|
|
||||||
log("cwd", cwd);
|
|
||||||
log("args", args);
|
|
||||||
|
|
||||||
if (repl) {
|
|
||||||
replLoop();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,170 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This module is the entry point for "worker" isolate, ie. the one
|
|
||||||
// that is created using `new Worker()` JS API.
|
|
||||||
//
|
|
||||||
// It provides a single function that should be called by Rust:
|
|
||||||
// - `bootstrapWorkerRuntime` - must be called once, when Isolate is created.
|
|
||||||
// It sets up runtime by providing globals for `DedicatedWorkerScope`.
|
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import {
|
|
||||||
readOnly,
|
|
||||||
writable,
|
|
||||||
nonEnumerable,
|
|
||||||
windowOrWorkerGlobalScopeMethods,
|
|
||||||
windowOrWorkerGlobalScopeProperties,
|
|
||||||
eventTargetProperties,
|
|
||||||
setEventTargetData,
|
|
||||||
} from "./globals.ts";
|
|
||||||
import { unstableMethods, unstableProperties } from "./globals_unstable.ts";
|
|
||||||
import * as denoNs from "./deno.ts";
|
|
||||||
import * as denoUnstableNs from "./deno_unstable.ts";
|
|
||||||
import * as webWorkerOps from "./ops/web_worker.ts";
|
|
||||||
import { log, assert, immutableDefine } from "./util.ts";
|
|
||||||
import { ErrorEventImpl as ErrorEvent } from "./web/error_event.ts";
|
|
||||||
import { MessageEvent } from "./web/workers.ts";
|
|
||||||
import { TextEncoder } from "./web/text_encoding.ts";
|
|
||||||
import * as runtime from "./runtime.ts";
|
|
||||||
import { internalObject, internalSymbol } from "./internals.ts";
|
|
||||||
import { setSignals } from "./signals.ts";
|
|
||||||
|
|
||||||
// FIXME(bartlomieju): duplicated in `runtime_main.ts`
|
|
||||||
// TODO: factor out `Deno` global assignment to separate function
|
|
||||||
// Add internal object to Deno object.
|
|
||||||
// This is not exposed as part of the Deno types.
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(denoNs as any)[internalSymbol] = internalObject;
|
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
|
|
||||||
// TODO(bartlomieju): remove these funtions
|
|
||||||
// Stuff for workers
|
|
||||||
export const onmessage: (e: { data: any }) => void = (): void => {};
|
|
||||||
export const onerror: (e: { data: any }) => void = (): void => {};
|
|
||||||
|
|
||||||
export function postMessage(data: any): void {
|
|
||||||
const dataJson = JSON.stringify(data);
|
|
||||||
const dataIntArray = encoder.encode(dataJson);
|
|
||||||
webWorkerOps.postMessage(dataIntArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
let isClosing = false;
|
|
||||||
let hasBootstrapped = false;
|
|
||||||
|
|
||||||
export function close(): void {
|
|
||||||
if (isClosing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isClosing = true;
|
|
||||||
webWorkerOps.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function workerMessageRecvCallback(data: string): Promise<void> {
|
|
||||||
const msgEvent = new MessageEvent("message", {
|
|
||||||
cancelable: false,
|
|
||||||
data,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (globalThis["onmessage"]) {
|
|
||||||
const result = globalThis.onmessage!(msgEvent);
|
|
||||||
if (result && "then" in result) {
|
|
||||||
await result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
globalThis.dispatchEvent(msgEvent);
|
|
||||||
} catch (e) {
|
|
||||||
let handled = false;
|
|
||||||
|
|
||||||
const errorEvent = new ErrorEvent("error", {
|
|
||||||
cancelable: true,
|
|
||||||
message: e.message,
|
|
||||||
lineno: e.lineNumber ? e.lineNumber + 1 : undefined,
|
|
||||||
colno: e.columnNumber ? e.columnNumber + 1 : undefined,
|
|
||||||
filename: e.fileName,
|
|
||||||
error: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (globalThis["onerror"]) {
|
|
||||||
const ret = globalThis.onerror(
|
|
||||||
e.message,
|
|
||||||
e.fileName,
|
|
||||||
e.lineNumber,
|
|
||||||
e.columnNumber,
|
|
||||||
e,
|
|
||||||
);
|
|
||||||
handled = ret === true;
|
|
||||||
}
|
|
||||||
|
|
||||||
globalThis.dispatchEvent(errorEvent);
|
|
||||||
if (errorEvent.defaultPrevented) {
|
|
||||||
handled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!handled) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const workerRuntimeGlobalProperties = {
|
|
||||||
self: readOnly(globalThis),
|
|
||||||
onmessage: writable(onmessage),
|
|
||||||
onerror: writable(onerror),
|
|
||||||
// TODO: should be readonly?
|
|
||||||
close: nonEnumerable(close),
|
|
||||||
postMessage: writable(postMessage),
|
|
||||||
workerMessageRecvCallback: nonEnumerable(workerMessageRecvCallback),
|
|
||||||
};
|
|
||||||
|
|
||||||
export function bootstrapWorkerRuntime(
|
|
||||||
name: string,
|
|
||||||
useDenoNamespace: boolean,
|
|
||||||
internalName?: string,
|
|
||||||
): void {
|
|
||||||
if (hasBootstrapped) {
|
|
||||||
throw new Error("Worker runtime already bootstrapped");
|
|
||||||
}
|
|
||||||
// Remove bootstrapping methods from global scope
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(globalThis as any).bootstrap = undefined;
|
|
||||||
log("bootstrapWorkerRuntime");
|
|
||||||
hasBootstrapped = true;
|
|
||||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeMethods);
|
|
||||||
Object.defineProperties(globalThis, windowOrWorkerGlobalScopeProperties);
|
|
||||||
Object.defineProperties(globalThis, workerRuntimeGlobalProperties);
|
|
||||||
Object.defineProperties(globalThis, eventTargetProperties);
|
|
||||||
Object.defineProperties(globalThis, { name: readOnly(name) });
|
|
||||||
setEventTargetData(globalThis);
|
|
||||||
const { unstableFlag, pid, noColor, args } = runtime.start(
|
|
||||||
internalName ?? name,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (unstableFlag) {
|
|
||||||
Object.defineProperties(globalThis, unstableMethods);
|
|
||||||
Object.defineProperties(globalThis, unstableProperties);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useDenoNamespace) {
|
|
||||||
if (unstableFlag) {
|
|
||||||
Object.assign(denoNs, denoUnstableNs);
|
|
||||||
}
|
|
||||||
Object.defineProperties(denoNs, {
|
|
||||||
pid: readOnly(pid),
|
|
||||||
noColor: readOnly(noColor),
|
|
||||||
args: readOnly(Object.freeze(args)),
|
|
||||||
});
|
|
||||||
// Setup `Deno` global - we're actually overriding already
|
|
||||||
// existing global `Deno` with `Deno` namespace from "./deno.ts".
|
|
||||||
immutableDefine(globalThis, "Deno", denoNs);
|
|
||||||
Object.freeze(globalThis.Deno);
|
|
||||||
Object.freeze(globalThis.Deno.core);
|
|
||||||
Object.freeze(globalThis.Deno.core.sharedQueue);
|
|
||||||
setSignals();
|
|
||||||
} else {
|
|
||||||
delete globalThis.Deno;
|
|
||||||
assert(globalThis.Deno === undefined);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,173 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { bindSignal, pollSignal, unbindSignal } from "./ops/signal.ts";
|
|
||||||
import { build } from "./build.ts";
|
|
||||||
|
|
||||||
// From `kill -l`
|
|
||||||
enum LinuxSignal {
|
|
||||||
SIGHUP = 1,
|
|
||||||
SIGINT = 2,
|
|
||||||
SIGQUIT = 3,
|
|
||||||
SIGILL = 4,
|
|
||||||
SIGTRAP = 5,
|
|
||||||
SIGABRT = 6,
|
|
||||||
SIGBUS = 7,
|
|
||||||
SIGFPE = 8,
|
|
||||||
SIGKILL = 9,
|
|
||||||
SIGUSR1 = 10,
|
|
||||||
SIGSEGV = 11,
|
|
||||||
SIGUSR2 = 12,
|
|
||||||
SIGPIPE = 13,
|
|
||||||
SIGALRM = 14,
|
|
||||||
SIGTERM = 15,
|
|
||||||
SIGSTKFLT = 16,
|
|
||||||
SIGCHLD = 17,
|
|
||||||
SIGCONT = 18,
|
|
||||||
SIGSTOP = 19,
|
|
||||||
SIGTSTP = 20,
|
|
||||||
SIGTTIN = 21,
|
|
||||||
SIGTTOU = 22,
|
|
||||||
SIGURG = 23,
|
|
||||||
SIGXCPU = 24,
|
|
||||||
SIGXFSZ = 25,
|
|
||||||
SIGVTALRM = 26,
|
|
||||||
SIGPROF = 27,
|
|
||||||
SIGWINCH = 28,
|
|
||||||
SIGIO = 29,
|
|
||||||
SIGPWR = 30,
|
|
||||||
SIGSYS = 31,
|
|
||||||
}
|
|
||||||
|
|
||||||
// From `kill -l`
|
|
||||||
enum MacOSSignal {
|
|
||||||
SIGHUP = 1,
|
|
||||||
SIGINT = 2,
|
|
||||||
SIGQUIT = 3,
|
|
||||||
SIGILL = 4,
|
|
||||||
SIGTRAP = 5,
|
|
||||||
SIGABRT = 6,
|
|
||||||
SIGEMT = 7,
|
|
||||||
SIGFPE = 8,
|
|
||||||
SIGKILL = 9,
|
|
||||||
SIGBUS = 10,
|
|
||||||
SIGSEGV = 11,
|
|
||||||
SIGSYS = 12,
|
|
||||||
SIGPIPE = 13,
|
|
||||||
SIGALRM = 14,
|
|
||||||
SIGTERM = 15,
|
|
||||||
SIGURG = 16,
|
|
||||||
SIGSTOP = 17,
|
|
||||||
SIGTSTP = 18,
|
|
||||||
SIGCONT = 19,
|
|
||||||
SIGCHLD = 20,
|
|
||||||
SIGTTIN = 21,
|
|
||||||
SIGTTOU = 22,
|
|
||||||
SIGIO = 23,
|
|
||||||
SIGXCPU = 24,
|
|
||||||
SIGXFSZ = 25,
|
|
||||||
SIGVTALRM = 26,
|
|
||||||
SIGPROF = 27,
|
|
||||||
SIGWINCH = 28,
|
|
||||||
SIGINFO = 29,
|
|
||||||
SIGUSR1 = 30,
|
|
||||||
SIGUSR2 = 31,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Signal: { [key: string]: number } = {};
|
|
||||||
|
|
||||||
export function setSignals(): void {
|
|
||||||
if (build.os === "darwin") {
|
|
||||||
Object.assign(Signal, MacOSSignal);
|
|
||||||
} else {
|
|
||||||
Object.assign(Signal, LinuxSignal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function signal(signo: number): SignalStream {
|
|
||||||
if (build.os === "windows") {
|
|
||||||
throw new Error("not implemented!");
|
|
||||||
}
|
|
||||||
return new SignalStream(signo);
|
|
||||||
}
|
|
||||||
|
|
||||||
export const signals = {
|
|
||||||
alarm(): SignalStream {
|
|
||||||
return signal(Signal.SIGALRM);
|
|
||||||
},
|
|
||||||
child(): SignalStream {
|
|
||||||
return signal(Signal.SIGCHLD);
|
|
||||||
},
|
|
||||||
hungup(): SignalStream {
|
|
||||||
return signal(Signal.SIGHUP);
|
|
||||||
},
|
|
||||||
interrupt(): SignalStream {
|
|
||||||
return signal(Signal.SIGINT);
|
|
||||||
},
|
|
||||||
io(): SignalStream {
|
|
||||||
return signal(Signal.SIGIO);
|
|
||||||
},
|
|
||||||
pipe(): SignalStream {
|
|
||||||
return signal(Signal.SIGPIPE);
|
|
||||||
},
|
|
||||||
quit(): SignalStream {
|
|
||||||
return signal(Signal.SIGQUIT);
|
|
||||||
},
|
|
||||||
terminate(): SignalStream {
|
|
||||||
return signal(Signal.SIGTERM);
|
|
||||||
},
|
|
||||||
userDefined1(): SignalStream {
|
|
||||||
return signal(Signal.SIGUSR1);
|
|
||||||
},
|
|
||||||
userDefined2(): SignalStream {
|
|
||||||
return signal(Signal.SIGUSR2);
|
|
||||||
},
|
|
||||||
windowChange(): SignalStream {
|
|
||||||
return signal(Signal.SIGWINCH);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export class SignalStream
|
|
||||||
implements AsyncIterableIterator<void>, PromiseLike<void> {
|
|
||||||
#disposed = false;
|
|
||||||
#pollingPromise: Promise<boolean> = Promise.resolve(false);
|
|
||||||
readonly #rid: number;
|
|
||||||
|
|
||||||
constructor(signo: number) {
|
|
||||||
this.#rid = bindSignal(signo).rid;
|
|
||||||
this.#loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
#pollSignal = async (): Promise<boolean> => {
|
|
||||||
const res = await pollSignal(this.#rid);
|
|
||||||
return res.done;
|
|
||||||
};
|
|
||||||
|
|
||||||
#loop = async (): Promise<void> => {
|
|
||||||
do {
|
|
||||||
this.#pollingPromise = this.#pollSignal();
|
|
||||||
} while (!(await this.#pollingPromise) && !this.#disposed);
|
|
||||||
};
|
|
||||||
|
|
||||||
then<T, S>(
|
|
||||||
f: (v: void) => T | Promise<T>,
|
|
||||||
g?: (v: Error) => S | Promise<S>,
|
|
||||||
): Promise<T | S> {
|
|
||||||
return this.#pollingPromise.then(() => {}).then(f, g);
|
|
||||||
}
|
|
||||||
|
|
||||||
async next(): Promise<IteratorResult<void>> {
|
|
||||||
return { done: await this.#pollingPromise, value: undefined };
|
|
||||||
}
|
|
||||||
|
|
||||||
[Symbol.asyncIterator](): AsyncIterableIterator<void> {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
dispose(): void {
|
|
||||||
if (this.#disposed) {
|
|
||||||
throw new Error("The stream has already been disposed.");
|
|
||||||
}
|
|
||||||
this.#disposed = true;
|
|
||||||
unbindSignal(this.#rid);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,387 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { gray, green, italic, red, yellow } from "./colors.ts";
|
|
||||||
import { exit } from "./ops/os.ts";
|
|
||||||
import { Console, inspectArgs } from "./web/console.ts";
|
|
||||||
import { stdout } from "./files.ts";
|
|
||||||
import { exposeForTest } from "./internals.ts";
|
|
||||||
import { TextEncoder } from "./web/text_encoding.ts";
|
|
||||||
import { metrics } from "./ops/runtime.ts";
|
|
||||||
import { resources } from "./ops/resources.ts";
|
|
||||||
import { assert } from "./util.ts";
|
|
||||||
|
|
||||||
const disabledConsole = new Console((): void => {});
|
|
||||||
|
|
||||||
function delay(ms: number): Promise<void> {
|
|
||||||
return new Promise((resolve: () => void) => {
|
|
||||||
setTimeout(resolve, ms);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDuration(time = 0): string {
|
|
||||||
const timeStr = `(${time}ms)`;
|
|
||||||
return gray(italic(timeStr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap test function in additional assertion that makes sure
|
|
||||||
// the test case does not leak async "ops" - ie. number of async
|
|
||||||
// completed ops after the test is the same as number of dispatched
|
|
||||||
// ops. Note that "unref" ops are ignored since in nature that are
|
|
||||||
// optional.
|
|
||||||
function assertOps(fn: () => void | Promise<void>): () => void | Promise<void> {
|
|
||||||
return async function asyncOpSanitizer(): Promise<void> {
|
|
||||||
const pre = metrics();
|
|
||||||
await fn();
|
|
||||||
// Defer until next event loop turn - that way timeouts and intervals
|
|
||||||
// cleared can actually be removed from resource table, otherwise
|
|
||||||
// false positives may occur (https://github.com/denoland/deno/issues/4591)
|
|
||||||
await delay(0);
|
|
||||||
const post = metrics();
|
|
||||||
// We're checking diff because one might spawn HTTP server in the background
|
|
||||||
// that will be a pending async op before test starts.
|
|
||||||
const dispatchedDiff = post.opsDispatchedAsync - pre.opsDispatchedAsync;
|
|
||||||
const completedDiff = post.opsCompletedAsync - pre.opsCompletedAsync;
|
|
||||||
assert(
|
|
||||||
dispatchedDiff === completedDiff,
|
|
||||||
`Test case is leaking async ops.
|
|
||||||
Before:
|
|
||||||
- dispatched: ${pre.opsDispatchedAsync}
|
|
||||||
- completed: ${pre.opsCompletedAsync}
|
|
||||||
After:
|
|
||||||
- dispatched: ${post.opsDispatchedAsync}
|
|
||||||
- completed: ${post.opsCompletedAsync}
|
|
||||||
|
|
||||||
Make sure to await all promises returned from Deno APIs before
|
|
||||||
finishing test case.`,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrap test function in additional assertion that makes sure
|
|
||||||
// the test case does not "leak" resources - ie. resource table after
|
|
||||||
// the test has exactly the same contents as before the test.
|
|
||||||
function assertResources(
|
|
||||||
fn: () => void | Promise<void>,
|
|
||||||
): () => void | Promise<void> {
|
|
||||||
return async function resourceSanitizer(): Promise<void> {
|
|
||||||
const pre = resources();
|
|
||||||
await fn();
|
|
||||||
const post = resources();
|
|
||||||
|
|
||||||
const preStr = JSON.stringify(pre, null, 2);
|
|
||||||
const postStr = JSON.stringify(post, null, 2);
|
|
||||||
const msg = `Test case is leaking resources.
|
|
||||||
Before: ${preStr}
|
|
||||||
After: ${postStr}
|
|
||||||
|
|
||||||
Make sure to close all open resource handles returned from Deno APIs before
|
|
||||||
finishing test case.`;
|
|
||||||
assert(preStr === postStr, msg);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface TestDefinition {
|
|
||||||
fn: () => void | Promise<void>;
|
|
||||||
name: string;
|
|
||||||
ignore?: boolean;
|
|
||||||
only?: boolean;
|
|
||||||
sanitizeOps?: boolean;
|
|
||||||
sanitizeResources?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const TEST_REGISTRY: TestDefinition[] = [];
|
|
||||||
|
|
||||||
export function test(t: TestDefinition): void;
|
|
||||||
export function test(name: string, fn: () => void | Promise<void>): void;
|
|
||||||
// Main test function provided by Deno, as you can see it merely
|
|
||||||
// creates a new object with "name" and "fn" fields.
|
|
||||||
export function test(
|
|
||||||
t: string | TestDefinition,
|
|
||||||
fn?: () => void | Promise<void>,
|
|
||||||
): void {
|
|
||||||
let testDef: TestDefinition;
|
|
||||||
const defaults = {
|
|
||||||
ignore: false,
|
|
||||||
only: false,
|
|
||||||
sanitizeOps: true,
|
|
||||||
sanitizeResources: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof t === "string") {
|
|
||||||
if (!fn || typeof fn != "function") {
|
|
||||||
throw new TypeError("Missing test function");
|
|
||||||
}
|
|
||||||
if (!t) {
|
|
||||||
throw new TypeError("The test name can't be empty");
|
|
||||||
}
|
|
||||||
testDef = { fn: fn as () => void | Promise<void>, name: t, ...defaults };
|
|
||||||
} else {
|
|
||||||
if (!t.fn) {
|
|
||||||
throw new TypeError("Missing test function");
|
|
||||||
}
|
|
||||||
if (!t.name) {
|
|
||||||
throw new TypeError("The test name can't be empty");
|
|
||||||
}
|
|
||||||
testDef = { ...defaults, ...t };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testDef.sanitizeOps) {
|
|
||||||
testDef.fn = assertOps(testDef.fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (testDef.sanitizeResources) {
|
|
||||||
testDef.fn = assertResources(testDef.fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_REGISTRY.push(testDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface TestMessage {
|
|
||||||
start?: {
|
|
||||||
tests: TestDefinition[];
|
|
||||||
};
|
|
||||||
// Must be extensible, avoiding `testStart?: TestDefinition;`.
|
|
||||||
testStart?: {
|
|
||||||
[P in keyof TestDefinition]: TestDefinition[P];
|
|
||||||
};
|
|
||||||
testEnd?: {
|
|
||||||
name: string;
|
|
||||||
status: "passed" | "failed" | "ignored";
|
|
||||||
duration: number;
|
|
||||||
error?: Error;
|
|
||||||
};
|
|
||||||
end?: {
|
|
||||||
filtered: number;
|
|
||||||
ignored: number;
|
|
||||||
measured: number;
|
|
||||||
passed: number;
|
|
||||||
failed: number;
|
|
||||||
usedOnly: boolean;
|
|
||||||
duration: number;
|
|
||||||
results: Array<TestMessage["testEnd"] & {}>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
|
|
||||||
function log(msg: string, noNewLine = false): void {
|
|
||||||
if (!noNewLine) {
|
|
||||||
msg += "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using `stdout` here because it doesn't force new lines
|
|
||||||
// compared to `console.log`; `core.print` on the other hand
|
|
||||||
// is line-buffered and doesn't output message without newline
|
|
||||||
stdout.writeSync(encoder.encode(msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
function reportToConsole(message: TestMessage): void {
|
|
||||||
const redFailed = red("FAILED");
|
|
||||||
const greenOk = green("ok");
|
|
||||||
const yellowIgnored = yellow("ignored");
|
|
||||||
if (message.start != null) {
|
|
||||||
log(`running ${message.start.tests.length} tests`);
|
|
||||||
} else if (message.testStart != null) {
|
|
||||||
const { name } = message.testStart;
|
|
||||||
|
|
||||||
log(`test ${name} ... `, true);
|
|
||||||
return;
|
|
||||||
} else if (message.testEnd != null) {
|
|
||||||
switch (message.testEnd.status) {
|
|
||||||
case "passed":
|
|
||||||
log(`${greenOk} ${formatDuration(message.testEnd.duration)}`);
|
|
||||||
break;
|
|
||||||
case "failed":
|
|
||||||
log(`${redFailed} ${formatDuration(message.testEnd.duration)}`);
|
|
||||||
break;
|
|
||||||
case "ignored":
|
|
||||||
log(`${yellowIgnored} ${formatDuration(message.testEnd.duration)}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else if (message.end != null) {
|
|
||||||
const failures = message.end.results.filter((m) => m.error != null);
|
|
||||||
if (failures.length > 0) {
|
|
||||||
log(`\nfailures:\n`);
|
|
||||||
|
|
||||||
for (const { name, error } of failures) {
|
|
||||||
log(name);
|
|
||||||
log(inspectArgs([error!]));
|
|
||||||
log("");
|
|
||||||
}
|
|
||||||
|
|
||||||
log(`failures:\n`);
|
|
||||||
|
|
||||||
for (const { name } of failures) {
|
|
||||||
log(`\t${name}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log(
|
|
||||||
`\ntest result: ${message.end.failed ? redFailed : greenOk}. ` +
|
|
||||||
`${message.end.passed} passed; ${message.end.failed} failed; ` +
|
|
||||||
`${message.end.ignored} ignored; ${message.end.measured} measured; ` +
|
|
||||||
`${message.end.filtered} filtered out ` +
|
|
||||||
`${formatDuration(message.end.duration)}\n`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (message.end.usedOnly && message.end.failed == 0) {
|
|
||||||
log(`${redFailed} because the "only" option was used\n`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeForTest("reportToConsole", reportToConsole);
|
|
||||||
|
|
||||||
// TODO: already implements AsyncGenerator<RunTestsMessage>, but add as "implements to class"
|
|
||||||
// TODO: implements PromiseLike<RunTestsEndResult>
|
|
||||||
class TestRunner {
|
|
||||||
readonly testsToRun: TestDefinition[];
|
|
||||||
readonly stats = {
|
|
||||||
filtered: 0,
|
|
||||||
ignored: 0,
|
|
||||||
measured: 0,
|
|
||||||
passed: 0,
|
|
||||||
failed: 0,
|
|
||||||
};
|
|
||||||
readonly #usedOnly: boolean;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
tests: TestDefinition[],
|
|
||||||
public filterFn: (def: TestDefinition) => boolean,
|
|
||||||
public failFast: boolean,
|
|
||||||
) {
|
|
||||||
const onlyTests = tests.filter(({ only }) => only);
|
|
||||||
this.#usedOnly = onlyTests.length > 0;
|
|
||||||
const unfilteredTests = this.#usedOnly ? onlyTests : tests;
|
|
||||||
this.testsToRun = unfilteredTests.filter(filterFn);
|
|
||||||
this.stats.filtered = unfilteredTests.length - this.testsToRun.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
async *[Symbol.asyncIterator](): AsyncIterator<TestMessage> {
|
|
||||||
yield { start: { tests: this.testsToRun } };
|
|
||||||
|
|
||||||
const results: Array<TestMessage["testEnd"] & {}> = [];
|
|
||||||
const suiteStart = +new Date();
|
|
||||||
for (const test of this.testsToRun) {
|
|
||||||
const endMessage: Partial<TestMessage["testEnd"] & {}> = {
|
|
||||||
name: test.name,
|
|
||||||
duration: 0,
|
|
||||||
};
|
|
||||||
yield { testStart: { ...test } };
|
|
||||||
if (test.ignore) {
|
|
||||||
endMessage.status = "ignored";
|
|
||||||
this.stats.ignored++;
|
|
||||||
} else {
|
|
||||||
const start = +new Date();
|
|
||||||
try {
|
|
||||||
await test.fn();
|
|
||||||
endMessage.status = "passed";
|
|
||||||
this.stats.passed++;
|
|
||||||
} catch (err) {
|
|
||||||
endMessage.status = "failed";
|
|
||||||
endMessage.error = err;
|
|
||||||
this.stats.failed++;
|
|
||||||
}
|
|
||||||
endMessage.duration = +new Date() - start;
|
|
||||||
}
|
|
||||||
results.push(endMessage as TestMessage["testEnd"] & {});
|
|
||||||
yield { testEnd: endMessage as TestMessage["testEnd"] };
|
|
||||||
if (this.failFast && endMessage.error != null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const duration = +new Date() - suiteStart;
|
|
||||||
|
|
||||||
yield {
|
|
||||||
end: { ...this.stats, usedOnly: this.#usedOnly, duration, results },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createFilterFn(
|
|
||||||
filter: undefined | string | RegExp,
|
|
||||||
skip: undefined | string | RegExp,
|
|
||||||
): (def: TestDefinition) => boolean {
|
|
||||||
return (def: TestDefinition): boolean => {
|
|
||||||
let passes = true;
|
|
||||||
|
|
||||||
if (filter) {
|
|
||||||
if (filter instanceof RegExp) {
|
|
||||||
passes = passes && filter.test(def.name);
|
|
||||||
} else if (filter.startsWith("/") && filter.endsWith("/")) {
|
|
||||||
const filterAsRegex = new RegExp(filter.slice(1, filter.length - 1));
|
|
||||||
passes = passes && filterAsRegex.test(def.name);
|
|
||||||
} else {
|
|
||||||
passes = passes && def.name.includes(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (skip) {
|
|
||||||
if (skip instanceof RegExp) {
|
|
||||||
passes = passes && !skip.test(def.name);
|
|
||||||
} else {
|
|
||||||
passes = passes && !def.name.includes(skip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return passes;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeForTest("createFilterFn", createFilterFn);
|
|
||||||
|
|
||||||
interface RunTestsOptions {
|
|
||||||
exitOnFail?: boolean;
|
|
||||||
failFast?: boolean;
|
|
||||||
filter?: string | RegExp;
|
|
||||||
skip?: string | RegExp;
|
|
||||||
disableLog?: boolean;
|
|
||||||
reportToConsole?: boolean;
|
|
||||||
onMessage?: (message: TestMessage) => void | Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function runTests({
|
|
||||||
exitOnFail = true,
|
|
||||||
failFast = false,
|
|
||||||
filter = undefined,
|
|
||||||
skip = undefined,
|
|
||||||
disableLog = false,
|
|
||||||
reportToConsole: reportToConsole_ = true,
|
|
||||||
onMessage = undefined,
|
|
||||||
}: RunTestsOptions = {}): Promise<TestMessage["end"] & {}> {
|
|
||||||
const filterFn = createFilterFn(filter, skip);
|
|
||||||
const testRunner = new TestRunner(TEST_REGISTRY, filterFn, failFast);
|
|
||||||
|
|
||||||
const originalConsole = globalThis.console;
|
|
||||||
|
|
||||||
if (disableLog) {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
(globalThis as any).console = disabledConsole;
|
|
||||||
}
|
|
||||||
|
|
||||||
let endMsg: TestMessage["end"];
|
|
||||||
|
|
||||||
for await (const message of testRunner) {
|
|
||||||
if (onMessage != null) {
|
|
||||||
await onMessage(message);
|
|
||||||
}
|
|
||||||
if (reportToConsole_) {
|
|
||||||
reportToConsole(message);
|
|
||||||
}
|
|
||||||
if (message.end != null) {
|
|
||||||
endMsg = message.end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disableLog) {
|
|
||||||
globalThis.console = originalConsole;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((endMsg!.failed > 0 || endMsg?.usedOnly) && exitOnFail) {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return endMsg!;
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeForTest("runTests", runTests);
|
|
|
@ -1,77 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import * as tlsOps from "./ops/tls.ts";
|
|
||||||
import { Listener, Conn, ConnImpl, ListenerImpl } from "./net.ts";
|
|
||||||
|
|
||||||
// TODO(ry) There are many configuration options to add...
|
|
||||||
// https://docs.rs/rustls/0.16.0/rustls/struct.ClientConfig.html
|
|
||||||
interface ConnectTlsOptions {
|
|
||||||
transport?: "tcp";
|
|
||||||
port: number;
|
|
||||||
hostname?: string;
|
|
||||||
certFile?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function connectTls({
|
|
||||||
port,
|
|
||||||
hostname = "127.0.0.1",
|
|
||||||
transport = "tcp",
|
|
||||||
certFile = undefined,
|
|
||||||
}: ConnectTlsOptions): Promise<Conn> {
|
|
||||||
const res = await tlsOps.connectTls({
|
|
||||||
port,
|
|
||||||
hostname,
|
|
||||||
transport,
|
|
||||||
certFile,
|
|
||||||
});
|
|
||||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
|
||||||
}
|
|
||||||
|
|
||||||
class TLSListenerImpl extends ListenerImpl {
|
|
||||||
async accept(): Promise<Conn> {
|
|
||||||
const res = await tlsOps.acceptTLS(this.rid);
|
|
||||||
return new ConnImpl(res.rid, res.remoteAddr, res.localAddr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ListenTlsOptions {
|
|
||||||
port: number;
|
|
||||||
hostname?: string;
|
|
||||||
transport?: "tcp";
|
|
||||||
certFile: string;
|
|
||||||
keyFile: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function listenTls({
|
|
||||||
port,
|
|
||||||
certFile,
|
|
||||||
keyFile,
|
|
||||||
hostname = "0.0.0.0",
|
|
||||||
transport = "tcp",
|
|
||||||
}: ListenTlsOptions): Listener {
|
|
||||||
const res = tlsOps.listenTls({
|
|
||||||
port,
|
|
||||||
certFile,
|
|
||||||
keyFile,
|
|
||||||
hostname,
|
|
||||||
transport,
|
|
||||||
});
|
|
||||||
return new TLSListenerImpl(res.rid, res.localAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface StartTlsOptions {
|
|
||||||
hostname?: string;
|
|
||||||
certFile?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function startTls(
|
|
||||||
conn: Conn,
|
|
||||||
{ hostname = "127.0.0.1", certFile }: StartTlsOptions = {},
|
|
||||||
): Promise<Conn> {
|
|
||||||
const res = await tlsOps.startTls({
|
|
||||||
rid: conn.rid,
|
|
||||||
hostname,
|
|
||||||
certFile,
|
|
||||||
});
|
|
||||||
return new ConnImpl(res.rid, res.remoteAddr!, res.localAddr!);
|
|
||||||
}
|
|
37
cli/js/ts_global.d.ts
vendored
37
cli/js/ts_global.d.ts
vendored
|
@ -1,37 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This scopes the `ts` namespace globally, which is where it exists at runtime
|
|
||||||
// when building Deno, but the `typescript/lib/typescript.d.ts` is defined as a
|
|
||||||
// module.
|
|
||||||
|
|
||||||
// Warning! This is a magical import. We don't want to have multiple copies of
|
|
||||||
// typescript.d.ts around the repo, there's already one in
|
|
||||||
// deno_typescript/typescript/lib/typescript.d.ts. Ideally we could simply point
|
|
||||||
// to that in this import specifier, but "cargo package" is very strict and
|
|
||||||
// requires all files to be present in a crate's subtree.
|
|
||||||
// to get proper editor intellisense, you can substitute "$asset$" with
|
|
||||||
// "../../deno_typescript/typescript/lib" - remember to revert before committing
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
import * as ts_ from "$asset$/typescript.d.ts";
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
namespace ts {
|
|
||||||
export = ts_;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ts {
|
|
||||||
// this are marked @internal in TypeScript, but we need to access them,
|
|
||||||
// there is a risk these could change in future versions of TypeScript
|
|
||||||
export const libs: string[];
|
|
||||||
export const libMap: Map<string, string>;
|
|
||||||
export const performance: {
|
|
||||||
enable(): void;
|
|
||||||
disable(): void;
|
|
||||||
getDuration(value: string): number;
|
|
||||||
};
|
|
||||||
|
|
||||||
interface SourceFile {
|
|
||||||
version?: string;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
126
cli/js/util.ts
126
cli/js/util.ts
|
@ -1,126 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { build } from "./build.ts";
|
|
||||||
import { exposeForTest } from "./internals.ts";
|
|
||||||
|
|
||||||
let logDebug = false;
|
|
||||||
let logSource = "JS";
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function setLogDebug(debug: boolean, source?: string): void {
|
|
||||||
logDebug = debug;
|
|
||||||
if (source) {
|
|
||||||
logSource = source;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function log(...args: unknown[]): void {
|
|
||||||
if (logDebug) {
|
|
||||||
// if we destructure `console` off `globalThis` too early, we don't bind to
|
|
||||||
// the right console, therefore we don't log anything out.
|
|
||||||
globalThis.console.log(`DEBUG ${logSource} -`, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export class AssertionError extends Error {
|
|
||||||
constructor(msg?: string) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "AssertionError";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function assert(cond: unknown, msg = "Assertion failed."): asserts cond {
|
|
||||||
if (!cond) {
|
|
||||||
throw new AssertionError(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ResolveFunction<T> = (value?: T | PromiseLike<T>) => void;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export type RejectFunction = (reason?: any) => void;
|
|
||||||
|
|
||||||
export interface ResolvableMethods<T> {
|
|
||||||
resolve: ResolveFunction<T>;
|
|
||||||
reject: RejectFunction;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export type Resolvable<T> = Promise<T> & ResolvableMethods<T>;
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function createResolvable<T>(): Resolvable<T> {
|
|
||||||
let resolve: ResolveFunction<T>;
|
|
||||||
let reject: RejectFunction;
|
|
||||||
const promise = new Promise<T>((res, rej): void => {
|
|
||||||
resolve = res;
|
|
||||||
reject = rej;
|
|
||||||
}) as Resolvable<T>;
|
|
||||||
promise.resolve = resolve!;
|
|
||||||
promise.reject = reject!;
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function notImplemented(): never {
|
|
||||||
throw new Error("not implemented");
|
|
||||||
}
|
|
||||||
|
|
||||||
// @internal
|
|
||||||
export function immutableDefine(
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
o: any,
|
|
||||||
p: string | number | symbol,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
value: any,
|
|
||||||
): void {
|
|
||||||
Object.defineProperty(o, p, {
|
|
||||||
value,
|
|
||||||
configurable: false,
|
|
||||||
writable: false,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function pathFromURLWin32(url: URL): string {
|
|
||||||
const hostname = url.hostname;
|
|
||||||
const pathname = decodeURIComponent(url.pathname.replace(/\//g, "\\"));
|
|
||||||
|
|
||||||
if (hostname !== "") {
|
|
||||||
//TODO(actual-size) Node adds a punycode decoding step, we should consider adding this
|
|
||||||
return `\\\\${hostname}${pathname}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
const validPath = /^\\(?<driveLetter>[A-Za-z]):\\/;
|
|
||||||
const matches = validPath.exec(pathname);
|
|
||||||
|
|
||||||
if (!matches?.groups?.driveLetter) {
|
|
||||||
throw new TypeError("A URL with the file schema must be absolute.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// we don't want a leading slash on an absolute path in Windows
|
|
||||||
return pathname.slice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function pathFromURLPosix(url: URL): string {
|
|
||||||
if (url.hostname !== "") {
|
|
||||||
throw new TypeError(`Host must be empty.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeURIComponent(url.pathname);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function pathFromURL(pathOrUrl: string | URL): string {
|
|
||||||
if (pathOrUrl instanceof URL) {
|
|
||||||
if (pathOrUrl.protocol != "file:") {
|
|
||||||
throw new TypeError("Must be a file URL.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return build.os == "windows"
|
|
||||||
? pathFromURLWin32(pathOrUrl)
|
|
||||||
: pathFromURLPosix(pathOrUrl);
|
|
||||||
}
|
|
||||||
return pathOrUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeForTest("pathFromURL", pathFromURL);
|
|
|
@ -1,25 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
interface Version {
|
|
||||||
deno: string;
|
|
||||||
v8: string;
|
|
||||||
typescript: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const version: Version = {
|
|
||||||
deno: "",
|
|
||||||
v8: "",
|
|
||||||
typescript: "",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function setVersions(
|
|
||||||
denoVersion: string,
|
|
||||||
v8Version: string,
|
|
||||||
tsVersion: string,
|
|
||||||
): void {
|
|
||||||
version.deno = denoVersion;
|
|
||||||
version.v8 = v8Version;
|
|
||||||
version.typescript = tsVersion;
|
|
||||||
|
|
||||||
Object.freeze(version);
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
# Deno Web APIs
|
|
||||||
|
|
||||||
This directory facilities Web APIs that are available in Deno.
|
|
||||||
|
|
||||||
Please note, that some implementations might not be completely aligned with
|
|
||||||
specification.
|
|
||||||
|
|
||||||
Some Web APIs are using ops under the hood, eg. `console`, `performance`.
|
|
||||||
|
|
||||||
## Implemented Web APIs
|
|
||||||
|
|
||||||
- [Blob](https://developer.mozilla.org/en-US/docs/Web/API/Blob): for
|
|
||||||
representing opaque binary data
|
|
||||||
- [Console](https://developer.mozilla.org/en-US/docs/Web/API/Console): for
|
|
||||||
logging purposes
|
|
||||||
- [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent),
|
|
||||||
[EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget)
|
|
||||||
and
|
|
||||||
[EventListener](https://developer.mozilla.org/en-US/docs/Web/API/EventListener):
|
|
||||||
to work with DOM events
|
|
||||||
- **Implementation notes:** There is no DOM hierarchy in Deno, so there is no
|
|
||||||
tree for Events to bubble/capture through.
|
|
||||||
- [fetch](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch),
|
|
||||||
[Request](https://developer.mozilla.org/en-US/docs/Web/API/Request),
|
|
||||||
[Response](https://developer.mozilla.org/en-US/docs/Web/API/Response),
|
|
||||||
[Body](https://developer.mozilla.org/en-US/docs/Web/API/Body) and
|
|
||||||
[Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers): modern
|
|
||||||
Promise-based HTTP Request API
|
|
||||||
- [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData): access
|
|
||||||
to a `multipart/form-data` serialization
|
|
||||||
- [Performance](https://developer.mozilla.org/en-US/docs/Web/API/Performance):
|
|
||||||
retrieving current time with a high precision
|
|
||||||
- [setTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout),
|
|
||||||
[setInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setInterval),
|
|
||||||
[clearTimeout](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearTimeout):
|
|
||||||
scheduling callbacks in future and
|
|
||||||
[clearInterval](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/clearInterval)
|
|
||||||
- [Stream](https://developer.mozilla.org/en-US/docs/Web/API/Streams_API) for
|
|
||||||
creating, composing, and consuming streams of data
|
|
||||||
- [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) and
|
|
||||||
[URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams):
|
|
||||||
to construct and parse URLSs
|
|
||||||
- [Worker](https://developer.mozilla.org/en-US/docs/Web/API/Worker): executing
|
|
||||||
additional code in a separate thread
|
|
||||||
- **Implementation notes:** Blob URLs are not supported, object ownership
|
|
||||||
cannot be transferred, posted data is serialized to JSON instead of
|
|
||||||
[structured cloning](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm).
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { AbortSignalImpl, signalAbort } from "./abort_signal.ts";
|
|
||||||
|
|
||||||
export class AbortControllerImpl implements AbortController {
|
|
||||||
#signal = new AbortSignalImpl();
|
|
||||||
|
|
||||||
get signal(): AbortSignal {
|
|
||||||
return this.#signal;
|
|
||||||
}
|
|
||||||
|
|
||||||
abort(): void {
|
|
||||||
this.#signal[signalAbort]();
|
|
||||||
}
|
|
||||||
|
|
||||||
get [Symbol.toStringTag](): string {
|
|
||||||
return "AbortController";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(AbortControllerImpl, "name", {
|
|
||||||
value: "AbortController",
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
|
@ -1,58 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
import { EventImpl } from "./event.ts";
|
|
||||||
import { EventTargetImpl } from "./event_target.ts";
|
|
||||||
|
|
||||||
export const add = Symbol("add");
|
|
||||||
export const signalAbort = Symbol("signalAbort");
|
|
||||||
export const remove = Symbol("remove");
|
|
||||||
|
|
||||||
export class AbortSignalImpl extends EventTargetImpl implements AbortSignal {
|
|
||||||
#aborted?: boolean;
|
|
||||||
#abortAlgorithms = new Set<() => void>();
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
onabort: ((this: AbortSignal, ev: Event) => any) | null = null;
|
|
||||||
|
|
||||||
[add](algorithm: () => void): void {
|
|
||||||
this.#abortAlgorithms.add(algorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
[signalAbort](): void {
|
|
||||||
if (this.#aborted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.#aborted = true;
|
|
||||||
for (const algorithm of this.#abortAlgorithms) {
|
|
||||||
algorithm();
|
|
||||||
}
|
|
||||||
this.#abortAlgorithms.clear();
|
|
||||||
this.dispatchEvent(new EventImpl("abort"));
|
|
||||||
}
|
|
||||||
|
|
||||||
[remove](algorithm: () => void): void {
|
|
||||||
this.#abortAlgorithms.delete(algorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
this.addEventListener("abort", (evt: Event) => {
|
|
||||||
const { onabort } = this;
|
|
||||||
if (typeof onabort === "function") {
|
|
||||||
onabort.call(this, evt);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get aborted(): boolean {
|
|
||||||
return Boolean(this.#aborted);
|
|
||||||
}
|
|
||||||
|
|
||||||
get [Symbol.toStringTag](): string {
|
|
||||||
return "AbortSignal";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.defineProperty(AbortSignalImpl, "name", {
|
|
||||||
value: "AbortSignal",
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
|
@ -1,148 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Forked from https://github.com/beatgammit/base64-js
|
|
||||||
// Copyright (c) 2014 Jameson Little. MIT License.
|
|
||||||
|
|
||||||
const lookup: string[] = [];
|
|
||||||
const revLookup: number[] = [];
|
|
||||||
|
|
||||||
const code = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
||||||
for (let i = 0, len = code.length; i < len; ++i) {
|
|
||||||
lookup[i] = code[i];
|
|
||||||
revLookup[code.charCodeAt(i)] = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support decoding URL-safe base64 strings, as Node.js does.
|
|
||||||
// See: https://en.wikipedia.org/wiki/Base64#URL_applications
|
|
||||||
revLookup["-".charCodeAt(0)] = 62;
|
|
||||||
revLookup["_".charCodeAt(0)] = 63;
|
|
||||||
|
|
||||||
function getLens(b64: string): [number, number] {
|
|
||||||
const len = b64.length;
|
|
||||||
|
|
||||||
if (len % 4 > 0) {
|
|
||||||
throw new Error("Invalid string. Length must be a multiple of 4");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim off extra bytes after placeholder bytes are found
|
|
||||||
// See: https://github.com/beatgammit/base64-js/issues/42
|
|
||||||
let validLen = b64.indexOf("=");
|
|
||||||
if (validLen === -1) validLen = len;
|
|
||||||
|
|
||||||
const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4);
|
|
||||||
|
|
||||||
return [validLen, placeHoldersLen];
|
|
||||||
}
|
|
||||||
|
|
||||||
// base64 is 4/3 + up to two characters of the original data
|
|
||||||
export function byteLength(b64: string): number {
|
|
||||||
const lens = getLens(b64);
|
|
||||||
const validLen = lens[0];
|
|
||||||
const placeHoldersLen = lens[1];
|
|
||||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
function _byteLength(
|
|
||||||
b64: string,
|
|
||||||
validLen: number,
|
|
||||||
placeHoldersLen: number,
|
|
||||||
): number {
|
|
||||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function toByteArray(b64: string): Uint8Array {
|
|
||||||
let tmp;
|
|
||||||
const lens = getLens(b64);
|
|
||||||
const validLen = lens[0];
|
|
||||||
const placeHoldersLen = lens[1];
|
|
||||||
|
|
||||||
const arr = new Uint8Array(_byteLength(b64, validLen, placeHoldersLen));
|
|
||||||
|
|
||||||
let curByte = 0;
|
|
||||||
|
|
||||||
// if there are placeholders, only get up to the last complete 4 chars
|
|
||||||
const len = placeHoldersLen > 0 ? validLen - 4 : validLen;
|
|
||||||
|
|
||||||
let i;
|
|
||||||
for (i = 0; i < len; i += 4) {
|
|
||||||
tmp = (revLookup[b64.charCodeAt(i)] << 18) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] << 12) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 2)] << 6) |
|
|
||||||
revLookup[b64.charCodeAt(i + 3)];
|
|
||||||
arr[curByte++] = (tmp >> 16) & 0xff;
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xff;
|
|
||||||
arr[curByte++] = tmp & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 2) {
|
|
||||||
tmp = (revLookup[b64.charCodeAt(i)] << 2) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] >> 4);
|
|
||||||
arr[curByte++] = tmp & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 1) {
|
|
||||||
tmp = (revLookup[b64.charCodeAt(i)] << 10) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 1)] << 4) |
|
|
||||||
(revLookup[b64.charCodeAt(i + 2)] >> 2);
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xff;
|
|
||||||
arr[curByte++] = tmp & 0xff;
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tripletToBase64(num: number): string {
|
|
||||||
return (
|
|
||||||
lookup[(num >> 18) & 0x3f] +
|
|
||||||
lookup[(num >> 12) & 0x3f] +
|
|
||||||
lookup[(num >> 6) & 0x3f] +
|
|
||||||
lookup[num & 0x3f]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeChunk(uint8: Uint8Array, start: number, end: number): string {
|
|
||||||
let tmp;
|
|
||||||
const output = [];
|
|
||||||
for (let i = start; i < end; i += 3) {
|
|
||||||
tmp = ((uint8[i] << 16) & 0xff0000) +
|
|
||||||
((uint8[i + 1] << 8) & 0xff00) +
|
|
||||||
(uint8[i + 2] & 0xff);
|
|
||||||
output.push(tripletToBase64(tmp));
|
|
||||||
}
|
|
||||||
return output.join("");
|
|
||||||
}
|
|
||||||
|
|
||||||
export function fromByteArray(uint8: Uint8Array): string {
|
|
||||||
let tmp;
|
|
||||||
const len = uint8.length;
|
|
||||||
const extraBytes = len % 3; // if we have 1 byte left, pad 2 bytes
|
|
||||||
const parts = [];
|
|
||||||
const maxChunkLength = 16383; // must be multiple of 3
|
|
||||||
|
|
||||||
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
||||||
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
||||||
parts.push(
|
|
||||||
encodeChunk(
|
|
||||||
uint8,
|
|
||||||
i,
|
|
||||||
i + maxChunkLength > len2 ? len2 : i + maxChunkLength,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
||||||
if (extraBytes === 1) {
|
|
||||||
tmp = uint8[len - 1];
|
|
||||||
parts.push(lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3f] + "==");
|
|
||||||
} else if (extraBytes === 2) {
|
|
||||||
tmp = (uint8[len - 2] << 8) + uint8[len - 1];
|
|
||||||
parts.push(
|
|
||||||
lookup[tmp >> 10] +
|
|
||||||
lookup[(tmp >> 4) & 0x3f] +
|
|
||||||
lookup[(tmp << 2) & 0x3f] +
|
|
||||||
"=",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join("");
|
|
||||||
}
|
|
|
@ -1,227 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { TextDecoder, TextEncoder } from "./text_encoding.ts";
|
|
||||||
import { build } from "../build.ts";
|
|
||||||
import { ReadableStreamImpl } from "./streams/readable_stream.ts";
|
|
||||||
|
|
||||||
export const bytesSymbol = Symbol("bytes");
|
|
||||||
|
|
||||||
export function containsOnlyASCII(str: string): boolean {
|
|
||||||
if (typeof str !== "string") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return /^[\x00-\x7F]*$/.test(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertLineEndingsToNative(s: string): string {
|
|
||||||
const nativeLineEnd = build.os == "windows" ? "\r\n" : "\n";
|
|
||||||
|
|
||||||
let position = 0;
|
|
||||||
|
|
||||||
let collectionResult = collectSequenceNotCRLF(s, position);
|
|
||||||
|
|
||||||
let token = collectionResult.collected;
|
|
||||||
position = collectionResult.newPosition;
|
|
||||||
|
|
||||||
let result = token;
|
|
||||||
|
|
||||||
while (position < s.length) {
|
|
||||||
const c = s.charAt(position);
|
|
||||||
if (c == "\r") {
|
|
||||||
result += nativeLineEnd;
|
|
||||||
position++;
|
|
||||||
if (position < s.length && s.charAt(position) == "\n") {
|
|
||||||
position++;
|
|
||||||
}
|
|
||||||
} else if (c == "\n") {
|
|
||||||
position++;
|
|
||||||
result += nativeLineEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
collectionResult = collectSequenceNotCRLF(s, position);
|
|
||||||
|
|
||||||
token = collectionResult.collected;
|
|
||||||
position = collectionResult.newPosition;
|
|
||||||
|
|
||||||
result += token;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function collectSequenceNotCRLF(
|
|
||||||
s: string,
|
|
||||||
position: number,
|
|
||||||
): { collected: string; newPosition: number } {
|
|
||||||
const start = position;
|
|
||||||
for (
|
|
||||||
let c = s.charAt(position);
|
|
||||||
position < s.length && !(c == "\r" || c == "\n");
|
|
||||||
c = s.charAt(++position)
|
|
||||||
);
|
|
||||||
return { collected: s.slice(start, position), newPosition: position };
|
|
||||||
}
|
|
||||||
|
|
||||||
function toUint8Arrays(
|
|
||||||
blobParts: BlobPart[],
|
|
||||||
doNormalizeLineEndingsToNative: boolean,
|
|
||||||
): Uint8Array[] {
|
|
||||||
const ret: Uint8Array[] = [];
|
|
||||||
const enc = new TextEncoder();
|
|
||||||
for (const element of blobParts) {
|
|
||||||
if (typeof element === "string") {
|
|
||||||
let str = element;
|
|
||||||
if (doNormalizeLineEndingsToNative) {
|
|
||||||
str = convertLineEndingsToNative(element);
|
|
||||||
}
|
|
||||||
ret.push(enc.encode(str));
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
|
||||||
} else if (element instanceof DenoBlob) {
|
|
||||||
ret.push(element[bytesSymbol]);
|
|
||||||
} else if (element instanceof Uint8Array) {
|
|
||||||
ret.push(element);
|
|
||||||
} else if (element instanceof Uint16Array) {
|
|
||||||
const uint8 = new Uint8Array(element.buffer);
|
|
||||||
ret.push(uint8);
|
|
||||||
} else if (element instanceof Uint32Array) {
|
|
||||||
const uint8 = new Uint8Array(element.buffer);
|
|
||||||
ret.push(uint8);
|
|
||||||
} else if (ArrayBuffer.isView(element)) {
|
|
||||||
// Convert view to Uint8Array.
|
|
||||||
const uint8 = new Uint8Array(element.buffer);
|
|
||||||
ret.push(uint8);
|
|
||||||
} else if (element instanceof ArrayBuffer) {
|
|
||||||
// Create a new Uint8Array view for the given ArrayBuffer.
|
|
||||||
const uint8 = new Uint8Array(element);
|
|
||||||
ret.push(uint8);
|
|
||||||
} else {
|
|
||||||
ret.push(enc.encode(String(element)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
function processBlobParts(
|
|
||||||
blobParts: BlobPart[],
|
|
||||||
options: BlobPropertyBag,
|
|
||||||
): Uint8Array {
|
|
||||||
const normalizeLineEndingsToNative = options.ending === "native";
|
|
||||||
// ArrayBuffer.transfer is not yet implemented in V8, so we just have to
|
|
||||||
// pre compute size of the array buffer and do some sort of static allocation
|
|
||||||
// instead of dynamic allocation.
|
|
||||||
const uint8Arrays = toUint8Arrays(blobParts, normalizeLineEndingsToNative);
|
|
||||||
const byteLength = uint8Arrays
|
|
||||||
.map((u8): number => u8.byteLength)
|
|
||||||
.reduce((a, b): number => a + b, 0);
|
|
||||||
const ab = new ArrayBuffer(byteLength);
|
|
||||||
const bytes = new Uint8Array(ab);
|
|
||||||
let courser = 0;
|
|
||||||
for (const u8 of uint8Arrays) {
|
|
||||||
bytes.set(u8, courser);
|
|
||||||
courser += u8.byteLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStream(blobBytes: Uint8Array): ReadableStream<ArrayBufferView> {
|
|
||||||
// TODO: Align to spec https://fetch.spec.whatwg.org/#concept-construct-readablestream
|
|
||||||
return new ReadableStreamImpl({
|
|
||||||
type: "bytes",
|
|
||||||
start: (controller: ReadableByteStreamController): void => {
|
|
||||||
controller.enqueue(blobBytes);
|
|
||||||
controller.close();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function readBytes(
|
|
||||||
reader: ReadableStreamReader<ArrayBufferView>,
|
|
||||||
): Promise<ArrayBuffer> {
|
|
||||||
const chunks: Uint8Array[] = [];
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = await reader.read();
|
|
||||||
if (!done && value instanceof Uint8Array) {
|
|
||||||
chunks.push(value);
|
|
||||||
} else if (done) {
|
|
||||||
const size = chunks.reduce((p, i) => p + i.byteLength, 0);
|
|
||||||
const bytes = new Uint8Array(size);
|
|
||||||
let offs = 0;
|
|
||||||
for (const chunk of chunks) {
|
|
||||||
bytes.set(chunk, offs);
|
|
||||||
offs += chunk.byteLength;
|
|
||||||
}
|
|
||||||
return bytes;
|
|
||||||
} else {
|
|
||||||
throw new TypeError("Invalid reader result.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A WeakMap holding blob to byte array mapping.
|
|
||||||
// Ensures it does not impact garbage collection.
|
|
||||||
export const blobBytesWeakMap = new WeakMap<Blob, Uint8Array>();
|
|
||||||
|
|
||||||
class DenoBlob implements Blob {
|
|
||||||
[bytesSymbol]: Uint8Array;
|
|
||||||
readonly size: number = 0;
|
|
||||||
readonly type: string = "";
|
|
||||||
|
|
||||||
constructor(blobParts?: BlobPart[], options?: BlobPropertyBag) {
|
|
||||||
if (arguments.length === 0) {
|
|
||||||
this[bytesSymbol] = new Uint8Array();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { ending = "transparent", type = "" } = options ?? {};
|
|
||||||
// Normalize options.type.
|
|
||||||
let normalizedType = type;
|
|
||||||
if (!containsOnlyASCII(type)) {
|
|
||||||
normalizedType = "";
|
|
||||||
} else {
|
|
||||||
if (type.length) {
|
|
||||||
for (let i = 0; i < type.length; ++i) {
|
|
||||||
const char = type[i];
|
|
||||||
if (char < "\u0020" || char > "\u007E") {
|
|
||||||
normalizedType = "";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
normalizedType = type.toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const bytes = processBlobParts(blobParts!, { ending, type });
|
|
||||||
// Set Blob object's properties.
|
|
||||||
this[bytesSymbol] = bytes;
|
|
||||||
this.size = bytes.byteLength;
|
|
||||||
this.type = normalizedType;
|
|
||||||
}
|
|
||||||
|
|
||||||
slice(start?: number, end?: number, contentType?: string): DenoBlob {
|
|
||||||
return new DenoBlob([this[bytesSymbol].slice(start, end)], {
|
|
||||||
type: contentType || this.type,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
stream(): ReadableStream<ArrayBufferView> {
|
|
||||||
return getStream(this[bytesSymbol]);
|
|
||||||
}
|
|
||||||
|
|
||||||
async text(): Promise<string> {
|
|
||||||
const reader = getStream(this[bytesSymbol]).getReader();
|
|
||||||
const decoder = new TextDecoder();
|
|
||||||
return decoder.decode(await readBytes(reader));
|
|
||||||
}
|
|
||||||
|
|
||||||
arrayBuffer(): Promise<ArrayBuffer> {
|
|
||||||
return readBytes(getStream(this[bytesSymbol]).getReader());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we want the Base class name to be the name of the class.
|
|
||||||
Object.defineProperty(DenoBlob, "name", {
|
|
||||||
value: "Blob",
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
export { DenoBlob };
|
|
|
@ -1,213 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import * as blob from "./blob.ts";
|
|
||||||
import * as encoding from "./text_encoding.ts";
|
|
||||||
import type * as domTypes from "./dom_types.d.ts";
|
|
||||||
import { ReadableStreamImpl } from "./streams/readable_stream.ts";
|
|
||||||
import { isReadableStreamDisturbed } from "./streams/internals.ts";
|
|
||||||
import { Buffer } from "../buffer.ts";
|
|
||||||
|
|
||||||
import {
|
|
||||||
getHeaderValueParams,
|
|
||||||
hasHeaderValueOf,
|
|
||||||
isTypedArray,
|
|
||||||
} from "./util.ts";
|
|
||||||
import { MultipartParser } from "./fetch/multipart.ts";
|
|
||||||
|
|
||||||
// only namespace imports work for now, plucking out what we need
|
|
||||||
const { TextEncoder, TextDecoder } = encoding;
|
|
||||||
const DenoBlob = blob.DenoBlob;
|
|
||||||
|
|
||||||
interface BodyMeta {
|
|
||||||
contentType: string;
|
|
||||||
size?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateBodyType(owner: Body, bodySource: BodyInit | null): boolean {
|
|
||||||
if (isTypedArray(bodySource)) {
|
|
||||||
return true;
|
|
||||||
} else if (bodySource instanceof ArrayBuffer) {
|
|
||||||
return true;
|
|
||||||
} else if (typeof bodySource === "string") {
|
|
||||||
return true;
|
|
||||||
} else if (bodySource instanceof ReadableStreamImpl) {
|
|
||||||
return true;
|
|
||||||
} else if (bodySource instanceof FormData) {
|
|
||||||
return true;
|
|
||||||
} else if (bodySource instanceof URLSearchParams) {
|
|
||||||
return true;
|
|
||||||
} else if (!bodySource) {
|
|
||||||
return true; // null body is fine
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
`Bad ${owner.constructor.name} body type: ${bodySource.constructor.name}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function bufferFromStream(
|
|
||||||
stream: ReadableStreamReader,
|
|
||||||
size?: number,
|
|
||||||
): Promise<ArrayBuffer> {
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
const buffer = new Buffer();
|
|
||||||
|
|
||||||
if (size) {
|
|
||||||
// grow to avoid unnecessary allocations & copies
|
|
||||||
buffer.grow(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
const { done, value } = await stream.read();
|
|
||||||
|
|
||||||
if (done) break;
|
|
||||||
|
|
||||||
if (typeof value === "string") {
|
|
||||||
buffer.writeSync(encoder.encode(value));
|
|
||||||
} else if (value instanceof ArrayBuffer) {
|
|
||||||
buffer.writeSync(new Uint8Array(value));
|
|
||||||
} else if (value instanceof Uint8Array) {
|
|
||||||
buffer.writeSync(value);
|
|
||||||
} else if (!value) {
|
|
||||||
// noop for undefined
|
|
||||||
} else {
|
|
||||||
throw new Error("unhandled type on stream read");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer.bytes().buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BodyUsedError =
|
|
||||||
"Failed to execute 'clone' on 'Body': body is already used";
|
|
||||||
|
|
||||||
export class Body implements domTypes.Body {
|
|
||||||
protected _stream: ReadableStreamImpl<string | ArrayBuffer> | null;
|
|
||||||
#contentType: string;
|
|
||||||
#size: number | undefined;
|
|
||||||
constructor(protected _bodySource: BodyInit | null, meta: BodyMeta) {
|
|
||||||
validateBodyType(this, _bodySource);
|
|
||||||
this._bodySource = _bodySource;
|
|
||||||
this.#contentType = meta.contentType;
|
|
||||||
this.#size = meta.size;
|
|
||||||
this._stream = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get body(): ReadableStream | null {
|
|
||||||
if (this._stream) {
|
|
||||||
return this._stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._bodySource instanceof ReadableStreamImpl) {
|
|
||||||
this._stream = this._bodySource;
|
|
||||||
}
|
|
||||||
if (typeof this._bodySource === "string") {
|
|
||||||
const bodySource = this._bodySource;
|
|
||||||
this._stream = new ReadableStreamImpl<string | ArrayBuffer>({
|
|
||||||
start(controller: ReadableStreamDefaultController): void {
|
|
||||||
controller.enqueue(bodySource);
|
|
||||||
controller.close();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this._stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
get bodyUsed(): boolean {
|
|
||||||
if (this.body && isReadableStreamDisturbed(this.body)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async blob(): Promise<Blob> {
|
|
||||||
return new DenoBlob([await this.arrayBuffer()], {
|
|
||||||
type: this.#contentType,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// ref: https://fetch.spec.whatwg.org/#body-mixin
|
|
||||||
public async formData(): Promise<FormData> {
|
|
||||||
const formData = new FormData();
|
|
||||||
if (hasHeaderValueOf(this.#contentType, "multipart/form-data")) {
|
|
||||||
const params = getHeaderValueParams(this.#contentType);
|
|
||||||
|
|
||||||
// ref: https://tools.ietf.org/html/rfc2046#section-5.1
|
|
||||||
const boundary = params.get("boundary")!;
|
|
||||||
const body = new Uint8Array(await this.arrayBuffer());
|
|
||||||
const multipartParser = new MultipartParser(body, boundary);
|
|
||||||
|
|
||||||
return multipartParser.parse();
|
|
||||||
} else if (
|
|
||||||
hasHeaderValueOf(this.#contentType, "application/x-www-form-urlencoded")
|
|
||||||
) {
|
|
||||||
// From https://github.com/github/fetch/blob/master/fetch.js
|
|
||||||
// Copyright (c) 2014-2016 GitHub, Inc. MIT License
|
|
||||||
const body = await this.text();
|
|
||||||
try {
|
|
||||||
body
|
|
||||||
.trim()
|
|
||||||
.split("&")
|
|
||||||
.forEach((bytes): void => {
|
|
||||||
if (bytes) {
|
|
||||||
const split = bytes.split("=");
|
|
||||||
const name = split.shift()!.replace(/\+/g, " ");
|
|
||||||
const value = split.join("=").replace(/\+/g, " ");
|
|
||||||
formData.append(
|
|
||||||
decodeURIComponent(name),
|
|
||||||
decodeURIComponent(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
throw new TypeError("Invalid form urlencoded format");
|
|
||||||
}
|
|
||||||
return formData;
|
|
||||||
} else {
|
|
||||||
throw new TypeError("Invalid form data");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async text(): Promise<string> {
|
|
||||||
if (typeof this._bodySource === "string") {
|
|
||||||
return this._bodySource;
|
|
||||||
}
|
|
||||||
|
|
||||||
const ab = await this.arrayBuffer();
|
|
||||||
const decoder = new TextDecoder("utf-8");
|
|
||||||
return decoder.decode(ab);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
public async json(): Promise<any> {
|
|
||||||
const raw = await this.text();
|
|
||||||
return JSON.parse(raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
public arrayBuffer(): Promise<ArrayBuffer> {
|
|
||||||
if (isTypedArray(this._bodySource)) {
|
|
||||||
return Promise.resolve(this._bodySource.buffer as ArrayBuffer);
|
|
||||||
} else if (this._bodySource instanceof ArrayBuffer) {
|
|
||||||
return Promise.resolve(this._bodySource);
|
|
||||||
} else if (typeof this._bodySource === "string") {
|
|
||||||
const enc = new TextEncoder();
|
|
||||||
return Promise.resolve(
|
|
||||||
enc.encode(this._bodySource).buffer as ArrayBuffer,
|
|
||||||
);
|
|
||||||
} else if (this._bodySource instanceof ReadableStreamImpl) {
|
|
||||||
return bufferFromStream(this._bodySource.getReader(), this.#size);
|
|
||||||
} else if (
|
|
||||||
this._bodySource instanceof FormData ||
|
|
||||||
this._bodySource instanceof URLSearchParams
|
|
||||||
) {
|
|
||||||
const enc = new TextEncoder();
|
|
||||||
return Promise.resolve(
|
|
||||||
enc.encode(this._bodySource.toString()).buffer as ArrayBuffer,
|
|
||||||
);
|
|
||||||
} else if (!this._bodySource) {
|
|
||||||
return Promise.resolve(new ArrayBuffer(0));
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
`Body type not yet implemented: ${this._bodySource.constructor.name}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,130 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// Copyright Joyent, Inc. and other Node contributors. MIT license.
|
|
||||||
// Forked from Node's lib/internal/cli_table.js
|
|
||||||
|
|
||||||
import { hasOwnProperty } from "./util.ts";
|
|
||||||
import { stripColor } from "../colors.ts";
|
|
||||||
|
|
||||||
const tableChars = {
|
|
||||||
middleMiddle: "─",
|
|
||||||
rowMiddle: "┼",
|
|
||||||
topRight: "┐",
|
|
||||||
topLeft: "┌",
|
|
||||||
leftMiddle: "├",
|
|
||||||
topMiddle: "┬",
|
|
||||||
bottomRight: "┘",
|
|
||||||
bottomLeft: "└",
|
|
||||||
bottomMiddle: "┴",
|
|
||||||
rightMiddle: "┤",
|
|
||||||
left: "│ ",
|
|
||||||
right: " │",
|
|
||||||
middle: " │ ",
|
|
||||||
};
|
|
||||||
|
|
||||||
function isFullWidthCodePoint(code: number): boolean {
|
|
||||||
// Code points are partially derived from:
|
|
||||||
// http://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt
|
|
||||||
return (
|
|
||||||
code >= 0x1100 &&
|
|
||||||
(code <= 0x115f || // Hangul Jamo
|
|
||||||
code === 0x2329 || // LEFT-POINTING ANGLE BRACKET
|
|
||||||
code === 0x232a || // RIGHT-POINTING ANGLE BRACKET
|
|
||||||
// CJK Radicals Supplement .. Enclosed CJK Letters and Months
|
|
||||||
(code >= 0x2e80 && code <= 0x3247 && code !== 0x303f) ||
|
|
||||||
// Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A
|
|
||||||
(code >= 0x3250 && code <= 0x4dbf) ||
|
|
||||||
// CJK Unified Ideographs .. Yi Radicals
|
|
||||||
(code >= 0x4e00 && code <= 0xa4c6) ||
|
|
||||||
// Hangul Jamo Extended-A
|
|
||||||
(code >= 0xa960 && code <= 0xa97c) ||
|
|
||||||
// Hangul Syllables
|
|
||||||
(code >= 0xac00 && code <= 0xd7a3) ||
|
|
||||||
// CJK Compatibility Ideographs
|
|
||||||
(code >= 0xf900 && code <= 0xfaff) ||
|
|
||||||
// Vertical Forms
|
|
||||||
(code >= 0xfe10 && code <= 0xfe19) ||
|
|
||||||
// CJK Compatibility Forms .. Small Form Variants
|
|
||||||
(code >= 0xfe30 && code <= 0xfe6b) ||
|
|
||||||
// Halfwidth and Fullwidth Forms
|
|
||||||
(code >= 0xff01 && code <= 0xff60) ||
|
|
||||||
(code >= 0xffe0 && code <= 0xffe6) ||
|
|
||||||
// Kana Supplement
|
|
||||||
(code >= 0x1b000 && code <= 0x1b001) ||
|
|
||||||
// Enclosed Ideographic Supplement
|
|
||||||
(code >= 0x1f200 && code <= 0x1f251) ||
|
|
||||||
// Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff
|
|
||||||
// Emoticons 0x1f600 - 0x1f64f
|
|
||||||
(code >= 0x1f300 && code <= 0x1f64f) ||
|
|
||||||
// CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane
|
|
||||||
(code >= 0x20000 && code <= 0x3fffd))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStringWidth(str: string): number {
|
|
||||||
str = stripColor(str).normalize("NFC");
|
|
||||||
let width = 0;
|
|
||||||
|
|
||||||
for (const ch of str) {
|
|
||||||
width += isFullWidthCodePoint(ch.codePointAt(0)!) ? 2 : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderRow(row: string[], columnWidths: number[]): string {
|
|
||||||
let out = tableChars.left;
|
|
||||||
for (let i = 0; i < row.length; i++) {
|
|
||||||
const cell = row[i];
|
|
||||||
const len = getStringWidth(cell);
|
|
||||||
const needed = (columnWidths[i] - len) / 2;
|
|
||||||
// round(needed) + ceil(needed) will always add up to the amount
|
|
||||||
// of spaces we need while also left justifying the output.
|
|
||||||
out += `${" ".repeat(needed)}${cell}${" ".repeat(Math.ceil(needed))}`;
|
|
||||||
if (i !== row.length - 1) {
|
|
||||||
out += tableChars.middle;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
out += tableChars.right;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function cliTable(head: string[], columns: string[][]): string {
|
|
||||||
const rows: string[][] = [];
|
|
||||||
const columnWidths = head.map((h: string): number => getStringWidth(h));
|
|
||||||
const longestColumn = columns.reduce(
|
|
||||||
(n: number, a: string[]): number => Math.max(n, a.length),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
|
|
||||||
for (let i = 0; i < head.length; i++) {
|
|
||||||
const column = columns[i];
|
|
||||||
for (let j = 0; j < longestColumn; j++) {
|
|
||||||
if (rows[j] === undefined) {
|
|
||||||
rows[j] = [];
|
|
||||||
}
|
|
||||||
const value = (rows[j][i] = hasOwnProperty(column, j) ? column[j] : "");
|
|
||||||
const width = columnWidths[i] || 0;
|
|
||||||
const counted = getStringWidth(value);
|
|
||||||
columnWidths[i] = Math.max(width, counted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const divider = columnWidths.map((i: number): string =>
|
|
||||||
tableChars.middleMiddle.repeat(i + 2)
|
|
||||||
);
|
|
||||||
|
|
||||||
let result = `${tableChars.topLeft}${divider.join(tableChars.topMiddle)}` +
|
|
||||||
`${tableChars.topRight}\n${renderRow(head, columnWidths)}\n` +
|
|
||||||
`${tableChars.leftMiddle}${divider.join(tableChars.rowMiddle)}` +
|
|
||||||
`${tableChars.rightMiddle}\n`;
|
|
||||||
|
|
||||||
for (const row of rows) {
|
|
||||||
result += `${renderRow(row, columnWidths)}\n`;
|
|
||||||
}
|
|
||||||
|
|
||||||
result += `${tableChars.bottomLeft}${divider.join(tableChars.bottomMiddle)}` +
|
|
||||||
tableChars.bottomRight;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
|
@ -1,29 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { EventImpl as Event } from "./event.ts";
|
|
||||||
import { requiredArguments } from "./util.ts";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export class CustomEventImpl<T = any> extends Event implements CustomEvent {
|
|
||||||
readonly #detail: T;
|
|
||||||
|
|
||||||
constructor(type: string, eventInitDict: CustomEventInit<T> = {}) {
|
|
||||||
super(type, eventInitDict);
|
|
||||||
requiredArguments("CustomEvent", arguments.length, 1);
|
|
||||||
const { detail } = eventInitDict;
|
|
||||||
this.#detail = detail as T;
|
|
||||||
}
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
get detail(): T {
|
|
||||||
return this.#detail;
|
|
||||||
}
|
|
||||||
|
|
||||||
get [Symbol.toStringTag](): string {
|
|
||||||
return "CustomEvent";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Reflect.defineProperty(CustomEventImpl.prototype, "detail", {
|
|
||||||
enumerable: true,
|
|
||||||
});
|
|
|
@ -1,135 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This module is based on Bjoern Hoehrmann's DFA UTF-8 decoder.
|
|
||||||
// See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
|
|
||||||
//
|
|
||||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
// of this software and associated documentation files (the "Software"), to deal
|
|
||||||
// in the Software without restriction, including without limitation the rights
|
|
||||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
// copies of the Software, and to permit persons to whom the Software is
|
|
||||||
// furnished to do so, subject to the following conditions:
|
|
||||||
//
|
|
||||||
// The above copyright notice and this permission notice shall be included in
|
|
||||||
// all copies or substantial portions of the Software.
|
|
||||||
//
|
|
||||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
// SOFTWARE.
|
|
||||||
|
|
||||||
// `.apply` can actually take a typed array, though the type system doesn't
|
|
||||||
// really support it, so we have to "hack" it a bit to get past some of the
|
|
||||||
// strict type checks.
|
|
||||||
declare global {
|
|
||||||
interface CallableFunction extends Function {
|
|
||||||
apply<T, R>(
|
|
||||||
this: (this: T, ...args: number[]) => R,
|
|
||||||
thisArg: T,
|
|
||||||
args: Uint16Array,
|
|
||||||
): R;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function decodeUtf8(
|
|
||||||
input: Uint8Array,
|
|
||||||
fatal: boolean,
|
|
||||||
ignoreBOM: boolean,
|
|
||||||
): string {
|
|
||||||
let outString = "";
|
|
||||||
|
|
||||||
// Prepare a buffer so that we don't have to do a lot of string concats, which
|
|
||||||
// are very slow.
|
|
||||||
const outBufferLength: number = Math.min(1024, input.length);
|
|
||||||
const outBuffer = new Uint16Array(outBufferLength);
|
|
||||||
let outIndex = 0;
|
|
||||||
|
|
||||||
let state = 0;
|
|
||||||
let codepoint = 0;
|
|
||||||
let type: number;
|
|
||||||
|
|
||||||
let i =
|
|
||||||
ignoreBOM && input[0] === 0xef && input[1] === 0xbb && input[2] === 0xbf
|
|
||||||
? 3
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
for (; i < input.length; ++i) {
|
|
||||||
// Encoding error handling
|
|
||||||
if (state === 12 || (state !== 0 && (input[i] & 0xc0) !== 0x80)) {
|
|
||||||
if (fatal) {
|
|
||||||
throw new TypeError(
|
|
||||||
`Decoder error. Invalid byte in sequence at position ${i} in data.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
outBuffer[outIndex++] = 0xfffd; // Replacement character
|
|
||||||
if (outIndex === outBufferLength) {
|
|
||||||
outString += String.fromCharCode.apply(null, outBuffer);
|
|
||||||
outIndex = 0;
|
|
||||||
}
|
|
||||||
state = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// deno-fmt-ignore
|
|
||||||
type = [
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
|
||||||
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
|
|
||||||
7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
|
|
||||||
8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
|
||||||
10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8
|
|
||||||
][input[i]];
|
|
||||||
codepoint = state !== 0
|
|
||||||
? (input[i] & 0x3f) | (codepoint << 6)
|
|
||||||
: (0xff >> type) & input[i];
|
|
||||||
// deno-fmt-ignore
|
|
||||||
state = [
|
|
||||||
0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
|
|
||||||
12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
|
|
||||||
12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
|
|
||||||
12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
|
|
||||||
12,36,12,12,12,12,12,12,12,12,12,12
|
|
||||||
][state + type];
|
|
||||||
|
|
||||||
if (state !== 0) continue;
|
|
||||||
|
|
||||||
// Add codepoint to buffer (as charcodes for utf-16), and flush buffer to
|
|
||||||
// string if needed.
|
|
||||||
if (codepoint > 0xffff) {
|
|
||||||
outBuffer[outIndex++] = 0xd7c0 + (codepoint >> 10);
|
|
||||||
if (outIndex === outBufferLength) {
|
|
||||||
outString += String.fromCharCode.apply(null, outBuffer);
|
|
||||||
outIndex = 0;
|
|
||||||
}
|
|
||||||
outBuffer[outIndex++] = 0xdc00 | (codepoint & 0x3ff);
|
|
||||||
if (outIndex === outBufferLength) {
|
|
||||||
outString += String.fromCharCode.apply(null, outBuffer);
|
|
||||||
outIndex = 0;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
outBuffer[outIndex++] = codepoint;
|
|
||||||
if (outIndex === outBufferLength) {
|
|
||||||
outString += String.fromCharCode.apply(null, outBuffer);
|
|
||||||
outIndex = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a replacement character if we ended in the middle of a sequence or
|
|
||||||
// encountered an invalid code at the end.
|
|
||||||
if (state !== 0) {
|
|
||||||
if (fatal) throw new TypeError(`Decoder error. Unexpected end of data.`);
|
|
||||||
outBuffer[outIndex++] = 0xfffd; // Replacement character
|
|
||||||
}
|
|
||||||
|
|
||||||
// Final flush of buffer
|
|
||||||
outString += String.fromCharCode.apply(null, outBuffer.subarray(0, outIndex));
|
|
||||||
|
|
||||||
return outString;
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import * as blob from "./blob.ts";
|
|
||||||
|
|
||||||
export class DomFileImpl extends blob.DenoBlob implements File {
|
|
||||||
lastModified: number;
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
fileBits: BlobPart[],
|
|
||||||
fileName: string,
|
|
||||||
options?: FilePropertyBag,
|
|
||||||
) {
|
|
||||||
const { lastModified = Date.now(), ...blobPropertyBag } = options ?? {};
|
|
||||||
super(fileBits, blobPropertyBag);
|
|
||||||
|
|
||||||
// 4.1.2.1 Replace any "/" character (U+002F SOLIDUS)
|
|
||||||
// with a ":" (U + 003A COLON)
|
|
||||||
this.name = String(fileName).replace(/\u002F/g, "\u003A");
|
|
||||||
// 4.1.3.3 If lastModified is not provided, set lastModified to the current
|
|
||||||
// date and time represented in number of milliseconds since the Unix Epoch.
|
|
||||||
this.lastModified = lastModified;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { requiredArguments } from "./util.ts";
|
|
||||||
import { exposeForTest } from "../internals.ts";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export type Constructor<T = {}> = new (...args: any[]) => T;
|
|
||||||
|
|
||||||
export interface DomIterable<K, V> {
|
|
||||||
keys(): IterableIterator<K>;
|
|
||||||
values(): IterableIterator<V>;
|
|
||||||
entries(): IterableIterator<[K, V]>;
|
|
||||||
[Symbol.iterator](): IterableIterator<[K, V]>;
|
|
||||||
forEach(
|
|
||||||
callback: (value: V, key: K, parent: this) => void,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
thisArg?: any,
|
|
||||||
): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function DomIterableMixin<K, V, TBase extends Constructor>(
|
|
||||||
Base: TBase,
|
|
||||||
dataSymbol: symbol,
|
|
||||||
): TBase & Constructor<DomIterable<K, V>> {
|
|
||||||
// we have to cast `this` as `any` because there is no way to describe the
|
|
||||||
// Base class in a way where the Symbol `dataSymbol` is defined. So the
|
|
||||||
// runtime code works, but we do lose a little bit of type safety.
|
|
||||||
|
|
||||||
// Additionally, we have to not use .keys() nor .values() since the internal
|
|
||||||
// slot differs in type - some have a Map, which yields [K, V] in
|
|
||||||
// Symbol.iterator, and some have an Array, which yields V, in this case
|
|
||||||
// [K, V] too as they are arrays of tuples.
|
|
||||||
|
|
||||||
const DomIterable = class extends Base {
|
|
||||||
*entries(): IterableIterator<[K, V]> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
for (const entry of (this as any)[dataSymbol]) {
|
|
||||||
yield entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*keys(): IterableIterator<K> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
for (const [key] of (this as any)[dataSymbol]) {
|
|
||||||
yield key;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*values(): IterableIterator<V> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
for (const [, value] of (this as any)[dataSymbol]) {
|
|
||||||
yield value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
forEach(
|
|
||||||
callbackfn: (value: V, key: K, parent: this) => void,
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
thisArg?: any,
|
|
||||||
): void {
|
|
||||||
requiredArguments(
|
|
||||||
`${this.constructor.name}.forEach`,
|
|
||||||
arguments.length,
|
|
||||||
1,
|
|
||||||
);
|
|
||||||
callbackfn = callbackfn.bind(
|
|
||||||
thisArg == null ? globalThis : Object(thisArg),
|
|
||||||
);
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
for (const [key, value] of (this as any)[dataSymbol]) {
|
|
||||||
callbackfn(value, key, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
*[Symbol.iterator](): IterableIterator<[K, V]> {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
for (const entry of (this as any)[dataSymbol]) {
|
|
||||||
yield entry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// we want the Base class name to be the name of the class.
|
|
||||||
Object.defineProperty(DomIterable, "name", {
|
|
||||||
value: Base.name,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return DomIterable;
|
|
||||||
}
|
|
||||||
|
|
||||||
exposeForTest("DomIterableMixin", DomIterableMixin);
|
|
318
cli/js/web/dom_types.d.ts
vendored
318
cli/js/web/dom_types.d.ts
vendored
|
@ -1,318 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
/*! ****************************************************************************
|
|
||||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
|
|
||||||
this file except in compliance with the License. You may obtain a copy of the
|
|
||||||
License at http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
|
||||||
ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
|
|
||||||
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
|
|
||||||
MERCHANTABLITY OR NON-INFRINGEMENT.
|
|
||||||
|
|
||||||
See the Apache Version 2.0 License for specific language governing permissions
|
|
||||||
and limitations under the License.
|
|
||||||
*******************************************************************************/
|
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
|
|
||||||
export type RequestInfo = Request | string;
|
|
||||||
|
|
||||||
export interface ProgressEventInit extends EventInit {
|
|
||||||
lengthComputable?: boolean;
|
|
||||||
loaded?: number;
|
|
||||||
total?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UIEventInit extends EventInit {
|
|
||||||
detail?: number;
|
|
||||||
// adjust Window -> Node
|
|
||||||
view?: Node | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UIEvent extends Event {
|
|
||||||
constructor(type: string, eventInitDict?: UIEventInit);
|
|
||||||
readonly detail: number;
|
|
||||||
// adjust Window -> Node
|
|
||||||
readonly view: Node | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FocusEventInit extends UIEventInit {
|
|
||||||
relatedTarget?: EventTarget | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class FocusEvent extends UIEvent {
|
|
||||||
constructor(type: string, eventInitDict?: FocusEventInit);
|
|
||||||
readonly relatedTarget: EventTarget | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface EventModifierInit extends UIEventInit {
|
|
||||||
altKey?: boolean;
|
|
||||||
ctrlKey?: boolean;
|
|
||||||
metaKey?: boolean;
|
|
||||||
modifierAltGraph?: boolean;
|
|
||||||
modifierCapsLock?: boolean;
|
|
||||||
modifierFn?: boolean;
|
|
||||||
modifierFnLock?: boolean;
|
|
||||||
modifierHyper?: boolean;
|
|
||||||
modifierNumLock?: boolean;
|
|
||||||
modifierScrollLock?: boolean;
|
|
||||||
modifierSuper?: boolean;
|
|
||||||
modifierSymbol?: boolean;
|
|
||||||
modifierSymbolLock?: boolean;
|
|
||||||
shiftKey?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MouseEventInit extends EventModifierInit {
|
|
||||||
button?: number;
|
|
||||||
buttons?: number;
|
|
||||||
clientX?: number;
|
|
||||||
clientY?: number;
|
|
||||||
movementX?: number;
|
|
||||||
movementY?: number;
|
|
||||||
relatedTarget?: EventTarget | null;
|
|
||||||
screenX?: number;
|
|
||||||
screenY?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MouseEvent extends UIEvent {
|
|
||||||
constructor(type: string, eventInitDict?: MouseEventInit);
|
|
||||||
readonly altKey: boolean;
|
|
||||||
readonly button: number;
|
|
||||||
readonly buttons: number;
|
|
||||||
readonly clientX: number;
|
|
||||||
readonly clientY: number;
|
|
||||||
readonly ctrlKey: boolean;
|
|
||||||
readonly metaKey: boolean;
|
|
||||||
readonly movementX: number;
|
|
||||||
readonly movementY: number;
|
|
||||||
readonly offsetX: number;
|
|
||||||
readonly offsetY: number;
|
|
||||||
readonly pageX: number;
|
|
||||||
readonly pageY: number;
|
|
||||||
readonly relatedTarget: EventTarget | null;
|
|
||||||
readonly screenX: number;
|
|
||||||
readonly screenY: number;
|
|
||||||
readonly shiftKey: boolean;
|
|
||||||
readonly x: number;
|
|
||||||
readonly y: number;
|
|
||||||
getModifierState(keyArg: string): boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface GetRootNodeOptions {
|
|
||||||
composed?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Node extends EventTarget {
|
|
||||||
readonly baseURI: string;
|
|
||||||
readonly childNodes: NodeListOf<ChildNode>;
|
|
||||||
readonly firstChild: ChildNode | null;
|
|
||||||
readonly isConnected: boolean;
|
|
||||||
readonly lastChild: ChildNode | null;
|
|
||||||
readonly nextSibling: ChildNode | null;
|
|
||||||
readonly nodeName: string;
|
|
||||||
readonly nodeType: number;
|
|
||||||
nodeValue: string | null;
|
|
||||||
// adjusted: Document -> Node
|
|
||||||
readonly ownerDocument: Node | null;
|
|
||||||
// adjusted: HTMLElement -> Node
|
|
||||||
readonly parentElement: Node | null;
|
|
||||||
readonly parentNode: (Node & ParentNode) | null;
|
|
||||||
readonly previousSibling: ChildNode | null;
|
|
||||||
textContent: string | null;
|
|
||||||
appendChild<T extends Node>(newChild: T): T;
|
|
||||||
cloneNode(deep?: boolean): Node;
|
|
||||||
compareDocumentPosition(other: Node): number;
|
|
||||||
contains(other: Node | null): boolean;
|
|
||||||
getRootNode(options?: GetRootNodeOptions): Node;
|
|
||||||
hasChildNodes(): boolean;
|
|
||||||
insertBefore<T extends Node>(newChild: T, refChild: Node | null): T;
|
|
||||||
isDefaultNamespace(namespace: string | null): boolean;
|
|
||||||
isEqualNode(otherNode: Node | null): boolean;
|
|
||||||
isSameNode(otherNode: Node | null): boolean;
|
|
||||||
lookupNamespaceURI(prefix: string | null): string | null;
|
|
||||||
lookupPrefix(namespace: string | null): string | null;
|
|
||||||
normalize(): void;
|
|
||||||
removeChild<T extends Node>(oldChild: T): T;
|
|
||||||
replaceChild<T extends Node>(newChild: Node, oldChild: T): T;
|
|
||||||
readonly ATTRIBUTE_NODE: number;
|
|
||||||
readonly CDATA_SECTION_NODE: number;
|
|
||||||
readonly COMMENT_NODE: number;
|
|
||||||
readonly DOCUMENT_FRAGMENT_NODE: number;
|
|
||||||
readonly DOCUMENT_NODE: number;
|
|
||||||
readonly DOCUMENT_POSITION_CONTAINED_BY: number;
|
|
||||||
readonly DOCUMENT_POSITION_CONTAINS: number;
|
|
||||||
readonly DOCUMENT_POSITION_DISCONNECTED: number;
|
|
||||||
readonly DOCUMENT_POSITION_FOLLOWING: number;
|
|
||||||
readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number;
|
|
||||||
readonly DOCUMENT_POSITION_PRECEDING: number;
|
|
||||||
readonly DOCUMENT_TYPE_NODE: number;
|
|
||||||
readonly ELEMENT_NODE: number;
|
|
||||||
readonly ENTITY_NODE: number;
|
|
||||||
readonly ENTITY_REFERENCE_NODE: number;
|
|
||||||
readonly NOTATION_NODE: number;
|
|
||||||
readonly PROCESSING_INSTRUCTION_NODE: number;
|
|
||||||
readonly TEXT_NODE: number;
|
|
||||||
static readonly ATTRIBUTE_NODE: number;
|
|
||||||
static readonly CDATA_SECTION_NODE: number;
|
|
||||||
static readonly COMMENT_NODE: number;
|
|
||||||
static readonly DOCUMENT_FRAGMENT_NODE: number;
|
|
||||||
static readonly DOCUMENT_NODE: number;
|
|
||||||
static readonly DOCUMENT_POSITION_CONTAINED_BY: number;
|
|
||||||
static readonly DOCUMENT_POSITION_CONTAINS: number;
|
|
||||||
static readonly DOCUMENT_POSITION_DISCONNECTED: number;
|
|
||||||
static readonly DOCUMENT_POSITION_FOLLOWING: number;
|
|
||||||
static readonly DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: number;
|
|
||||||
static readonly DOCUMENT_POSITION_PRECEDING: number;
|
|
||||||
static readonly DOCUMENT_TYPE_NODE: number;
|
|
||||||
static readonly ELEMENT_NODE: number;
|
|
||||||
static readonly ENTITY_NODE: number;
|
|
||||||
static readonly ENTITY_REFERENCE_NODE: number;
|
|
||||||
static readonly NOTATION_NODE: number;
|
|
||||||
static readonly PROCESSING_INSTRUCTION_NODE: number;
|
|
||||||
static readonly TEXT_NODE: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Slotable {
|
|
||||||
// adjusted: HTMLSlotElement -> Node
|
|
||||||
readonly assignedSlot: Node | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ChildNode extends Node {
|
|
||||||
after(...nodes: Array<Node | string>): void;
|
|
||||||
before(...nodes: Array<Node | string>): void;
|
|
||||||
remove(): void;
|
|
||||||
replaceWith(...nodes: Array<Node | string>): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ParentNode {
|
|
||||||
readonly childElementCount: number;
|
|
||||||
// not currently supported
|
|
||||||
// readonly children: HTMLCollection;
|
|
||||||
// adjusted: Element -> Node
|
|
||||||
readonly firstElementChild: Node | null;
|
|
||||||
// adjusted: Element -> Node
|
|
||||||
readonly lastElementChild: Node | null;
|
|
||||||
append(...nodes: Array<Node | string>): void;
|
|
||||||
prepend(...nodes: Array<Node | string>): void;
|
|
||||||
// not currently supported
|
|
||||||
// querySelector<K extends keyof HTMLElementTagNameMap>(
|
|
||||||
// selectors: K,
|
|
||||||
// ): HTMLElementTagNameMap[K] | null;
|
|
||||||
// querySelector<K extends keyof SVGElementTagNameMap>(
|
|
||||||
// selectors: K,
|
|
||||||
// ): SVGElementTagNameMap[K] | null;
|
|
||||||
// querySelector<E extends Element = Element>(selectors: string): E | null;
|
|
||||||
// querySelectorAll<K extends keyof HTMLElementTagNameMap>(
|
|
||||||
// selectors: K,
|
|
||||||
// ): NodeListOf<HTMLElementTagNameMap[K]>;
|
|
||||||
// querySelectorAll<K extends keyof SVGElementTagNameMap>(
|
|
||||||
// selectors: K,
|
|
||||||
// ): NodeListOf<SVGElementTagNameMap[K]>;
|
|
||||||
// querySelectorAll<E extends Element = Element>(
|
|
||||||
// selectors: string,
|
|
||||||
// ): NodeListOf<E>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NodeList {
|
|
||||||
readonly length: number;
|
|
||||||
item(index: number): Node | null;
|
|
||||||
forEach(
|
|
||||||
callbackfn: (value: Node, key: number, parent: NodeList) => void,
|
|
||||||
thisArg?: any,
|
|
||||||
): void;
|
|
||||||
[index: number]: Node;
|
|
||||||
[Symbol.iterator](): IterableIterator<Node>;
|
|
||||||
entries(): IterableIterator<[number, Node]>;
|
|
||||||
keys(): IterableIterator<number>;
|
|
||||||
values(): IterableIterator<Node>;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NodeListOf<TNode extends Node> extends NodeList {
|
|
||||||
length: number;
|
|
||||||
item(index: number): TNode;
|
|
||||||
forEach(
|
|
||||||
callbackfn: (value: TNode, key: number, parent: NodeListOf<TNode>) => void,
|
|
||||||
thisArg?: any,
|
|
||||||
): void;
|
|
||||||
[index: number]: TNode;
|
|
||||||
[Symbol.iterator](): IterableIterator<TNode>;
|
|
||||||
entries(): IterableIterator<[number, TNode]>;
|
|
||||||
keys(): IterableIterator<number>;
|
|
||||||
values(): IterableIterator<TNode>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Body {
|
|
||||||
readonly body: ReadableStream<Uint8Array> | null;
|
|
||||||
readonly bodyUsed: boolean;
|
|
||||||
arrayBuffer(): Promise<ArrayBuffer>;
|
|
||||||
blob(): Promise<Blob>;
|
|
||||||
formData(): Promise<FormData>;
|
|
||||||
json(): Promise<any>;
|
|
||||||
text(): Promise<string>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RequestInit {
|
|
||||||
body?: BodyInit | null;
|
|
||||||
cache?: RequestCache;
|
|
||||||
credentials?: RequestCredentials;
|
|
||||||
headers?: HeadersInit;
|
|
||||||
integrity?: string;
|
|
||||||
keepalive?: boolean;
|
|
||||||
method?: string;
|
|
||||||
mode?: RequestMode;
|
|
||||||
redirect?: RequestRedirect;
|
|
||||||
referrer?: string;
|
|
||||||
referrerPolicy?: ReferrerPolicy;
|
|
||||||
signal?: AbortSignal | null;
|
|
||||||
window?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResponseInit {
|
|
||||||
headers?: HeadersInit;
|
|
||||||
status?: number;
|
|
||||||
statusText?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Request extends Body {
|
|
||||||
readonly cache?: RequestCache;
|
|
||||||
readonly credentials?: RequestCredentials;
|
|
||||||
readonly destination?: RequestDestination;
|
|
||||||
readonly headers: Headers;
|
|
||||||
readonly integrity?: string;
|
|
||||||
readonly isHistoryNavigation?: boolean;
|
|
||||||
readonly isReloadNavigation?: boolean;
|
|
||||||
readonly keepalive?: boolean;
|
|
||||||
readonly method: string;
|
|
||||||
readonly mode?: RequestMode;
|
|
||||||
readonly redirect?: RequestRedirect;
|
|
||||||
readonly referrer?: string;
|
|
||||||
readonly referrerPolicy?: ReferrerPolicy;
|
|
||||||
readonly signal?: AbortSignal;
|
|
||||||
readonly url: string;
|
|
||||||
clone(): Request;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface RequestConstructor {
|
|
||||||
new (input: RequestInfo, init?: RequestInit): Request;
|
|
||||||
prototype: Request;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Response extends Body {
|
|
||||||
readonly headers: Headers;
|
|
||||||
readonly ok: boolean;
|
|
||||||
readonly redirected: boolean;
|
|
||||||
readonly status: number;
|
|
||||||
readonly statusText: string;
|
|
||||||
readonly type: ResponseType;
|
|
||||||
readonly url: string;
|
|
||||||
clone(): Response;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ResponseConstructor {
|
|
||||||
prototype: Response;
|
|
||||||
new (body?: BodyInit | null, init?: ResponseInit): Response;
|
|
||||||
error(): Response;
|
|
||||||
redirect(url: string, status?: number): Response;
|
|
||||||
}
|
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
export function getDOMStringList(arr: string[]): DOMStringList {
|
|
||||||
Object.defineProperties(arr, {
|
|
||||||
contains: {
|
|
||||||
value(searchElement: string): boolean {
|
|
||||||
return arr.includes(searchElement);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
item: {
|
|
||||||
value(idx: number): string | null {
|
|
||||||
return idx in arr ? arr[idx] : null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return arr as string[] & DOMStringList;
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { EventImpl as Event } from "./event.ts";
|
|
||||||
import { defineEnumerableProps } from "./util.ts";
|
|
||||||
|
|
||||||
export class ErrorEventImpl extends Event implements ErrorEvent {
|
|
||||||
readonly #message: string;
|
|
||||||
readonly #filename: string;
|
|
||||||
readonly #lineno: number;
|
|
||||||
readonly #colno: number;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
readonly #error: any;
|
|
||||||
|
|
||||||
get message(): string {
|
|
||||||
return this.#message;
|
|
||||||
}
|
|
||||||
get filename(): string {
|
|
||||||
return this.#filename;
|
|
||||||
}
|
|
||||||
get lineno(): number {
|
|
||||||
return this.#lineno;
|
|
||||||
}
|
|
||||||
get colno(): number {
|
|
||||||
return this.#colno;
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
get error(): any {
|
|
||||||
return this.#error;
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
type: string,
|
|
||||||
{
|
|
||||||
bubbles,
|
|
||||||
cancelable,
|
|
||||||
composed,
|
|
||||||
message = "",
|
|
||||||
filename = "",
|
|
||||||
lineno = 0,
|
|
||||||
colno = 0,
|
|
||||||
error = null,
|
|
||||||
}: ErrorEventInit = {},
|
|
||||||
) {
|
|
||||||
super(type, {
|
|
||||||
bubbles: bubbles,
|
|
||||||
cancelable: cancelable,
|
|
||||||
composed: composed,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.#message = message;
|
|
||||||
this.#filename = filename;
|
|
||||||
this.#lineno = lineno;
|
|
||||||
this.#colno = colno;
|
|
||||||
this.#error = error;
|
|
||||||
}
|
|
||||||
|
|
||||||
get [Symbol.toStringTag](): string {
|
|
||||||
return "ErrorEvent";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineEnumerableProps(ErrorEventImpl, [
|
|
||||||
"message",
|
|
||||||
"filename",
|
|
||||||
"lineno",
|
|
||||||
"colno",
|
|
||||||
"error",
|
|
||||||
]);
|
|
|
@ -1,406 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import type * as domTypes from "./dom_types.d.ts";
|
|
||||||
import { defineEnumerableProps, requiredArguments } from "./util.ts";
|
|
||||||
import { assert } from "../util.ts";
|
|
||||||
|
|
||||||
/** Stores a non-accessible view of the event path which is used internally in
|
|
||||||
* the logic for determining the path of an event. */
|
|
||||||
export interface EventPath {
|
|
||||||
item: EventTarget;
|
|
||||||
itemInShadowTree: boolean;
|
|
||||||
relatedTarget: EventTarget | null;
|
|
||||||
rootOfClosedTree: boolean;
|
|
||||||
slotInClosedTree: boolean;
|
|
||||||
target: EventTarget | null;
|
|
||||||
touchTargetList: EventTarget[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EventAttributes {
|
|
||||||
type: string;
|
|
||||||
bubbles: boolean;
|
|
||||||
cancelable: boolean;
|
|
||||||
composed: boolean;
|
|
||||||
currentTarget: EventTarget | null;
|
|
||||||
eventPhase: number;
|
|
||||||
target: EventTarget | null;
|
|
||||||
timeStamp: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EventData {
|
|
||||||
dispatched: boolean;
|
|
||||||
inPassiveListener: boolean;
|
|
||||||
isTrusted: boolean;
|
|
||||||
path: EventPath[];
|
|
||||||
stopImmediatePropagation: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventData = new WeakMap<Event, EventData>();
|
|
||||||
|
|
||||||
// accessors for non runtime visible data
|
|
||||||
|
|
||||||
export function getDispatched(event: Event): boolean {
|
|
||||||
return Boolean(eventData.get(event)?.dispatched);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getPath(event: Event): EventPath[] {
|
|
||||||
return eventData.get(event)?.path ?? [];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getStopImmediatePropagation(event: Event): boolean {
|
|
||||||
return Boolean(eventData.get(event)?.stopImmediatePropagation);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setCurrentTarget(
|
|
||||||
event: Event,
|
|
||||||
value: EventTarget | null,
|
|
||||||
): void {
|
|
||||||
(event as EventImpl).currentTarget = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setDispatched(event: Event, value: boolean): void {
|
|
||||||
const data = eventData.get(event as Event);
|
|
||||||
if (data) {
|
|
||||||
data.dispatched = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setEventPhase(event: Event, value: number): void {
|
|
||||||
(event as EventImpl).eventPhase = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setInPassiveListener(event: Event, value: boolean): void {
|
|
||||||
const data = eventData.get(event as Event);
|
|
||||||
if (data) {
|
|
||||||
data.inPassiveListener = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setPath(event: Event, value: EventPath[]): void {
|
|
||||||
const data = eventData.get(event as Event);
|
|
||||||
if (data) {
|
|
||||||
data.path = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setRelatedTarget<T extends Event>(
|
|
||||||
event: T,
|
|
||||||
value: EventTarget | null,
|
|
||||||
): void {
|
|
||||||
if ("relatedTarget" in event) {
|
|
||||||
(event as T & {
|
|
||||||
relatedTarget: EventTarget | null;
|
|
||||||
}).relatedTarget = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setTarget(event: Event, value: EventTarget | null): void {
|
|
||||||
(event as EventImpl).target = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function setStopImmediatePropagation(
|
|
||||||
event: Event,
|
|
||||||
value: boolean,
|
|
||||||
): void {
|
|
||||||
const data = eventData.get(event as Event);
|
|
||||||
if (data) {
|
|
||||||
data.stopImmediatePropagation = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type guards that widen the event type
|
|
||||||
|
|
||||||
export function hasRelatedTarget(
|
|
||||||
event: Event,
|
|
||||||
): event is domTypes.FocusEvent | domTypes.MouseEvent {
|
|
||||||
return "relatedTarget" in event;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isTrusted(this: Event): boolean {
|
|
||||||
return eventData.get(this)!.isTrusted;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EventImpl implements Event {
|
|
||||||
// The default value is `false`.
|
|
||||||
// Use `defineProperty` to define on each instance, NOT on the prototype.
|
|
||||||
isTrusted!: boolean;
|
|
||||||
|
|
||||||
#canceledFlag = false;
|
|
||||||
#stopPropagationFlag = false;
|
|
||||||
#attributes: EventAttributes;
|
|
||||||
|
|
||||||
constructor(type: string, eventInitDict: EventInit = {}) {
|
|
||||||
requiredArguments("Event", arguments.length, 1);
|
|
||||||
type = String(type);
|
|
||||||
this.#attributes = {
|
|
||||||
type,
|
|
||||||
bubbles: eventInitDict.bubbles ?? false,
|
|
||||||
cancelable: eventInitDict.cancelable ?? false,
|
|
||||||
composed: eventInitDict.composed ?? false,
|
|
||||||
currentTarget: null,
|
|
||||||
eventPhase: Event.NONE,
|
|
||||||
target: null,
|
|
||||||
timeStamp: Date.now(),
|
|
||||||
};
|
|
||||||
eventData.set(this, {
|
|
||||||
dispatched: false,
|
|
||||||
inPassiveListener: false,
|
|
||||||
isTrusted: false,
|
|
||||||
path: [],
|
|
||||||
stopImmediatePropagation: false,
|
|
||||||
});
|
|
||||||
Reflect.defineProperty(this, "isTrusted", {
|
|
||||||
enumerable: true,
|
|
||||||
get: isTrusted,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get bubbles(): boolean {
|
|
||||||
return this.#attributes.bubbles;
|
|
||||||
}
|
|
||||||
|
|
||||||
get cancelBubble(): boolean {
|
|
||||||
return this.#stopPropagationFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
set cancelBubble(value: boolean) {
|
|
||||||
this.#stopPropagationFlag = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
get cancelable(): boolean {
|
|
||||||
return this.#attributes.cancelable;
|
|
||||||
}
|
|
||||||
|
|
||||||
get composed(): boolean {
|
|
||||||
return this.#attributes.composed;
|
|
||||||
}
|
|
||||||
|
|
||||||
get currentTarget(): EventTarget | null {
|
|
||||||
return this.#attributes.currentTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
set currentTarget(value: EventTarget | null) {
|
|
||||||
this.#attributes = {
|
|
||||||
type: this.type,
|
|
||||||
bubbles: this.bubbles,
|
|
||||||
cancelable: this.cancelable,
|
|
||||||
composed: this.composed,
|
|
||||||
currentTarget: value,
|
|
||||||
eventPhase: this.eventPhase,
|
|
||||||
target: this.target,
|
|
||||||
timeStamp: this.timeStamp,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get defaultPrevented(): boolean {
|
|
||||||
return this.#canceledFlag;
|
|
||||||
}
|
|
||||||
|
|
||||||
get eventPhase(): number {
|
|
||||||
return this.#attributes.eventPhase;
|
|
||||||
}
|
|
||||||
|
|
||||||
set eventPhase(value: number) {
|
|
||||||
this.#attributes = {
|
|
||||||
type: this.type,
|
|
||||||
bubbles: this.bubbles,
|
|
||||||
cancelable: this.cancelable,
|
|
||||||
composed: this.composed,
|
|
||||||
currentTarget: this.currentTarget,
|
|
||||||
eventPhase: value,
|
|
||||||
target: this.target,
|
|
||||||
timeStamp: this.timeStamp,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get initialized(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get target(): EventTarget | null {
|
|
||||||
return this.#attributes.target;
|
|
||||||
}
|
|
||||||
|
|
||||||
set target(value: EventTarget | null) {
|
|
||||||
this.#attributes = {
|
|
||||||
type: this.type,
|
|
||||||
bubbles: this.bubbles,
|
|
||||||
cancelable: this.cancelable,
|
|
||||||
composed: this.composed,
|
|
||||||
currentTarget: this.currentTarget,
|
|
||||||
eventPhase: this.eventPhase,
|
|
||||||
target: value,
|
|
||||||
timeStamp: this.timeStamp,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
get timeStamp(): number {
|
|
||||||
return this.#attributes.timeStamp;
|
|
||||||
}
|
|
||||||
|
|
||||||
get type(): string {
|
|
||||||
return this.#attributes.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
composedPath(): EventTarget[] {
|
|
||||||
const path = eventData.get(this)!.path;
|
|
||||||
if (path.length === 0) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(this.currentTarget);
|
|
||||||
const composedPath: EventPath[] = [
|
|
||||||
{
|
|
||||||
item: this.currentTarget,
|
|
||||||
itemInShadowTree: false,
|
|
||||||
relatedTarget: null,
|
|
||||||
rootOfClosedTree: false,
|
|
||||||
slotInClosedTree: false,
|
|
||||||
target: null,
|
|
||||||
touchTargetList: [],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
let currentTargetIndex = 0;
|
|
||||||
let currentTargetHiddenSubtreeLevel = 0;
|
|
||||||
|
|
||||||
for (let index = path.length - 1; index >= 0; index--) {
|
|
||||||
const { item, rootOfClosedTree, slotInClosedTree } = path[index];
|
|
||||||
|
|
||||||
if (rootOfClosedTree) {
|
|
||||||
currentTargetHiddenSubtreeLevel++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (item === this.currentTarget) {
|
|
||||||
currentTargetIndex = index;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slotInClosedTree) {
|
|
||||||
currentTargetHiddenSubtreeLevel--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let currentHiddenLevel = currentTargetHiddenSubtreeLevel;
|
|
||||||
let maxHiddenLevel = currentTargetHiddenSubtreeLevel;
|
|
||||||
|
|
||||||
for (let i = currentTargetIndex - 1; i >= 0; i--) {
|
|
||||||
const { item, rootOfClosedTree, slotInClosedTree } = path[i];
|
|
||||||
|
|
||||||
if (rootOfClosedTree) {
|
|
||||||
currentHiddenLevel++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentHiddenLevel <= maxHiddenLevel) {
|
|
||||||
composedPath.unshift({
|
|
||||||
item,
|
|
||||||
itemInShadowTree: false,
|
|
||||||
relatedTarget: null,
|
|
||||||
rootOfClosedTree: false,
|
|
||||||
slotInClosedTree: false,
|
|
||||||
target: null,
|
|
||||||
touchTargetList: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slotInClosedTree) {
|
|
||||||
currentHiddenLevel--;
|
|
||||||
|
|
||||||
if (currentHiddenLevel < maxHiddenLevel) {
|
|
||||||
maxHiddenLevel = currentHiddenLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentHiddenLevel = currentTargetHiddenSubtreeLevel;
|
|
||||||
maxHiddenLevel = currentTargetHiddenSubtreeLevel;
|
|
||||||
|
|
||||||
for (let index = currentTargetIndex + 1; index < path.length; index++) {
|
|
||||||
const { item, rootOfClosedTree, slotInClosedTree } = path[index];
|
|
||||||
|
|
||||||
if (slotInClosedTree) {
|
|
||||||
currentHiddenLevel++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentHiddenLevel <= maxHiddenLevel) {
|
|
||||||
composedPath.push({
|
|
||||||
item,
|
|
||||||
itemInShadowTree: false,
|
|
||||||
relatedTarget: null,
|
|
||||||
rootOfClosedTree: false,
|
|
||||||
slotInClosedTree: false,
|
|
||||||
target: null,
|
|
||||||
touchTargetList: [],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rootOfClosedTree) {
|
|
||||||
currentHiddenLevel--;
|
|
||||||
|
|
||||||
if (currentHiddenLevel < maxHiddenLevel) {
|
|
||||||
maxHiddenLevel = currentHiddenLevel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return composedPath.map((p) => p.item);
|
|
||||||
}
|
|
||||||
|
|
||||||
preventDefault(): void {
|
|
||||||
if (this.cancelable && !eventData.get(this)!.inPassiveListener) {
|
|
||||||
this.#canceledFlag = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stopPropagation(): void {
|
|
||||||
this.#stopPropagationFlag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
stopImmediatePropagation(): void {
|
|
||||||
this.#stopPropagationFlag = true;
|
|
||||||
eventData.get(this)!.stopImmediatePropagation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
get NONE(): number {
|
|
||||||
return Event.NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
get CAPTURING_PHASE(): number {
|
|
||||||
return Event.CAPTURING_PHASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
get AT_TARGET(): number {
|
|
||||||
return Event.AT_TARGET;
|
|
||||||
}
|
|
||||||
|
|
||||||
get BUBBLING_PHASE(): number {
|
|
||||||
return Event.BUBBLING_PHASE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get NONE(): number {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get CAPTURING_PHASE(): number {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get AT_TARGET(): number {
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get BUBBLING_PHASE(): number {
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineEnumerableProps(EventImpl, [
|
|
||||||
"bubbles",
|
|
||||||
"cancelable",
|
|
||||||
"composed",
|
|
||||||
"currentTarget",
|
|
||||||
"defaultPrevented",
|
|
||||||
"eventPhase",
|
|
||||||
"target",
|
|
||||||
"timeStamp",
|
|
||||||
"type",
|
|
||||||
]);
|
|
|
@ -1,588 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
// This module follows most of the WHATWG Living Standard for the DOM logic.
|
|
||||||
// Many parts of the DOM are not implemented in Deno, but the logic for those
|
|
||||||
// parts still exists. This means you will observe a lot of strange structures
|
|
||||||
// and impossible logic branches based on what Deno currently supports.
|
|
||||||
|
|
||||||
import { DOMExceptionImpl as DOMException } from "./dom_exception.ts";
|
|
||||||
import type * as domTypes from "./dom_types.d.ts";
|
|
||||||
import {
|
|
||||||
EventImpl as Event,
|
|
||||||
EventPath,
|
|
||||||
getDispatched,
|
|
||||||
getPath,
|
|
||||||
getStopImmediatePropagation,
|
|
||||||
hasRelatedTarget,
|
|
||||||
setCurrentTarget,
|
|
||||||
setDispatched,
|
|
||||||
setEventPhase,
|
|
||||||
setInPassiveListener,
|
|
||||||
setPath,
|
|
||||||
setRelatedTarget,
|
|
||||||
setStopImmediatePropagation,
|
|
||||||
setTarget,
|
|
||||||
} from "./event.ts";
|
|
||||||
import { defineEnumerableProps, requiredArguments } from "./util.ts";
|
|
||||||
|
|
||||||
// This is currently the only node type we are using, so instead of implementing
|
|
||||||
// the whole of the Node interface at the moment, this just gives us the one
|
|
||||||
// value to power the standards based logic
|
|
||||||
const DOCUMENT_FRAGMENT_NODE = 11;
|
|
||||||
|
|
||||||
// DOM Logic Helper functions and type guards
|
|
||||||
|
|
||||||
/** Get the parent node, for event targets that have a parent.
|
|
||||||
*
|
|
||||||
* Ref: https://dom.spec.whatwg.org/#get-the-parent */
|
|
||||||
function getParent(eventTarget: EventTarget): EventTarget | null {
|
|
||||||
return isNode(eventTarget) ? eventTarget.parentNode : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRoot(eventTarget: EventTarget): EventTarget | null {
|
|
||||||
return isNode(eventTarget)
|
|
||||||
? eventTarget.getRootNode({ composed: true })
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isNode<T extends EventTarget>(
|
|
||||||
eventTarget: T | null,
|
|
||||||
): eventTarget is T & domTypes.Node {
|
|
||||||
return Boolean(eventTarget && "nodeType" in eventTarget);
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
|
|
||||||
function isShadowInclusiveAncestor(
|
|
||||||
ancestor: EventTarget | null,
|
|
||||||
node: EventTarget | null,
|
|
||||||
): boolean {
|
|
||||||
while (isNode(node)) {
|
|
||||||
if (node === ancestor) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isShadowRoot(node)) {
|
|
||||||
node = node && getHost(node);
|
|
||||||
} else {
|
|
||||||
node = getParent(node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isShadowRoot(nodeImpl: EventTarget | null): boolean {
|
|
||||||
return Boolean(
|
|
||||||
nodeImpl &&
|
|
||||||
isNode(nodeImpl) &&
|
|
||||||
nodeImpl.nodeType === DOCUMENT_FRAGMENT_NODE &&
|
|
||||||
getHost(nodeImpl) != null,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isSlotable<T extends EventTarget>(
|
|
||||||
nodeImpl: T | null,
|
|
||||||
): nodeImpl is T & domTypes.Node & domTypes.Slotable {
|
|
||||||
return Boolean(isNode(nodeImpl) && "assignedSlot" in nodeImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
// DOM Logic functions
|
|
||||||
|
|
||||||
/** Append a path item to an event's path.
|
|
||||||
*
|
|
||||||
* Ref: https://dom.spec.whatwg.org/#concept-event-path-append
|
|
||||||
*/
|
|
||||||
function appendToEventPath(
|
|
||||||
eventImpl: Event,
|
|
||||||
target: EventTarget,
|
|
||||||
targetOverride: EventTarget | null,
|
|
||||||
relatedTarget: EventTarget | null,
|
|
||||||
touchTargets: EventTarget[],
|
|
||||||
slotInClosedTree: boolean,
|
|
||||||
): void {
|
|
||||||
const itemInShadowTree = isNode(target) && isShadowRoot(getRoot(target));
|
|
||||||
const rootOfClosedTree = isShadowRoot(target) && getMode(target) === "closed";
|
|
||||||
|
|
||||||
getPath(eventImpl).push({
|
|
||||||
item: target,
|
|
||||||
itemInShadowTree,
|
|
||||||
target: targetOverride,
|
|
||||||
relatedTarget,
|
|
||||||
touchTargetList: touchTargets,
|
|
||||||
rootOfClosedTree,
|
|
||||||
slotInClosedTree,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function dispatch(
|
|
||||||
targetImpl: EventTarget,
|
|
||||||
eventImpl: Event,
|
|
||||||
targetOverride?: EventTarget,
|
|
||||||
): boolean {
|
|
||||||
let clearTargets = false;
|
|
||||||
let activationTarget: EventTarget | null = null;
|
|
||||||
|
|
||||||
setDispatched(eventImpl, true);
|
|
||||||
|
|
||||||
targetOverride = targetOverride ?? targetImpl;
|
|
||||||
const eventRelatedTarget = hasRelatedTarget(eventImpl)
|
|
||||||
? eventImpl.relatedTarget
|
|
||||||
: null;
|
|
||||||
let relatedTarget = retarget(eventRelatedTarget, targetImpl);
|
|
||||||
|
|
||||||
if (targetImpl !== relatedTarget || targetImpl === eventRelatedTarget) {
|
|
||||||
const touchTargets: EventTarget[] = [];
|
|
||||||
|
|
||||||
appendToEventPath(
|
|
||||||
eventImpl,
|
|
||||||
targetImpl,
|
|
||||||
targetOverride,
|
|
||||||
relatedTarget,
|
|
||||||
touchTargets,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
const isActivationEvent = eventImpl.type === "click";
|
|
||||||
|
|
||||||
if (isActivationEvent && getHasActivationBehavior(targetImpl)) {
|
|
||||||
activationTarget = targetImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
let slotInClosedTree = false;
|
|
||||||
let slotable = isSlotable(targetImpl) && getAssignedSlot(targetImpl)
|
|
||||||
? targetImpl
|
|
||||||
: null;
|
|
||||||
let parent = getParent(targetImpl);
|
|
||||||
|
|
||||||
// Populate event path
|
|
||||||
// https://dom.spec.whatwg.org/#event-path
|
|
||||||
while (parent !== null) {
|
|
||||||
if (slotable !== null) {
|
|
||||||
slotable = null;
|
|
||||||
|
|
||||||
const parentRoot = getRoot(parent);
|
|
||||||
if (
|
|
||||||
isShadowRoot(parentRoot) &&
|
|
||||||
parentRoot &&
|
|
||||||
getMode(parentRoot) === "closed"
|
|
||||||
) {
|
|
||||||
slotInClosedTree = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
relatedTarget = retarget(eventRelatedTarget, parent);
|
|
||||||
|
|
||||||
if (
|
|
||||||
isNode(parent) &&
|
|
||||||
isShadowInclusiveAncestor(getRoot(targetImpl), parent)
|
|
||||||
) {
|
|
||||||
appendToEventPath(
|
|
||||||
eventImpl,
|
|
||||||
parent,
|
|
||||||
null,
|
|
||||||
relatedTarget,
|
|
||||||
touchTargets,
|
|
||||||
slotInClosedTree,
|
|
||||||
);
|
|
||||||
} else if (parent === relatedTarget) {
|
|
||||||
parent = null;
|
|
||||||
} else {
|
|
||||||
targetImpl = parent;
|
|
||||||
|
|
||||||
if (
|
|
||||||
isActivationEvent &&
|
|
||||||
activationTarget === null &&
|
|
||||||
getHasActivationBehavior(targetImpl)
|
|
||||||
) {
|
|
||||||
activationTarget = targetImpl;
|
|
||||||
}
|
|
||||||
|
|
||||||
appendToEventPath(
|
|
||||||
eventImpl,
|
|
||||||
parent,
|
|
||||||
targetImpl,
|
|
||||||
relatedTarget,
|
|
||||||
touchTargets,
|
|
||||||
slotInClosedTree,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parent !== null) {
|
|
||||||
parent = getParent(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
slotInClosedTree = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let clearTargetsTupleIndex = -1;
|
|
||||||
const path = getPath(eventImpl);
|
|
||||||
for (
|
|
||||||
let i = path.length - 1;
|
|
||||||
i >= 0 && clearTargetsTupleIndex === -1;
|
|
||||||
i--
|
|
||||||
) {
|
|
||||||
if (path[i].target !== null) {
|
|
||||||
clearTargetsTupleIndex = i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const clearTargetsTuple = path[clearTargetsTupleIndex];
|
|
||||||
|
|
||||||
clearTargets = (isNode(clearTargetsTuple.target) &&
|
|
||||||
isShadowRoot(getRoot(clearTargetsTuple.target))) ||
|
|
||||||
(isNode(clearTargetsTuple.relatedTarget) &&
|
|
||||||
isShadowRoot(getRoot(clearTargetsTuple.relatedTarget)));
|
|
||||||
|
|
||||||
setEventPhase(eventImpl, Event.CAPTURING_PHASE);
|
|
||||||
|
|
||||||
for (let i = path.length - 1; i >= 0; --i) {
|
|
||||||
const tuple = path[i];
|
|
||||||
|
|
||||||
if (tuple.target === null) {
|
|
||||||
invokeEventListeners(tuple, eventImpl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < path.length; i++) {
|
|
||||||
const tuple = path[i];
|
|
||||||
|
|
||||||
if (tuple.target !== null) {
|
|
||||||
setEventPhase(eventImpl, Event.AT_TARGET);
|
|
||||||
} else {
|
|
||||||
setEventPhase(eventImpl, Event.BUBBLING_PHASE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
(eventImpl.eventPhase === Event.BUBBLING_PHASE && eventImpl.bubbles) ||
|
|
||||||
eventImpl.eventPhase === Event.AT_TARGET
|
|
||||||
) {
|
|
||||||
invokeEventListeners(tuple, eventImpl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setEventPhase(eventImpl, Event.NONE);
|
|
||||||
setCurrentTarget(eventImpl, null);
|
|
||||||
setPath(eventImpl, []);
|
|
||||||
setDispatched(eventImpl, false);
|
|
||||||
eventImpl.cancelBubble = false;
|
|
||||||
setStopImmediatePropagation(eventImpl, false);
|
|
||||||
|
|
||||||
if (clearTargets) {
|
|
||||||
setTarget(eventImpl, null);
|
|
||||||
setRelatedTarget(eventImpl, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: invoke activation targets if HTML nodes will be implemented
|
|
||||||
// if (activationTarget !== null) {
|
|
||||||
// if (!eventImpl.defaultPrevented) {
|
|
||||||
// activationTarget._activationBehavior();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return !eventImpl.defaultPrevented;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Inner invoking of the event listeners where the resolved listeners are
|
|
||||||
* called.
|
|
||||||
*
|
|
||||||
* Ref: https://dom.spec.whatwg.org/#concept-event-listener-inner-invoke */
|
|
||||||
function innerInvokeEventListeners(
|
|
||||||
eventImpl: Event,
|
|
||||||
targetListeners: Record<string, Listener[]>,
|
|
||||||
): boolean {
|
|
||||||
let found = false;
|
|
||||||
|
|
||||||
const { type } = eventImpl;
|
|
||||||
|
|
||||||
if (!targetListeners || !targetListeners[type]) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy event listeners before iterating since the list can be modified during the iteration.
|
|
||||||
const handlers = targetListeners[type].slice();
|
|
||||||
|
|
||||||
for (let i = 0; i < handlers.length; i++) {
|
|
||||||
const listener = handlers[i];
|
|
||||||
|
|
||||||
let capture, once, passive;
|
|
||||||
if (typeof listener.options === "boolean") {
|
|
||||||
capture = listener.options;
|
|
||||||
once = false;
|
|
||||||
passive = false;
|
|
||||||
} else {
|
|
||||||
capture = listener.options.capture;
|
|
||||||
once = listener.options.once;
|
|
||||||
passive = listener.options.passive;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the event listener has been removed since the listeners has been cloned.
|
|
||||||
if (!targetListeners[type].includes(listener)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
found = true;
|
|
||||||
|
|
||||||
if (
|
|
||||||
(eventImpl.eventPhase === Event.CAPTURING_PHASE && !capture) ||
|
|
||||||
(eventImpl.eventPhase === Event.BUBBLING_PHASE && capture)
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (once) {
|
|
||||||
targetListeners[type].splice(targetListeners[type].indexOf(listener), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (passive) {
|
|
||||||
setInPassiveListener(eventImpl, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof listener.callback === "object") {
|
|
||||||
if (typeof listener.callback.handleEvent === "function") {
|
|
||||||
listener.callback.handleEvent(eventImpl);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
listener.callback.call(eventImpl.currentTarget, eventImpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
setInPassiveListener(eventImpl, false);
|
|
||||||
|
|
||||||
if (getStopImmediatePropagation(eventImpl)) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Invokes the listeners on a given event path with the supplied event.
|
|
||||||
*
|
|
||||||
* Ref: https://dom.spec.whatwg.org/#concept-event-listener-invoke */
|
|
||||||
function invokeEventListeners(tuple: EventPath, eventImpl: Event): void {
|
|
||||||
const path = getPath(eventImpl);
|
|
||||||
const tupleIndex = path.indexOf(tuple);
|
|
||||||
for (let i = tupleIndex; i >= 0; i--) {
|
|
||||||
const t = path[i];
|
|
||||||
if (t.target) {
|
|
||||||
setTarget(eventImpl, t.target);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setRelatedTarget(eventImpl, tuple.relatedTarget);
|
|
||||||
|
|
||||||
if (eventImpl.cancelBubble) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setCurrentTarget(eventImpl, tuple.item);
|
|
||||||
|
|
||||||
innerInvokeEventListeners(eventImpl, getListeners(tuple.item));
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeAddEventHandlerOptions(
|
|
||||||
options: boolean | AddEventListenerOptions | undefined,
|
|
||||||
): AddEventListenerOptions {
|
|
||||||
if (typeof options === "boolean" || typeof options === "undefined") {
|
|
||||||
return {
|
|
||||||
capture: Boolean(options),
|
|
||||||
once: false,
|
|
||||||
passive: false,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeEventHandlerOptions(
|
|
||||||
options: boolean | EventListenerOptions | undefined,
|
|
||||||
): EventListenerOptions {
|
|
||||||
if (typeof options === "boolean" || typeof options === "undefined") {
|
|
||||||
return {
|
|
||||||
capture: Boolean(options),
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Retarget the target following the spec logic.
|
|
||||||
*
|
|
||||||
* Ref: https://dom.spec.whatwg.org/#retarget */
|
|
||||||
function retarget(a: EventTarget | null, b: EventTarget): EventTarget | null {
|
|
||||||
while (true) {
|
|
||||||
if (!isNode(a)) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
const aRoot = a.getRootNode();
|
|
||||||
|
|
||||||
if (aRoot) {
|
|
||||||
if (
|
|
||||||
!isShadowRoot(aRoot) ||
|
|
||||||
(isNode(b) && isShadowInclusiveAncestor(aRoot, b))
|
|
||||||
) {
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
a = getHost(aRoot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Non-public state information for an event target that needs to held onto.
|
|
||||||
// Some of the information should be moved to other entities (like Node,
|
|
||||||
// ShowRoot, UIElement, etc.).
|
|
||||||
interface EventTargetData {
|
|
||||||
assignedSlot: boolean;
|
|
||||||
hasActivationBehavior: boolean;
|
|
||||||
host: EventTarget | null;
|
|
||||||
listeners: Record<string, Listener[]>;
|
|
||||||
mode: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Listener {
|
|
||||||
callback: EventListenerOrEventListenerObject;
|
|
||||||
options: AddEventListenerOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Accessors for non-public data
|
|
||||||
|
|
||||||
export const eventTargetData = new WeakMap<EventTarget, EventTargetData>();
|
|
||||||
|
|
||||||
function getAssignedSlot(target: EventTarget): boolean {
|
|
||||||
return Boolean(eventTargetData.get(target as EventTarget)?.assignedSlot);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHasActivationBehavior(target: EventTarget): boolean {
|
|
||||||
return Boolean(
|
|
||||||
eventTargetData.get(target as EventTarget)?.hasActivationBehavior,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getHost(target: EventTarget): EventTarget | null {
|
|
||||||
return eventTargetData.get(target as EventTarget)?.host ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getListeners(target: EventTarget): Record<string, Listener[]> {
|
|
||||||
return eventTargetData.get(target as EventTarget)?.listeners ?? {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMode(target: EventTarget): string | null {
|
|
||||||
return eventTargetData.get(target as EventTarget)?.mode ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getDefaultTargetData(): Readonly<EventTargetData> {
|
|
||||||
return {
|
|
||||||
assignedSlot: false,
|
|
||||||
hasActivationBehavior: false,
|
|
||||||
host: null,
|
|
||||||
listeners: Object.create(null),
|
|
||||||
mode: "",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class EventTargetImpl implements EventTarget {
|
|
||||||
constructor() {
|
|
||||||
eventTargetData.set(this, getDefaultTargetData());
|
|
||||||
}
|
|
||||||
|
|
||||||
public addEventListener(
|
|
||||||
type: string,
|
|
||||||
callback: EventListenerOrEventListenerObject | null,
|
|
||||||
options?: AddEventListenerOptions | boolean,
|
|
||||||
): void {
|
|
||||||
requiredArguments("EventTarget.addEventListener", arguments.length, 2);
|
|
||||||
if (callback === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
options = normalizeAddEventHandlerOptions(options);
|
|
||||||
const { listeners } = eventTargetData.get(this ?? globalThis)!;
|
|
||||||
|
|
||||||
if (!(type in listeners)) {
|
|
||||||
listeners[type] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const listener of listeners[type]) {
|
|
||||||
if (
|
|
||||||
((typeof listener.options === "boolean" &&
|
|
||||||
listener.options === options.capture) ||
|
|
||||||
(typeof listener.options === "object" &&
|
|
||||||
listener.options.capture === options.capture)) &&
|
|
||||||
listener.callback === callback
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
listeners[type].push({ callback, options });
|
|
||||||
}
|
|
||||||
|
|
||||||
public removeEventListener(
|
|
||||||
type: string,
|
|
||||||
callback: EventListenerOrEventListenerObject | null,
|
|
||||||
options?: EventListenerOptions | boolean,
|
|
||||||
): void {
|
|
||||||
requiredArguments("EventTarget.removeEventListener", arguments.length, 2);
|
|
||||||
|
|
||||||
const listeners = eventTargetData.get(this ?? globalThis)!.listeners;
|
|
||||||
if (callback !== null && type in listeners) {
|
|
||||||
listeners[type] = listeners[type].filter(
|
|
||||||
(listener) => listener.callback !== callback,
|
|
||||||
);
|
|
||||||
} else if (callback === null || !listeners[type]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
options = normalizeEventHandlerOptions(options);
|
|
||||||
|
|
||||||
for (let i = 0; i < listeners[type].length; ++i) {
|
|
||||||
const listener = listeners[type][i];
|
|
||||||
if (
|
|
||||||
((typeof listener.options === "boolean" &&
|
|
||||||
listener.options === options.capture) ||
|
|
||||||
(typeof listener.options === "object" &&
|
|
||||||
listener.options.capture === options.capture)) &&
|
|
||||||
listener.callback === callback
|
|
||||||
) {
|
|
||||||
listeners[type].splice(i, 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public dispatchEvent(event: Event): boolean {
|
|
||||||
requiredArguments("EventTarget.dispatchEvent", arguments.length, 1);
|
|
||||||
const self = this ?? globalThis;
|
|
||||||
|
|
||||||
const listeners = eventTargetData.get(self)!.listeners;
|
|
||||||
if (!(event.type in listeners)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getDispatched(event)) {
|
|
||||||
throw new DOMException("Invalid event state.", "InvalidStateError");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.eventPhase !== Event.NONE) {
|
|
||||||
throw new DOMException("Invalid event state.", "InvalidStateError");
|
|
||||||
}
|
|
||||||
|
|
||||||
return dispatch(self, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
get [Symbol.toStringTag](): string {
|
|
||||||
return "EventTarget";
|
|
||||||
}
|
|
||||||
|
|
||||||
protected getParent(_event: Event): EventTarget | null {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
defineEnumerableProps(EventTargetImpl, [
|
|
||||||
"addEventListener",
|
|
||||||
"removeEventListener",
|
|
||||||
"dispatchEvent",
|
|
||||||
]);
|
|
|
@ -1,361 +0,0 @@
|
||||||
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
|
||||||
|
|
||||||
import { notImplemented } from "../util.ts";
|
|
||||||
import { isTypedArray } from "./util.ts";
|
|
||||||
import type * as domTypes from "./dom_types.d.ts";
|
|
||||||
import { TextEncoder } from "./text_encoding.ts";
|
|
||||||
import { DenoBlob, bytesSymbol as blobBytesSymbol } from "./blob.ts";
|
|
||||||
import { read } from "../ops/io.ts";
|
|
||||||
import { close } from "../ops/resources.ts";
|
|
||||||
import { fetch as opFetch } from "../ops/fetch.ts";
|
|
||||||
import type { FetchResponse } from "../ops/fetch.ts";
|
|
||||||
import * as Body from "./body.ts";
|
|
||||||
import { getHeaderValueParams } from "./util.ts";
|
|
||||||
import { ReadableStreamImpl } from "./streams/readable_stream.ts";
|
|
||||||
import { MultipartBuilder } from "./fetch/multipart.ts";
|
|
||||||
|
|
||||||
const NULL_BODY_STATUS = [101, 204, 205, 304];
|
|
||||||
const REDIRECT_STATUS = [301, 302, 303, 307, 308];
|
|
||||||
|
|
||||||
const responseData = new WeakMap();
|
|
||||||
export class Response extends Body.Body implements domTypes.Response {
|
|
||||||
readonly type: ResponseType;
|
|
||||||
readonly redirected: boolean;
|
|
||||||
readonly url: string;
|
|
||||||
readonly status: number;
|
|
||||||
readonly statusText: string;
|
|
||||||
headers: Headers;
|
|
||||||
|
|
||||||
constructor(body: BodyInit | null = null, init?: domTypes.ResponseInit) {
|
|
||||||
init = init ?? {};
|
|
||||||
|
|
||||||
if (typeof init !== "object") {
|
|
||||||
throw new TypeError(`'init' is not an object`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const extraInit = responseData.get(init) || {};
|
|
||||||
let { type = "default", url = "" } = extraInit;
|
|
||||||
|
|
||||||
let status = init.status === undefined ? 200 : Number(init.status || 0);
|
|
||||||
let statusText = init.statusText ?? "";
|
|
||||||
let headers = init.headers instanceof Headers
|
|
||||||
? init.headers
|
|
||||||
: new Headers(init.headers);
|
|
||||||
|
|
||||||
if (init.status !== undefined && (status < 200 || status > 599)) {
|
|
||||||
throw new RangeError(
|
|
||||||
`The status provided (${init.status}) is outside the range [200, 599]`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// null body status
|
|
||||||
if (body && NULL_BODY_STATUS.includes(status)) {
|
|
||||||
throw new TypeError("Response with null body status cannot have body");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!type) {
|
|
||||||
type = "default";
|
|
||||||
} else {
|
|
||||||
if (type == "error") {
|
|
||||||
// spec: https://fetch.spec.whatwg.org/#concept-network-error
|
|
||||||
status = 0;
|
|
||||||
statusText = "";
|
|
||||||
headers = new Headers();
|
|
||||||
body = null;
|
|
||||||
/* spec for other Response types:
|
|
||||||
https://fetch.spec.whatwg.org/#concept-filtered-response-basic
|
|
||||||
Please note that type "basic" is not the same thing as "default".*/
|
|
||||||
} else if (type == "basic") {
|
|
||||||
for (const h of headers) {
|
|
||||||
/* Forbidden Response-Header Names:
|
|
||||||
https://fetch.spec.whatwg.org/#forbidden-response-header-name */
|
|
||||||
if (["set-cookie", "set-cookie2"].includes(h[0].toLowerCase())) {
|
|
||||||
headers.delete(h[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (type == "cors") {
|
|
||||||
/* CORS-safelisted Response-Header Names:
|
|
||||||
https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name */
|
|
||||||
const allowedHeaders = [
|
|
||||||
"Cache-Control",
|
|
||||||
"Content-Language",
|
|
||||||
"Content-Length",
|
|
||||||
"Content-Type",
|
|
||||||
"Expires",
|
|
||||||
"Last-Modified",
|
|
||||||
"Pragma",
|
|
||||||
].map((c: string) => c.toLowerCase());
|
|
||||||
for (const h of headers) {
|
|
||||||
/* Technically this is still not standards compliant because we are
|
|
||||||
supposed to allow headers allowed in the
|
|
||||||
'Access-Control-Expose-Headers' header in the 'internal response'
|
|
||||||
However, this implementation of response doesn't seem to have an
|
|
||||||
easy way to access the internal response, so we ignore that
|
|
||||||
header.
|
|
||||||
TODO(serverhiccups): change how internal responses are handled
|
|
||||||
so we can do this properly. */
|
|
||||||
if (!allowedHeaders.includes(h[0].toLowerCase())) {
|
|
||||||
headers.delete(h[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* TODO(serverhiccups): Once I fix the 'internal response' thing,
|
|
||||||
these actually need to treat the internal response differently */
|
|
||||||
} else if (type == "opaque" || type == "opaqueredirect") {
|
|
||||||
url = "";
|
|
||||||
status = 0;
|
|
||||||
statusText = "";
|
|
||||||
headers = new Headers();
|
|
||||||
body = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const contentType = headers.get("content-type") || "";
|
|
||||||
const size = Number(headers.get("content-length")) || undefined;
|
|
||||||
|
|
||||||
super(body, { contentType, size });
|
|
||||||
|
|
||||||
this.url = url;
|
|
||||||
this.statusText = statusText;
|
|
||||||
this.status = extraInit.status || status;
|
|
||||||
this.headers = headers;
|
|
||||||
this.redirected = extraInit.redirected || false;
|
|
||||||
this.type = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
get ok(): boolean {
|
|
||||||
return 200 <= this.status && this.status < 300;
|
|
||||||
}
|
|
||||||
|
|
||||||
public clone(): domTypes.Response {
|
|
||||||
if (this.bodyUsed) {
|
|
||||||
throw TypeError(Body.BodyUsedError);
|
|
||||||
}
|
|
||||||
|
|
||||||
const iterators = this.headers.entries();
|
|
||||||
const headersList: Array<[string, string]> = [];
|
|
||||||
for (const header of iterators) {
|
|
||||||
headersList.push(header);
|
|
||||||
}
|
|
||||||
|
|
||||||
let resBody = this._bodySource;
|
|
||||||
|
|
||||||
if (this._bodySource instanceof ReadableStreamImpl) {
|
|
||||||
const tees = this._bodySource.tee();
|
|
||||||
this._stream = this._bodySource = tees[0];
|
|
||||||
resBody = tees[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response(resBody, {
|
|
||||||
status: this.status,
|
|
||||||
statusText: this.statusText,
|
|
||||||
headers: new Headers(headersList),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static redirect(url: URL | string, status: number): domTypes.Response {
|
|
||||||
if (![301, 302, 303, 307, 308].includes(status)) {
|
|
||||||
throw new RangeError(
|
|
||||||
"The redirection status must be one of 301, 302, 303, 307 and 308.",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return new Response(null, {
|
|
||||||
status,
|
|
||||||
statusText: "",
|
|
||||||
headers: [["Location", typeof url === "string" ? url : url.toString()]],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendFetchReq(
|
|
||||||
url: string,
|
|
||||||
method: string | null,
|
|
||||||
headers: Headers | null,
|
|
||||||
body: ArrayBufferView | undefined,
|
|
||||||
): Promise<FetchResponse> {
|
|
||||||
let headerArray: Array<[string, string]> = [];
|
|
||||||
if (headers) {
|
|
||||||
headerArray = Array.from(headers.entries());
|
|
||||||
}
|
|
||||||
|
|
||||||
const args = {
|
|
||||||
method,
|
|
||||||
url,
|
|
||||||
headers: headerArray,
|
|
||||||
};
|
|
||||||
|
|
||||||
return opFetch(args, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function fetch(
|
|
||||||
input: (domTypes.Request & { _bodySource?: unknown }) | URL | string,
|
|
||||||
init?: domTypes.RequestInit,
|
|
||||||
): Promise<Response> {
|
|
||||||
let url: string;
|
|
||||||
let method: string | null = null;
|
|
||||||
let headers: Headers | null = null;
|
|
||||||
let body: ArrayBufferView | undefined;
|
|
||||||
let redirected = false;
|
|
||||||
let remRedirectCount = 20; // TODO: use a better way to handle
|
|
||||||
|
|
||||||
if (typeof input === "string" || input instanceof URL) {
|
|
||||||
url = typeof input === "string" ? (input as string) : (input as URL).href;
|
|
||||||
if (init != null) {
|
|
||||||
method = init.method || null;
|
|
||||||
if (init.headers) {
|
|
||||||
headers = init.headers instanceof Headers
|
|
||||||
? init.headers
|
|
||||||
: new Headers(init.headers);
|
|
||||||
} else {
|
|
||||||
headers = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ref: https://fetch.spec.whatwg.org/#body-mixin
|
|
||||||
// Body should have been a mixin
|
|
||||||
// but we are treating it as a separate class
|
|
||||||
if (init.body) {
|
|
||||||
if (!headers) {
|
|
||||||
headers = new Headers();
|
|
||||||
}
|
|
||||||
let contentType = "";
|
|
||||||
if (typeof init.body === "string") {
|
|
||||||
body = new TextEncoder().encode(init.body);
|
|
||||||
contentType = "text/plain;charset=UTF-8";
|
|
||||||
} else if (isTypedArray(init.body)) {
|
|
||||||
body = init.body;
|
|
||||||
} else if (init.body instanceof ArrayBuffer) {
|
|
||||||
body = new Uint8Array(init.body);
|
|
||||||
} else if (init.body instanceof URLSearchParams) {
|
|
||||||
body = new TextEncoder().encode(init.body.toString());
|
|
||||||
contentType = "application/x-www-form-urlencoded;charset=UTF-8";
|
|
||||||
} else if (init.body instanceof DenoBlob) {
|
|
||||||
body = init.body[blobBytesSymbol];
|
|
||||||
contentType = init.body.type;
|
|
||||||
} else if (init.body instanceof FormData) {
|
|
||||||
let boundary;
|
|
||||||
if (headers.has("content-type")) {
|
|
||||||
const params = getHeaderValueParams("content-type");
|
|
||||||
boundary = params.get("boundary")!;
|
|
||||||
}
|
|
||||||
const multipartBuilder = new MultipartBuilder(init.body, boundary);
|
|
||||||
body = multipartBuilder.getBody();
|
|
||||||
contentType = multipartBuilder.getContentType();
|
|
||||||
} else {
|
|
||||||
// TODO: ReadableStream
|
|
||||||
notImplemented();
|
|
||||||
}
|
|
||||||
if (contentType && !headers.has("content-type")) {
|
|
||||||
headers.set("content-type", contentType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
url = input.url;
|
|
||||||
method = input.method;
|
|
||||||
headers = input.headers;
|
|
||||||
|
|
||||||
if (input._bodySource) {
|
|
||||||
body = new DataView(await input.arrayBuffer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let responseBody;
|
|
||||||
let responseInit: domTypes.ResponseInit = {};
|
|
||||||
while (remRedirectCount) {
|
|
||||||
const fetchResponse = await sendFetchReq(url, method, headers, body);
|
|
||||||
|
|
||||||
if (
|
|
||||||
NULL_BODY_STATUS.includes(fetchResponse.status) ||
|
|
||||||
REDIRECT_STATUS.includes(fetchResponse.status)
|
|
||||||
) {
|
|
||||||
// We won't use body of received response, so close it now
|
|
||||||
// otherwise it will be kept in resource table.
|
|
||||||
close(fetchResponse.bodyRid);
|
|
||||||
responseBody = null;
|
|
||||||
} else {
|
|
||||||
responseBody = new ReadableStreamImpl({
|
|
||||||
async pull(controller: ReadableStreamDefaultController): Promise<void> {
|
|
||||||
try {
|
|
||||||
const b = new Uint8Array(1024 * 32);
|
|
||||||
const result = await read(fetchResponse.bodyRid, b);
|
|
||||||
if (result === null) {
|
|
||||||
controller.close();
|
|
||||||
return close(fetchResponse.bodyRid);
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.enqueue(b.subarray(0, result));
|
|
||||||
} catch (e) {
|
|
||||||
controller.error(e);
|
|
||||||
controller.close();
|
|
||||||
close(fetchResponse.bodyRid);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cancel(): void {
|
|
||||||
// When reader.cancel() is called
|
|
||||||
close(fetchResponse.bodyRid);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
responseInit = {
|
|
||||||
status: 200,
|
|
||||||
statusText: fetchResponse.statusText,
|
|
||||||
headers: fetchResponse.headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
responseData.set(responseInit, {
|
|
||||||
redirected,
|
|
||||||
rid: fetchResponse.bodyRid,
|
|
||||||
status: fetchResponse.status,
|
|
||||||
url,
|
|
||||||
});
|
|
||||||
|
|
||||||
const response = new Response(responseBody, responseInit);
|
|
||||||
|
|
||||||
if (REDIRECT_STATUS.includes(fetchResponse.status)) {
|
|
||||||
// We're in a redirect status
|
|
||||||
switch ((init && init.redirect) || "follow") {
|
|
||||||
case "error":
|
|
||||||
responseInit = {};
|
|
||||||
responseData.set(responseInit, {
|
|
||||||
type: "error",
|
|
||||||
redirected: false,
|
|
||||||
url: "",
|
|
||||||
});
|
|
||||||
return new Response(null, responseInit);
|
|
||||||
case "manual":
|
|
||||||
responseInit = {};
|
|
||||||
responseData.set(responseInit, {
|
|
||||||
type: "opaqueredirect",
|
|
||||||
redirected: false,
|
|
||||||
url: "",
|
|
||||||
});
|
|
||||||
return new Response(null, responseInit);
|
|
||||||
case "follow":
|
|
||||||
default:
|
|
||||||
let redirectUrl = response.headers.get("Location");
|
|
||||||
if (redirectUrl == null) {
|
|
||||||
return response; // Unspecified
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
!redirectUrl.startsWith("http://") &&
|
|
||||||
!redirectUrl.startsWith("https://")
|
|
||||||
) {
|
|
||||||
redirectUrl = new URL(redirectUrl, url).href;
|
|
||||||
}
|
|
||||||
url = redirectUrl;
|
|
||||||
redirected = true;
|
|
||||||
remRedirectCount--;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
responseData.set(responseInit, {
|
|
||||||
type: "error",
|
|
||||||
redirected: false,
|
|
||||||
url: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Response(null, responseInit);
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue