mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
refactor: Use ES modules for internal runtime code (#17648)
This PR refactors all internal js files (except core) to be written as ES modules. `__bootstrap`has been mostly replaced with static imports in form in `internal:[path to file from repo root]`. To specify if files are ESM, an `esm` method has been added to `Extension`, similar to the `js` method. A new ModuleLoader called `InternalModuleLoader` has been added to enable the loading of internal specifiers, which is used in all situations except when a snapshot is only loaded, and not a new one is created from it. --------- Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
parent
65500f36e8
commit
b4aa153097
123 changed files with 41574 additions and 41713 deletions
|
@ -10,6 +10,9 @@ use crate::profiling::is_profiling;
|
||||||
pub fn create_js_runtime(setup: impl FnOnce() -> Vec<Extension>) -> JsRuntime {
|
pub fn create_js_runtime(setup: impl FnOnce() -> Vec<Extension>) -> JsRuntime {
|
||||||
JsRuntime::new(RuntimeOptions {
|
JsRuntime::new(RuntimeOptions {
|
||||||
extensions_with_js: setup(),
|
extensions_with_js: setup(),
|
||||||
|
module_loader: Some(std::rc::Rc::new(
|
||||||
|
deno_core::InternalModuleLoader::new(None),
|
||||||
|
)),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
14
cli/build.rs
14
cli/build.rs
|
@ -275,6 +275,7 @@ mod ts {
|
||||||
.build()],
|
.build()],
|
||||||
extensions_with_js: vec![],
|
extensions_with_js: vec![],
|
||||||
additional_files: files,
|
additional_files: files,
|
||||||
|
additional_esm_files: vec![],
|
||||||
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
||||||
vec.extend_from_slice(
|
vec.extend_from_slice(
|
||||||
&zstd::bulk::compress(snapshot_slice, 22)
|
&zstd::bulk::compress(snapshot_slice, 22)
|
||||||
|
@ -306,7 +307,7 @@ mod ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_cli_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
|
fn create_cli_snapshot(snapshot_path: PathBuf, esm_files: Vec<PathBuf>) {
|
||||||
let extensions: Vec<Extension> = vec![
|
let extensions: Vec<Extension> = vec![
|
||||||
deno_webidl::init(),
|
deno_webidl::init(),
|
||||||
deno_console::init(),
|
deno_console::init(),
|
||||||
|
@ -343,7 +344,8 @@ fn create_cli_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
|
||||||
startup_snapshot: Some(deno_runtime::js::deno_isolate_init()),
|
startup_snapshot: Some(deno_runtime::js::deno_isolate_init()),
|
||||||
extensions,
|
extensions,
|
||||||
extensions_with_js: vec![],
|
extensions_with_js: vec![],
|
||||||
additional_files: files,
|
additional_files: vec![],
|
||||||
|
additional_esm_files: esm_files,
|
||||||
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
||||||
lzzzz::lz4_hc::compress_to_vec(
|
lzzzz::lz4_hc::compress_to_vec(
|
||||||
snapshot_slice,
|
snapshot_slice,
|
||||||
|
@ -448,13 +450,13 @@ fn main() {
|
||||||
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||||
|
|
||||||
let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
let compiler_snapshot_path = o.join("COMPILER_SNAPSHOT.bin");
|
||||||
let js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "tsc");
|
let js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "tsc", None);
|
||||||
ts::create_compiler_snapshot(compiler_snapshot_path, js_files, &c);
|
ts::create_compiler_snapshot(compiler_snapshot_path, js_files, &c);
|
||||||
|
|
||||||
let cli_snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
let cli_snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
||||||
let mut js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js");
|
let mut esm_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js", None);
|
||||||
js_files.push(deno_runtime::js::get_99_main());
|
esm_files.push(deno_runtime::js::get_99_main());
|
||||||
create_cli_snapshot(cli_snapshot_path, js_files);
|
create_cli_snapshot(cli_snapshot_path, esm_files);
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
{
|
{
|
||||||
|
|
2593
cli/js/40_testing.js
2593
cli/js/40_testing.js
File diff suppressed because it is too large
Load diff
|
@ -3844,3 +3844,15 @@ itest!(node_prefix_missing {
|
||||||
envs: env_vars_for_npm_tests_no_sync_download(),
|
envs: env_vars_for_npm_tests_no_sync_download(),
|
||||||
exit_code: 1,
|
exit_code: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(internal_import {
|
||||||
|
args: "run run/internal_import.ts",
|
||||||
|
output: "run/internal_import.ts.out",
|
||||||
|
exit_code: 1,
|
||||||
|
});
|
||||||
|
|
||||||
|
itest!(internal_dynamic_import {
|
||||||
|
args: "run run/internal_dynamic_import.ts",
|
||||||
|
output: "run/internal_dynamic_import.ts.out",
|
||||||
|
exit_code: 1,
|
||||||
|
});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
1
|
1
|
||||||
queueMicrotask
|
|
||||||
error: Uncaught Error: bar
|
error: Uncaught Error: bar
|
||||||
throw new Error("bar");
|
throw new Error("bar");
|
||||||
^
|
^
|
||||||
|
|
1
cli/tests/testdata/run/internal_dynamic_import.ts
vendored
Normal file
1
cli/tests/testdata/run/internal_dynamic_import.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
await import("internal:runtime/js/01_build.js");
|
4
cli/tests/testdata/run/internal_dynamic_import.ts.out
vendored
Normal file
4
cli/tests/testdata/run/internal_dynamic_import.ts.out
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
error: Uncaught TypeError: Cannot load internal module from external code
|
||||||
|
await import("internal:runtime/js/01_build.js");
|
||||||
|
^
|
||||||
|
at [WILDCARD]/internal_dynamic_import.ts:1:1
|
1
cli/tests/testdata/run/internal_import.ts
vendored
Normal file
1
cli/tests/testdata/run/internal_import.ts
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
import "internal:runtime/js/01_build.js";
|
8
cli/tests/testdata/run/internal_import.ts.out
vendored
Normal file
8
cli/tests/testdata/run/internal_import.ts.out
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
error: Unsupported scheme "internal" for module "internal:runtime/js/01_build.js". Supported schemes: [
|
||||||
|
"data",
|
||||||
|
"blob",
|
||||||
|
"file",
|
||||||
|
"http",
|
||||||
|
"https",
|
||||||
|
]
|
||||||
|
at [WILDCARD]
|
|
@ -427,6 +427,8 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
ObjectAssign(globalThis.__bootstrap, { core });
|
ObjectAssign(globalThis.__bootstrap, { core });
|
||||||
|
const internals = {};
|
||||||
|
ObjectAssign(globalThis.__bootstrap, { internals });
|
||||||
ObjectAssign(globalThis.Deno, { core });
|
ObjectAssign(globalThis.Deno, { core });
|
||||||
|
|
||||||
// Direct bindings on `globalThis`
|
// Direct bindings on `globalThis`
|
||||||
|
|
|
@ -267,45 +267,47 @@ pub fn host_import_module_dynamically_callback<'s>(
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.to_rust_string_lossy(scope);
|
.to_rust_string_lossy(scope);
|
||||||
|
|
||||||
|
let is_internal_module = specifier_str.starts_with("internal:");
|
||||||
let resolver = v8::PromiseResolver::new(scope).unwrap();
|
let resolver = v8::PromiseResolver::new(scope).unwrap();
|
||||||
let promise = resolver.get_promise(scope);
|
let promise = resolver.get_promise(scope);
|
||||||
|
|
||||||
let assertions = parse_import_assertions(
|
if !is_internal_module {
|
||||||
scope,
|
let assertions = parse_import_assertions(
|
||||||
import_assertions,
|
scope,
|
||||||
ImportAssertionsKind::DynamicImport,
|
import_assertions,
|
||||||
);
|
ImportAssertionsKind::DynamicImport,
|
||||||
|
);
|
||||||
|
|
||||||
{
|
{
|
||||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||||
validate_import_assertions(tc_scope, &assertions);
|
validate_import_assertions(tc_scope, &assertions);
|
||||||
if tc_scope.has_caught() {
|
if tc_scope.has_caught() {
|
||||||
let e = tc_scope.exception().unwrap();
|
let e = tc_scope.exception().unwrap();
|
||||||
resolver.reject(tc_scope, e);
|
resolver.reject(tc_scope, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let asserted_module_type =
|
||||||
|
get_asserted_module_type_from_assertions(&assertions);
|
||||||
|
|
||||||
|
let resolver_handle = v8::Global::new(scope, resolver);
|
||||||
|
{
|
||||||
|
let state_rc = JsRuntime::state(scope);
|
||||||
|
let module_map_rc = JsRuntime::module_map(scope);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"dyn_import specifier {} referrer {} ",
|
||||||
|
specifier_str, referrer_name_str
|
||||||
|
);
|
||||||
|
ModuleMap::load_dynamic_import(
|
||||||
|
module_map_rc,
|
||||||
|
&specifier_str,
|
||||||
|
&referrer_name_str,
|
||||||
|
asserted_module_type,
|
||||||
|
resolver_handle,
|
||||||
|
);
|
||||||
|
state_rc.borrow_mut().notify_new_dynamic_import();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let asserted_module_type =
|
|
||||||
get_asserted_module_type_from_assertions(&assertions);
|
|
||||||
|
|
||||||
let resolver_handle = v8::Global::new(scope, resolver);
|
|
||||||
{
|
|
||||||
let state_rc = JsRuntime::state(scope);
|
|
||||||
let module_map_rc = JsRuntime::module_map(scope);
|
|
||||||
|
|
||||||
debug!(
|
|
||||||
"dyn_import specifier {} referrer {} ",
|
|
||||||
specifier_str, referrer_name_str
|
|
||||||
);
|
|
||||||
ModuleMap::load_dynamic_import(
|
|
||||||
module_map_rc,
|
|
||||||
&specifier_str,
|
|
||||||
&referrer_name_str,
|
|
||||||
asserted_module_type,
|
|
||||||
resolver_handle,
|
|
||||||
);
|
|
||||||
state_rc.borrow_mut().notify_new_dynamic_import();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Map errors from module resolution (not JS errors from module execution) to
|
// Map errors from module resolution (not JS errors from module execution) to
|
||||||
// ones rethrown from this scope, so they include the call stack of the
|
// ones rethrown from this scope, so they include the call stack of the
|
||||||
// dynamic import site. Error objects without any stack frames are assumed to
|
// dynamic import site. Error objects without any stack frames are assumed to
|
||||||
|
@ -317,6 +319,14 @@ pub fn host_import_module_dynamically_callback<'s>(
|
||||||
|
|
||||||
let promise = promise.catch(scope, map_err).unwrap();
|
let promise = promise.catch(scope, map_err).unwrap();
|
||||||
|
|
||||||
|
if is_internal_module {
|
||||||
|
let message =
|
||||||
|
v8::String::new(scope, "Cannot load internal module from external code")
|
||||||
|
.unwrap();
|
||||||
|
let exception = v8::Exception::type_error(scope, message);
|
||||||
|
resolver.reject(scope, exception);
|
||||||
|
}
|
||||||
|
|
||||||
Some(promise)
|
Some(promise)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,7 @@ impl OpDecl {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Extension {
|
pub struct Extension {
|
||||||
js_files: Option<Vec<SourcePair>>,
|
js_files: Option<Vec<SourcePair>>,
|
||||||
|
esm_files: Option<Vec<SourcePair>>,
|
||||||
ops: Option<Vec<OpDecl>>,
|
ops: Option<Vec<OpDecl>>,
|
||||||
opstate_fn: Option<Box<OpStateFn>>,
|
opstate_fn: Option<Box<OpStateFn>>,
|
||||||
middleware_fn: Option<Box<OpMiddlewareFn>>,
|
middleware_fn: Option<Box<OpMiddlewareFn>>,
|
||||||
|
@ -81,13 +82,20 @@ impl Extension {
|
||||||
|
|
||||||
/// returns JS source code to be loaded into the isolate (either at snapshotting,
|
/// returns JS source code to be loaded into the isolate (either at snapshotting,
|
||||||
/// or at startup). as a vector of a tuple of the file name, and the source code.
|
/// or at startup). as a vector of a tuple of the file name, and the source code.
|
||||||
pub fn init_js(&self) -> &[SourcePair] {
|
pub fn get_js_sources(&self) -> &[SourcePair] {
|
||||||
match &self.js_files {
|
match &self.js_files {
|
||||||
Some(files) => files,
|
Some(files) => files,
|
||||||
None => &[],
|
None => &[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_esm_sources(&self) -> &[SourcePair] {
|
||||||
|
match &self.esm_files {
|
||||||
|
Some(files) => files,
|
||||||
|
None => &[],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Called at JsRuntime startup to initialize ops in the isolate.
|
/// Called at JsRuntime startup to initialize ops in the isolate.
|
||||||
pub fn init_ops(&mut self) -> Option<Vec<OpDecl>> {
|
pub fn init_ops(&mut self) -> Option<Vec<OpDecl>> {
|
||||||
// TODO(@AaronO): maybe make op registration idempotent
|
// TODO(@AaronO): maybe make op registration idempotent
|
||||||
|
@ -145,6 +153,7 @@ impl Extension {
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct ExtensionBuilder {
|
pub struct ExtensionBuilder {
|
||||||
js: Vec<SourcePair>,
|
js: Vec<SourcePair>,
|
||||||
|
esm: Vec<SourcePair>,
|
||||||
ops: Vec<OpDecl>,
|
ops: Vec<OpDecl>,
|
||||||
state: Option<Box<OpStateFn>>,
|
state: Option<Box<OpStateFn>>,
|
||||||
middleware: Option<Box<OpMiddlewareFn>>,
|
middleware: Option<Box<OpMiddlewareFn>>,
|
||||||
|
@ -164,6 +173,11 @@ impl ExtensionBuilder {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn esm(&mut self, js_files: Vec<SourcePair>) -> &mut Self {
|
||||||
|
self.esm.extend(js_files);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn ops(&mut self, ops: Vec<OpDecl>) -> &mut Self {
|
pub fn ops(&mut self, ops: Vec<OpDecl>) -> &mut Self {
|
||||||
self.ops.extend(ops);
|
self.ops.extend(ops);
|
||||||
self
|
self
|
||||||
|
@ -195,10 +209,12 @@ impl ExtensionBuilder {
|
||||||
|
|
||||||
pub fn build(&mut self) -> Extension {
|
pub fn build(&mut self) -> Extension {
|
||||||
let js_files = Some(std::mem::take(&mut self.js));
|
let js_files = Some(std::mem::take(&mut self.js));
|
||||||
|
let esm_files = Some(std::mem::take(&mut self.esm));
|
||||||
let ops = Some(std::mem::take(&mut self.ops));
|
let ops = Some(std::mem::take(&mut self.ops));
|
||||||
let deps = Some(std::mem::take(&mut self.deps));
|
let deps = Some(std::mem::take(&mut self.deps));
|
||||||
Extension {
|
Extension {
|
||||||
js_files,
|
js_files,
|
||||||
|
esm_files,
|
||||||
ops,
|
ops,
|
||||||
opstate_fn: self.state.take(),
|
opstate_fn: self.state.take(),
|
||||||
middleware_fn: self.middleware.take(),
|
middleware_fn: self.middleware.take(),
|
||||||
|
|
|
@ -73,6 +73,7 @@ pub use crate::module_specifier::ModuleResolutionError;
|
||||||
pub use crate::module_specifier::ModuleSpecifier;
|
pub use crate::module_specifier::ModuleSpecifier;
|
||||||
pub use crate::module_specifier::DUMMY_SPECIFIER;
|
pub use crate::module_specifier::DUMMY_SPECIFIER;
|
||||||
pub use crate::modules::FsModuleLoader;
|
pub use crate::modules::FsModuleLoader;
|
||||||
|
pub use crate::modules::InternalModuleLoader;
|
||||||
pub use crate::modules::ModuleId;
|
pub use crate::modules::ModuleId;
|
||||||
pub use crate::modules::ModuleLoader;
|
pub use crate::modules::ModuleLoader;
|
||||||
pub use crate::modules::ModuleSource;
|
pub use crate::modules::ModuleSource;
|
||||||
|
|
|
@ -292,6 +292,69 @@ impl ModuleLoader for NoopModuleLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct InternalModuleLoader(Rc<dyn ModuleLoader>);
|
||||||
|
|
||||||
|
impl InternalModuleLoader {
|
||||||
|
pub fn new(module_loader: Option<Rc<dyn ModuleLoader>>) -> Self {
|
||||||
|
InternalModuleLoader(
|
||||||
|
module_loader.unwrap_or_else(|| Rc::new(NoopModuleLoader)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleLoader for InternalModuleLoader {
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
specifier: &str,
|
||||||
|
referrer: &str,
|
||||||
|
kind: ResolutionKind,
|
||||||
|
) -> Result<ModuleSpecifier, Error> {
|
||||||
|
if let Ok(url_specifier) = ModuleSpecifier::parse(specifier) {
|
||||||
|
if url_specifier.scheme() == "internal" {
|
||||||
|
let referrer_specifier = ModuleSpecifier::parse(referrer).ok();
|
||||||
|
if referrer == "." || referrer_specifier.unwrap().scheme() == "internal"
|
||||||
|
{
|
||||||
|
return Ok(url_specifier);
|
||||||
|
} else {
|
||||||
|
return Err(generic_error(
|
||||||
|
"Cannot load internal module from external code",
|
||||||
|
));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.0.resolve(specifier, referrer, kind)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(
|
||||||
|
&self,
|
||||||
|
module_specifier: &ModuleSpecifier,
|
||||||
|
maybe_referrer: Option<ModuleSpecifier>,
|
||||||
|
is_dyn_import: bool,
|
||||||
|
) -> Pin<Box<ModuleSourceFuture>> {
|
||||||
|
self.0.load(module_specifier, maybe_referrer, is_dyn_import)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prepare_load(
|
||||||
|
&self,
|
||||||
|
op_state: Rc<RefCell<OpState>>,
|
||||||
|
module_specifier: &ModuleSpecifier,
|
||||||
|
maybe_referrer: Option<String>,
|
||||||
|
is_dyn_import: bool,
|
||||||
|
) -> Pin<Box<dyn Future<Output = Result<(), Error>>>> {
|
||||||
|
if module_specifier.scheme() == "internal" {
|
||||||
|
return async { Ok(()) }.boxed_local();
|
||||||
|
}
|
||||||
|
|
||||||
|
self.0.prepare_load(
|
||||||
|
op_state,
|
||||||
|
module_specifier,
|
||||||
|
maybe_referrer,
|
||||||
|
is_dyn_import,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Basic file system module loader.
|
/// Basic file system module loader.
|
||||||
///
|
///
|
||||||
/// Note that this loader will **block** event loop
|
/// Note that this loader will **block** event loop
|
||||||
|
@ -2508,4 +2571,33 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn internal_module_loader() {
|
||||||
|
let loader = InternalModuleLoader::new(None);
|
||||||
|
assert!(loader
|
||||||
|
.resolve("internal:foo", "internal:bar", ResolutionKind::Import)
|
||||||
|
.is_ok());
|
||||||
|
assert_eq!(
|
||||||
|
loader
|
||||||
|
.resolve("internal:foo", "file://bar", ResolutionKind::Import)
|
||||||
|
.err()
|
||||||
|
.map(|e| e.to_string()),
|
||||||
|
Some("Cannot load internal module from external code".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loader
|
||||||
|
.resolve("file://foo", "file://bar", ResolutionKind::Import)
|
||||||
|
.err()
|
||||||
|
.map(|e| e.to_string()),
|
||||||
|
Some("Module loading is not supported".to_string())
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
loader
|
||||||
|
.resolve("file://foo", "internal:bar", ResolutionKind::Import)
|
||||||
|
.err()
|
||||||
|
.map(|e| e.to_string()),
|
||||||
|
Some("Module loading is not supported".to_string())
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,17 +13,18 @@ use crate::modules::ModuleId;
|
||||||
use crate::modules::ModuleLoadId;
|
use crate::modules::ModuleLoadId;
|
||||||
use crate::modules::ModuleLoader;
|
use crate::modules::ModuleLoader;
|
||||||
use crate::modules::ModuleMap;
|
use crate::modules::ModuleMap;
|
||||||
use crate::modules::NoopModuleLoader;
|
|
||||||
use crate::op_void_async;
|
use crate::op_void_async;
|
||||||
use crate::op_void_sync;
|
use crate::op_void_sync;
|
||||||
use crate::ops::*;
|
use crate::ops::*;
|
||||||
use crate::source_map::SourceMapCache;
|
use crate::source_map::SourceMapCache;
|
||||||
use crate::source_map::SourceMapGetter;
|
use crate::source_map::SourceMapGetter;
|
||||||
use crate::Extension;
|
use crate::Extension;
|
||||||
|
use crate::NoopModuleLoader;
|
||||||
use crate::OpMiddlewareFn;
|
use crate::OpMiddlewareFn;
|
||||||
use crate::OpResult;
|
use crate::OpResult;
|
||||||
use crate::OpState;
|
use crate::OpState;
|
||||||
use crate::PromiseId;
|
use crate::PromiseId;
|
||||||
|
use anyhow::Context as AnyhowContext;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use futures::future::poll_fn;
|
use futures::future::poll_fn;
|
||||||
|
@ -605,9 +606,16 @@ impl JsRuntime {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let loader = options
|
let loader = if snapshot_options != SnapshotOptions::Load {
|
||||||
.module_loader
|
Rc::new(crate::modules::InternalModuleLoader::new(
|
||||||
.unwrap_or_else(|| Rc::new(NoopModuleLoader));
|
options.module_loader,
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
options
|
||||||
|
.module_loader
|
||||||
|
.unwrap_or_else(|| Rc::new(NoopModuleLoader))
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
let mut state = state_rc.borrow_mut();
|
let mut state = state_rc.borrow_mut();
|
||||||
state.global_realm = Some(JsRealm(global_context.clone()));
|
state.global_realm = Some(JsRealm(global_context.clone()));
|
||||||
|
@ -805,10 +813,30 @@ impl JsRuntime {
|
||||||
// Take extensions to avoid double-borrow
|
// Take extensions to avoid double-borrow
|
||||||
let extensions = std::mem::take(&mut self.extensions_with_js);
|
let extensions = std::mem::take(&mut self.extensions_with_js);
|
||||||
for ext in &extensions {
|
for ext in &extensions {
|
||||||
let js_files = ext.init_js();
|
{
|
||||||
for (filename, source) in js_files {
|
let js_files = ext.get_esm_sources();
|
||||||
// TODO(@AaronO): use JsRuntime::execute_static() here to move src off heap
|
for (filename, source) in js_files {
|
||||||
realm.execute_script(self.v8_isolate(), filename, source)?;
|
futures::executor::block_on(async {
|
||||||
|
let id = self
|
||||||
|
.load_side_module(
|
||||||
|
&ModuleSpecifier::parse(filename)?,
|
||||||
|
Some(source.to_string()),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let receiver = self.mod_evaluate(id);
|
||||||
|
self.run_event_loop(false).await?;
|
||||||
|
receiver.await?
|
||||||
|
})
|
||||||
|
.with_context(|| format!("Couldn't execute '{filename}'"))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let js_files = ext.get_js_sources();
|
||||||
|
for (filename, source) in js_files {
|
||||||
|
// TODO(@AaronO): use JsRuntime::execute_static() here to move src off heap
|
||||||
|
realm.execute_script(self.v8_isolate(), filename, source)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Restore extensions
|
// Restore extensions
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
|
use anyhow::Context;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use crate::Extension;
|
use crate::Extension;
|
||||||
use crate::JsRuntime;
|
use crate::JsRuntime;
|
||||||
|
use crate::ModuleSpecifier;
|
||||||
use crate::RuntimeOptions;
|
use crate::RuntimeOptions;
|
||||||
use crate::Snapshot;
|
use crate::Snapshot;
|
||||||
|
|
||||||
|
@ -17,6 +19,7 @@ pub struct CreateSnapshotOptions {
|
||||||
pub extensions: Vec<Extension>,
|
pub extensions: Vec<Extension>,
|
||||||
pub extensions_with_js: Vec<Extension>,
|
pub extensions_with_js: Vec<Extension>,
|
||||||
pub additional_files: Vec<PathBuf>,
|
pub additional_files: Vec<PathBuf>,
|
||||||
|
pub additional_esm_files: Vec<PathBuf>,
|
||||||
pub compression_cb: Option<Box<CompressionCb>>,
|
pub compression_cb: Option<Box<CompressionCb>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,6 +47,27 @@ pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
for file in create_snapshot_options.additional_esm_files {
|
||||||
|
let display_path = file.strip_prefix(display_root).unwrap_or(&file);
|
||||||
|
let display_path_str = display_path.display().to_string();
|
||||||
|
|
||||||
|
let filename =
|
||||||
|
&("internal:".to_string() + &display_path_str.replace('\\', "/"));
|
||||||
|
|
||||||
|
futures::executor::block_on(async {
|
||||||
|
let id = js_runtime
|
||||||
|
.load_side_module(
|
||||||
|
&ModuleSpecifier::parse(filename)?,
|
||||||
|
Some(std::fs::read_to_string(&file)?),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let receiver = js_runtime.mod_evaluate(id);
|
||||||
|
js_runtime.run_event_loop(false).await?;
|
||||||
|
receiver.await?
|
||||||
|
})
|
||||||
|
.with_context(|| format!("Couldn't execute '{}'", file.display()))
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
let snapshot = js_runtime.snapshot();
|
let snapshot = js_runtime.snapshot();
|
||||||
let snapshot_slice: &[u8] = &snapshot;
|
let snapshot_slice: &[u8] = &snapshot;
|
||||||
|
@ -79,9 +103,12 @@ pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type FilterFn = Box<dyn Fn(&PathBuf) -> bool>;
|
||||||
|
|
||||||
pub fn get_js_files(
|
pub fn get_js_files(
|
||||||
cargo_manifest_dir: &'static str,
|
cargo_manifest_dir: &'static str,
|
||||||
directory: &str,
|
directory: &str,
|
||||||
|
filter: Option<FilterFn>,
|
||||||
) -> Vec<PathBuf> {
|
) -> Vec<PathBuf> {
|
||||||
let manifest_dir = Path::new(cargo_manifest_dir);
|
let manifest_dir = Path::new(cargo_manifest_dir);
|
||||||
let mut js_files = std::fs::read_dir(directory)
|
let mut js_files = std::fs::read_dir(directory)
|
||||||
|
@ -92,7 +119,7 @@ pub fn get_js_files(
|
||||||
})
|
})
|
||||||
.filter(|path| {
|
.filter(|path| {
|
||||||
path.extension().unwrap_or_default() == "js"
|
path.extension().unwrap_or_default() == "js"
|
||||||
&& !path.ends_with("99_main.js")
|
&& filter.as_ref().map(|filter| filter(path)).unwrap_or(true)
|
||||||
})
|
})
|
||||||
.collect::<Vec<PathBuf>>();
|
.collect::<Vec<PathBuf>>();
|
||||||
js_files.sort();
|
js_files.sort();
|
||||||
|
|
|
@ -2,150 +2,149 @@
|
||||||
|
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import {
|
||||||
|
defineEventHandler,
|
||||||
|
EventTarget,
|
||||||
|
setTarget,
|
||||||
|
} from "internal:ext/web/02_event.js";
|
||||||
|
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeIndexOf,
|
||||||
|
ArrayPrototypeSplice,
|
||||||
|
ArrayPrototypePush,
|
||||||
|
Symbol,
|
||||||
|
Uint8Array,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
const _name = Symbol("[[name]]");
|
||||||
const core = window.Deno.core;
|
const _closed = Symbol("[[closed]]");
|
||||||
const ops = core.ops;
|
|
||||||
const webidl = window.__bootstrap.webidl;
|
|
||||||
const { MessageEvent, defineEventHandler, setTarget } =
|
|
||||||
window.__bootstrap.event;
|
|
||||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
|
||||||
const {
|
|
||||||
ArrayPrototypeIndexOf,
|
|
||||||
ArrayPrototypeSplice,
|
|
||||||
ArrayPrototypePush,
|
|
||||||
Symbol,
|
|
||||||
Uint8Array,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
const _name = Symbol("[[name]]");
|
const channels = [];
|
||||||
const _closed = Symbol("[[closed]]");
|
let rid = null;
|
||||||
|
|
||||||
const channels = [];
|
async function recv() {
|
||||||
let rid = null;
|
while (channels.length > 0) {
|
||||||
|
const message = await core.opAsync("op_broadcast_recv", rid);
|
||||||
|
|
||||||
async function recv() {
|
if (message === null) {
|
||||||
while (channels.length > 0) {
|
break;
|
||||||
const message = await core.opAsync("op_broadcast_recv", rid);
|
|
||||||
|
|
||||||
if (message === null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { 0: name, 1: data } = message;
|
|
||||||
dispatch(null, name, new Uint8Array(data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
core.close(rid);
|
const { 0: name, 1: data } = message;
|
||||||
rid = null;
|
dispatch(null, name, new Uint8Array(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
function dispatch(source, name, data) {
|
core.close(rid);
|
||||||
for (let i = 0; i < channels.length; ++i) {
|
rid = null;
|
||||||
const channel = channels[i];
|
}
|
||||||
|
|
||||||
if (channel === source) continue; // Don't self-send.
|
function dispatch(source, name, data) {
|
||||||
if (channel[_name] !== name) continue;
|
for (let i = 0; i < channels.length; ++i) {
|
||||||
if (channel[_closed]) continue;
|
const channel = channels[i];
|
||||||
|
|
||||||
const go = () => {
|
if (channel === source) continue; // Don't self-send.
|
||||||
if (channel[_closed]) return;
|
if (channel[_name] !== name) continue;
|
||||||
const event = new MessageEvent("message", {
|
if (channel[_closed]) continue;
|
||||||
data: core.deserialize(data), // TODO(bnoordhuis) Cache immutables.
|
|
||||||
origin: "http://127.0.0.1",
|
|
||||||
});
|
|
||||||
setTarget(event, channel);
|
|
||||||
channel.dispatchEvent(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
defer(go);
|
const go = () => {
|
||||||
}
|
if (channel[_closed]) return;
|
||||||
}
|
const event = new MessageEvent("message", {
|
||||||
|
data: core.deserialize(data), // TODO(bnoordhuis) Cache immutables.
|
||||||
// Defer to avoid starving the event loop. Not using queueMicrotask()
|
origin: "http://127.0.0.1",
|
||||||
// for that reason: it lets promises make forward progress but can
|
|
||||||
// still starve other parts of the event loop.
|
|
||||||
function defer(go) {
|
|
||||||
setTimeout(go, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
class BroadcastChannel extends EventTarget {
|
|
||||||
[_name];
|
|
||||||
[_closed] = false;
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this[_name];
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(name) {
|
|
||||||
super();
|
|
||||||
|
|
||||||
const prefix = "Failed to construct 'BroadcastChannel'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
|
|
||||||
this[_name] = webidl.converters["DOMString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
});
|
||||||
|
setTarget(event, channel);
|
||||||
|
channel.dispatchEvent(event);
|
||||||
|
};
|
||||||
|
|
||||||
this[webidl.brand] = webidl.brand;
|
defer(go);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ArrayPrototypePush(channels, this);
|
// Defer to avoid starving the event loop. Not using queueMicrotask()
|
||||||
|
// for that reason: it lets promises make forward progress but can
|
||||||
|
// still starve other parts of the event loop.
|
||||||
|
function defer(go) {
|
||||||
|
setTimeout(go, 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (rid === null) {
|
class BroadcastChannel extends EventTarget {
|
||||||
// Create the rid immediately, otherwise there is a time window (and a
|
[_name];
|
||||||
// race condition) where messages can get lost, because recv() is async.
|
[_closed] = false;
|
||||||
rid = ops.op_broadcast_subscribe();
|
|
||||||
recv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
postMessage(message) {
|
get name() {
|
||||||
webidl.assertBranded(this, BroadcastChannelPrototype);
|
return this[_name];
|
||||||
|
}
|
||||||
|
|
||||||
const prefix = "Failed to execute 'postMessage' on 'BroadcastChannel'";
|
constructor(name) {
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
super();
|
||||||
|
|
||||||
if (this[_closed]) {
|
const prefix = "Failed to construct 'BroadcastChannel'";
|
||||||
throw new DOMException("Already closed", "InvalidStateError");
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof message === "function" || typeof message === "symbol") {
|
this[_name] = webidl.converters["DOMString"](name, {
|
||||||
throw new DOMException("Uncloneable value", "DataCloneError");
|
prefix,
|
||||||
}
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
const data = core.serialize(message);
|
this[webidl.brand] = webidl.brand;
|
||||||
|
|
||||||
// Send to other listeners in this VM.
|
ArrayPrototypePush(channels, this);
|
||||||
dispatch(this, this[_name], new Uint8Array(data));
|
|
||||||
|
|
||||||
// Send to listeners in other VMs.
|
if (rid === null) {
|
||||||
defer(() => {
|
// Create the rid immediately, otherwise there is a time window (and a
|
||||||
if (!this[_closed]) {
|
// race condition) where messages can get lost, because recv() is async.
|
||||||
core.opAsync("op_broadcast_send", rid, this[_name], data);
|
rid = ops.op_broadcast_subscribe();
|
||||||
}
|
recv();
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
webidl.assertBranded(this, BroadcastChannelPrototype);
|
|
||||||
this[_closed] = true;
|
|
||||||
|
|
||||||
const index = ArrayPrototypeIndexOf(channels, this);
|
|
||||||
if (index === -1) return;
|
|
||||||
|
|
||||||
ArrayPrototypeSplice(channels, index, 1);
|
|
||||||
if (channels.length === 0) {
|
|
||||||
ops.op_broadcast_unsubscribe(rid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
defineEventHandler(BroadcastChannel.prototype, "message");
|
postMessage(message) {
|
||||||
defineEventHandler(BroadcastChannel.prototype, "messageerror");
|
webidl.assertBranded(this, BroadcastChannelPrototype);
|
||||||
const BroadcastChannelPrototype = BroadcastChannel.prototype;
|
|
||||||
|
|
||||||
window.__bootstrap.broadcastChannel = { BroadcastChannel };
|
const prefix = "Failed to execute 'postMessage' on 'BroadcastChannel'";
|
||||||
})(this);
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
|
||||||
|
if (this[_closed]) {
|
||||||
|
throw new DOMException("Already closed", "InvalidStateError");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof message === "function" || typeof message === "symbol") {
|
||||||
|
throw new DOMException("Uncloneable value", "DataCloneError");
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = core.serialize(message);
|
||||||
|
|
||||||
|
// Send to other listeners in this VM.
|
||||||
|
dispatch(this, this[_name], new Uint8Array(data));
|
||||||
|
|
||||||
|
// Send to listeners in other VMs.
|
||||||
|
defer(() => {
|
||||||
|
if (!this[_closed]) {
|
||||||
|
core.opAsync("op_broadcast_send", rid, this[_name], data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
webidl.assertBranded(this, BroadcastChannelPrototype);
|
||||||
|
this[_closed] = true;
|
||||||
|
|
||||||
|
const index = ArrayPrototypeIndexOf(channels, this);
|
||||||
|
if (index === -1) return;
|
||||||
|
|
||||||
|
ArrayPrototypeSplice(channels, index, 1);
|
||||||
|
if (channels.length === 0) {
|
||||||
|
ops.op_broadcast_unsubscribe(rid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineEventHandler(BroadcastChannel.prototype, "message");
|
||||||
|
defineEventHandler(BroadcastChannel.prototype, "messageerror");
|
||||||
|
const BroadcastChannelPrototype = BroadcastChannel.prototype;
|
||||||
|
|
||||||
|
export { BroadcastChannel };
|
||||||
|
|
|
@ -112,7 +112,7 @@ pub fn init<BC: BroadcastChannel + 'static>(
|
||||||
) -> Extension {
|
) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl", "deno_web"])
|
.dependencies(vec!["deno_webidl", "deno_web"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/broadcast_channel",
|
prefix "internal:ext/broadcast_channel",
|
||||||
"01_broadcast_channel.js",
|
"01_broadcast_channel.js",
|
||||||
))
|
))
|
||||||
|
|
530
ext/cache/01_cache.js
vendored
530
ext/cache/01_cache.js
vendored
|
@ -1,296 +1,292 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.__bootstrap.core;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = window.__bootstrap.webidl;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
Symbol,
|
Symbol,
|
||||||
TypeError,
|
TypeError,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
const {
|
import {
|
||||||
Request,
|
Request,
|
||||||
toInnerResponse,
|
RequestPrototype,
|
||||||
toInnerRequest,
|
toInnerRequest,
|
||||||
} = window.__bootstrap.fetch;
|
} from "internal:ext/fetch/23_request.js";
|
||||||
const { URLPrototype } = window.__bootstrap.url;
|
import { toInnerResponse } from "internal:ext/fetch/23_response.js";
|
||||||
const RequestPrototype = Request.prototype;
|
import { URLPrototype } from "internal:ext/url/00_url.js";
|
||||||
const { getHeader } = window.__bootstrap.headers;
|
import { getHeader } from "internal:ext/fetch/20_headers.js";
|
||||||
const { readableStreamForRid } = window.__bootstrap.streams;
|
import { readableStreamForRid } from "internal:ext/web/06_streams.js";
|
||||||
|
|
||||||
class CacheStorage {
|
class CacheStorage {
|
||||||
constructor() {
|
constructor() {
|
||||||
webidl.illegalConstructor();
|
webidl.illegalConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
async open(cacheName) {
|
||||||
|
webidl.assertBranded(this, CacheStoragePrototype);
|
||||||
|
const prefix = "Failed to execute 'open' on 'CacheStorage'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
cacheName = webidl.converters["DOMString"](cacheName, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
const cacheId = await core.opAsync("op_cache_storage_open", cacheName);
|
||||||
|
const cache = webidl.createBranded(Cache);
|
||||||
|
cache[_id] = cacheId;
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
async has(cacheName) {
|
||||||
|
webidl.assertBranded(this, CacheStoragePrototype);
|
||||||
|
const prefix = "Failed to execute 'has' on 'CacheStorage'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
cacheName = webidl.converters["DOMString"](cacheName, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
return await core.opAsync("op_cache_storage_has", cacheName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(cacheName) {
|
||||||
|
webidl.assertBranded(this, CacheStoragePrototype);
|
||||||
|
const prefix = "Failed to execute 'delete' on 'CacheStorage'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
cacheName = webidl.converters["DOMString"](cacheName, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
return await core.opAsync("op_cache_storage_delete", cacheName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const _matchAll = Symbol("[[matchAll]]");
|
||||||
|
const _id = Symbol("id");
|
||||||
|
|
||||||
|
class Cache {
|
||||||
|
/** @type {number} */
|
||||||
|
[_id];
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
webidl.illegalConstructor();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** See https://w3c.github.io/ServiceWorker/#dom-cache-put */
|
||||||
|
async put(request, response) {
|
||||||
|
webidl.assertBranded(this, CachePrototype);
|
||||||
|
const prefix = "Failed to execute 'put' on 'Cache'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
request = webidl.converters["RequestInfo_DOMString"](request, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
response = webidl.converters["Response"](response, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
// Step 1.
|
||||||
|
let innerRequest = null;
|
||||||
|
// Step 2.
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) {
|
||||||
|
innerRequest = toInnerRequest(request);
|
||||||
|
} else {
|
||||||
|
// Step 3.
|
||||||
|
innerRequest = toInnerRequest(new Request(request));
|
||||||
|
}
|
||||||
|
// Step 4.
|
||||||
|
const reqUrl = new URL(innerRequest.url());
|
||||||
|
if (reqUrl.protocol !== "http:" && reqUrl.protocol !== "https:") {
|
||||||
|
throw new TypeError(
|
||||||
|
"Request url protocol must be 'http:' or 'https:'",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (innerRequest.method !== "GET") {
|
||||||
|
throw new TypeError("Request method must be GET");
|
||||||
|
}
|
||||||
|
// Step 5.
|
||||||
|
const innerResponse = toInnerResponse(response);
|
||||||
|
// Step 6.
|
||||||
|
if (innerResponse.status === 206) {
|
||||||
|
throw new TypeError("Response status must not be 206");
|
||||||
|
}
|
||||||
|
// Step 7.
|
||||||
|
const varyHeader = getHeader(innerResponse.headerList, "vary");
|
||||||
|
if (varyHeader) {
|
||||||
|
const fieldValues = varyHeader.split(",");
|
||||||
|
for (let i = 0; i < fieldValues.length; ++i) {
|
||||||
|
const field = fieldValues[i];
|
||||||
|
if (field.trim() === "*") {
|
||||||
|
throw new TypeError("Vary header must not contain '*'");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async open(cacheName) {
|
// Step 8.
|
||||||
webidl.assertBranded(this, CacheStoragePrototype);
|
if (innerResponse.body !== null && innerResponse.body.unusable()) {
|
||||||
const prefix = "Failed to execute 'open' on 'CacheStorage'";
|
throw new TypeError("Response body is already used");
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
cacheName = webidl.converters["DOMString"](cacheName, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
const cacheId = await core.opAsync("op_cache_storage_open", cacheName);
|
|
||||||
const cache = webidl.createBranded(Cache);
|
|
||||||
cache[_id] = cacheId;
|
|
||||||
return cache;
|
|
||||||
}
|
}
|
||||||
|
// acquire lock before async op
|
||||||
|
const reader = innerResponse.body?.stream.getReader();
|
||||||
|
|
||||||
async has(cacheName) {
|
// Remove fragment from request URL before put.
|
||||||
webidl.assertBranded(this, CacheStoragePrototype);
|
reqUrl.hash = "";
|
||||||
const prefix = "Failed to execute 'has' on 'CacheStorage'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
// Step 9-11.
|
||||||
cacheName = webidl.converters["DOMString"](cacheName, {
|
const rid = await core.opAsync(
|
||||||
prefix,
|
"op_cache_put",
|
||||||
context: "Argument 1",
|
{
|
||||||
});
|
cacheId: this[_id],
|
||||||
return await core.opAsync("op_cache_storage_has", cacheName);
|
requestUrl: reqUrl.toString(),
|
||||||
|
responseHeaders: innerResponse.headerList,
|
||||||
|
requestHeaders: innerRequest.headerList,
|
||||||
|
responseHasBody: innerResponse.body !== null,
|
||||||
|
responseStatus: innerResponse.status,
|
||||||
|
responseStatusText: innerResponse.statusMessage,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (reader) {
|
||||||
|
try {
|
||||||
|
while (true) {
|
||||||
|
const { value, done } = await reader.read();
|
||||||
|
if (done) {
|
||||||
|
await core.shutdown(rid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
await core.writeAll(rid, value);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
core.close(rid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// Step 12-19: TODO(@satyarohith): do the insertion in background.
|
||||||
|
}
|
||||||
|
|
||||||
async delete(cacheName) {
|
/** See https://w3c.github.io/ServiceWorker/#cache-match */
|
||||||
webidl.assertBranded(this, CacheStoragePrototype);
|
async match(request, options) {
|
||||||
const prefix = "Failed to execute 'delete' on 'CacheStorage'";
|
webidl.assertBranded(this, CachePrototype);
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
const prefix = "Failed to execute 'match' on 'Cache'";
|
||||||
cacheName = webidl.converters["DOMString"](cacheName, {
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
prefix,
|
request = webidl.converters["RequestInfo_DOMString"](request, {
|
||||||
context: "Argument 1",
|
prefix,
|
||||||
});
|
context: "Argument 1",
|
||||||
return await core.opAsync("op_cache_storage_delete", cacheName);
|
});
|
||||||
|
const p = await this[_matchAll](request, options);
|
||||||
|
if (p.length > 0) {
|
||||||
|
return p[0];
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const _matchAll = Symbol("[[matchAll]]");
|
/** See https://w3c.github.io/ServiceWorker/#cache-delete */
|
||||||
const _id = Symbol("id");
|
async delete(request, _options) {
|
||||||
|
webidl.assertBranded(this, CachePrototype);
|
||||||
|
const prefix = "Failed to execute 'delete' on 'Cache'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
request = webidl.converters["RequestInfo_DOMString"](request, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
// Step 1.
|
||||||
|
let r = null;
|
||||||
|
// Step 2.
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) {
|
||||||
|
r = request;
|
||||||
|
if (request.method !== "GET") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
typeof request === "string" ||
|
||||||
|
ObjectPrototypeIsPrototypeOf(URLPrototype, request)
|
||||||
|
) {
|
||||||
|
r = new Request(request);
|
||||||
|
}
|
||||||
|
return await core.opAsync("op_cache_delete", {
|
||||||
|
cacheId: this[_id],
|
||||||
|
requestUrl: r.url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
class Cache {
|
/** See https://w3c.github.io/ServiceWorker/#cache-matchall
|
||||||
/** @type {number} */
|
*
|
||||||
[_id];
|
* Note: the function is private as we don't want to expose
|
||||||
|
* this API to the public yet.
|
||||||
constructor() {
|
*
|
||||||
webidl.illegalConstructor();
|
* The function will return an array of responses.
|
||||||
|
*/
|
||||||
|
async [_matchAll](request, _options) {
|
||||||
|
// Step 1.
|
||||||
|
let r = null;
|
||||||
|
// Step 2.
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) {
|
||||||
|
r = request;
|
||||||
|
if (request.method !== "GET") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
typeof request === "string" ||
|
||||||
|
ObjectPrototypeIsPrototypeOf(URLPrototype, request)
|
||||||
|
) {
|
||||||
|
r = new Request(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** See https://w3c.github.io/ServiceWorker/#dom-cache-put */
|
// Step 5.
|
||||||
async put(request, response) {
|
const responses = [];
|
||||||
webidl.assertBranded(this, CachePrototype);
|
// Step 5.2
|
||||||
const prefix = "Failed to execute 'put' on 'Cache'";
|
if (r === null) {
|
||||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
// Step 5.3
|
||||||
request = webidl.converters["RequestInfo_DOMString"](request, {
|
// Note: we have to return all responses in the cache when
|
||||||
prefix,
|
// the request is null.
|
||||||
context: "Argument 1",
|
// We deviate from the spec here and return an empty array
|
||||||
});
|
// as we don't expose matchAll() API.
|
||||||
response = webidl.converters["Response"](response, {
|
return responses;
|
||||||
prefix,
|
} else {
|
||||||
context: "Argument 2",
|
// Remove the fragment from the request URL.
|
||||||
});
|
const url = new URL(r.url);
|
||||||
// Step 1.
|
url.hash = "";
|
||||||
let innerRequest = null;
|
const innerRequest = toInnerRequest(r);
|
||||||
// Step 2.
|
const matchResult = await core.opAsync(
|
||||||
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) {
|
"op_cache_match",
|
||||||
innerRequest = toInnerRequest(request);
|
|
||||||
} else {
|
|
||||||
// Step 3.
|
|
||||||
innerRequest = toInnerRequest(new Request(request));
|
|
||||||
}
|
|
||||||
// Step 4.
|
|
||||||
const reqUrl = new URL(innerRequest.url());
|
|
||||||
if (reqUrl.protocol !== "http:" && reqUrl.protocol !== "https:") {
|
|
||||||
throw new TypeError(
|
|
||||||
"Request url protocol must be 'http:' or 'https:'",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (innerRequest.method !== "GET") {
|
|
||||||
throw new TypeError("Request method must be GET");
|
|
||||||
}
|
|
||||||
// Step 5.
|
|
||||||
const innerResponse = toInnerResponse(response);
|
|
||||||
// Step 6.
|
|
||||||
if (innerResponse.status === 206) {
|
|
||||||
throw new TypeError("Response status must not be 206");
|
|
||||||
}
|
|
||||||
// Step 7.
|
|
||||||
const varyHeader = getHeader(innerResponse.headerList, "vary");
|
|
||||||
if (varyHeader) {
|
|
||||||
const fieldValues = varyHeader.split(",");
|
|
||||||
for (let i = 0; i < fieldValues.length; ++i) {
|
|
||||||
const field = fieldValues[i];
|
|
||||||
if (field.trim() === "*") {
|
|
||||||
throw new TypeError("Vary header must not contain '*'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 8.
|
|
||||||
if (innerResponse.body !== null && innerResponse.body.unusable()) {
|
|
||||||
throw new TypeError("Response body is already used");
|
|
||||||
}
|
|
||||||
// acquire lock before async op
|
|
||||||
const reader = innerResponse.body?.stream.getReader();
|
|
||||||
|
|
||||||
// Remove fragment from request URL before put.
|
|
||||||
reqUrl.hash = "";
|
|
||||||
|
|
||||||
// Step 9-11.
|
|
||||||
const rid = await core.opAsync(
|
|
||||||
"op_cache_put",
|
|
||||||
{
|
{
|
||||||
cacheId: this[_id],
|
cacheId: this[_id],
|
||||||
requestUrl: reqUrl.toString(),
|
requestUrl: url.toString(),
|
||||||
responseHeaders: innerResponse.headerList,
|
|
||||||
requestHeaders: innerRequest.headerList,
|
requestHeaders: innerRequest.headerList,
|
||||||
responseHasBody: innerResponse.body !== null,
|
|
||||||
responseStatus: innerResponse.status,
|
|
||||||
responseStatusText: innerResponse.statusMessage,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (reader) {
|
if (matchResult) {
|
||||||
try {
|
const { 0: meta, 1: responseBodyRid } = matchResult;
|
||||||
while (true) {
|
let body = null;
|
||||||
const { value, done } = await reader.read();
|
if (responseBodyRid !== null) {
|
||||||
if (done) {
|
body = readableStreamForRid(responseBodyRid);
|
||||||
await core.shutdown(rid);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
await core.writeAll(rid, value);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
core.close(rid);
|
|
||||||
}
|
}
|
||||||
}
|
const response = new Response(
|
||||||
// Step 12-19: TODO(@satyarohith): do the insertion in background.
|
body,
|
||||||
}
|
|
||||||
|
|
||||||
/** See https://w3c.github.io/ServiceWorker/#cache-match */
|
|
||||||
async match(request, options) {
|
|
||||||
webidl.assertBranded(this, CachePrototype);
|
|
||||||
const prefix = "Failed to execute 'match' on 'Cache'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
request = webidl.converters["RequestInfo_DOMString"](request, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
const p = await this[_matchAll](request, options);
|
|
||||||
if (p.length > 0) {
|
|
||||||
return p[0];
|
|
||||||
} else {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** See https://w3c.github.io/ServiceWorker/#cache-delete */
|
|
||||||
async delete(request, _options) {
|
|
||||||
webidl.assertBranded(this, CachePrototype);
|
|
||||||
const prefix = "Failed to execute 'delete' on 'Cache'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
request = webidl.converters["RequestInfo_DOMString"](request, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
// Step 1.
|
|
||||||
let r = null;
|
|
||||||
// Step 2.
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) {
|
|
||||||
r = request;
|
|
||||||
if (request.method !== "GET") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
typeof request === "string" ||
|
|
||||||
ObjectPrototypeIsPrototypeOf(URLPrototype, request)
|
|
||||||
) {
|
|
||||||
r = new Request(request);
|
|
||||||
}
|
|
||||||
return await core.opAsync("op_cache_delete", {
|
|
||||||
cacheId: this[_id],
|
|
||||||
requestUrl: r.url,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** See https://w3c.github.io/ServiceWorker/#cache-matchall
|
|
||||||
*
|
|
||||||
* Note: the function is private as we don't want to expose
|
|
||||||
* this API to the public yet.
|
|
||||||
*
|
|
||||||
* The function will return an array of responses.
|
|
||||||
*/
|
|
||||||
async [_matchAll](request, _options) {
|
|
||||||
// Step 1.
|
|
||||||
let r = null;
|
|
||||||
// Step 2.
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(RequestPrototype, request)) {
|
|
||||||
r = request;
|
|
||||||
if (request.method !== "GET") {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
} else if (
|
|
||||||
typeof request === "string" ||
|
|
||||||
ObjectPrototypeIsPrototypeOf(URLPrototype, request)
|
|
||||||
) {
|
|
||||||
r = new Request(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 5.
|
|
||||||
const responses = [];
|
|
||||||
// Step 5.2
|
|
||||||
if (r === null) {
|
|
||||||
// Step 5.3
|
|
||||||
// Note: we have to return all responses in the cache when
|
|
||||||
// the request is null.
|
|
||||||
// We deviate from the spec here and return an empty array
|
|
||||||
// as we don't expose matchAll() API.
|
|
||||||
return responses;
|
|
||||||
} else {
|
|
||||||
// Remove the fragment from the request URL.
|
|
||||||
const url = new URL(r.url);
|
|
||||||
url.hash = "";
|
|
||||||
const innerRequest = toInnerRequest(r);
|
|
||||||
const matchResult = await core.opAsync(
|
|
||||||
"op_cache_match",
|
|
||||||
{
|
{
|
||||||
cacheId: this[_id],
|
headers: meta.responseHeaders,
|
||||||
requestUrl: url.toString(),
|
status: meta.responseStatus,
|
||||||
requestHeaders: innerRequest.headerList,
|
statusText: meta.responseStatusText,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (matchResult) {
|
responses.push(response);
|
||||||
const { 0: meta, 1: responseBodyRid } = matchResult;
|
|
||||||
let body = null;
|
|
||||||
if (responseBodyRid !== null) {
|
|
||||||
body = readableStreamForRid(responseBodyRid);
|
|
||||||
}
|
|
||||||
const response = new Response(
|
|
||||||
body,
|
|
||||||
{
|
|
||||||
headers: meta.responseHeaders,
|
|
||||||
status: meta.responseStatus,
|
|
||||||
statusText: meta.responseStatusText,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
responses.push(response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Step 5.4-5.5: don't apply in this context.
|
|
||||||
|
|
||||||
return responses;
|
|
||||||
}
|
}
|
||||||
|
// Step 5.4-5.5: don't apply in this context.
|
||||||
|
|
||||||
|
return responses;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(CacheStorage);
|
webidl.configurePrototype(CacheStorage);
|
||||||
webidl.configurePrototype(Cache);
|
webidl.configurePrototype(Cache);
|
||||||
const CacheStoragePrototype = CacheStorage.prototype;
|
const CacheStoragePrototype = CacheStorage.prototype;
|
||||||
const CachePrototype = Cache.prototype;
|
const CachePrototype = Cache.prototype;
|
||||||
|
|
||||||
let cacheStorage;
|
let cacheStorageStorage;
|
||||||
window.__bootstrap.caches = {
|
function cacheStorage() {
|
||||||
CacheStorage,
|
if (!cacheStorageStorage) {
|
||||||
Cache,
|
cacheStorageStorage = webidl.createBranded(CacheStorage);
|
||||||
cacheStorage() {
|
}
|
||||||
if (!cacheStorage) {
|
return cacheStorageStorage;
|
||||||
cacheStorage = webidl.createBranded(CacheStorage);
|
}
|
||||||
}
|
|
||||||
return cacheStorage;
|
export { Cache, CacheStorage, cacheStorage };
|
||||||
},
|
|
||||||
};
|
|
||||||
})(this);
|
|
||||||
|
|
2
ext/cache/lib.rs
vendored
2
ext/cache/lib.rs
vendored
|
@ -27,7 +27,7 @@ pub fn init<CA: Cache + 'static>(
|
||||||
) -> Extension {
|
) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl", "deno_web", "deno_url", "deno_fetch"])
|
.dependencies(vec!["deno_webidl", "deno_web", "deno_url", "deno_fetch"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/cache",
|
prefix "internal:ext/cache",
|
||||||
"01_cache.js",
|
"01_cache.js",
|
||||||
))
|
))
|
||||||
|
|
|
@ -2,110 +2,107 @@
|
||||||
|
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
RegExp,
|
||||||
|
StringPrototypeReplace,
|
||||||
|
ArrayPrototypeJoin,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
let noColor = false;
|
||||||
const {
|
|
||||||
RegExp,
|
|
||||||
StringPrototypeReplace,
|
|
||||||
ArrayPrototypeJoin,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
let noColor = false;
|
function setNoColor(value) {
|
||||||
|
noColor = value;
|
||||||
|
}
|
||||||
|
|
||||||
function setNoColor(value) {
|
function getNoColor() {
|
||||||
noColor = value;
|
return noColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNoColor() {
|
function code(open, close) {
|
||||||
return noColor;
|
return {
|
||||||
}
|
open: `\x1b[${open}m`,
|
||||||
|
close: `\x1b[${close}m`,
|
||||||
function code(open, close) {
|
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||||
return {
|
|
||||||
open: `\x1b[${open}m`,
|
|
||||||
close: `\x1b[${close}m`,
|
|
||||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function run(str, code) {
|
|
||||||
return `${code.open}${
|
|
||||||
StringPrototypeReplace(str, code.regexp, code.open)
|
|
||||||
}${code.close}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function bold(str) {
|
|
||||||
return run(str, code(1, 22));
|
|
||||||
}
|
|
||||||
|
|
||||||
function italic(str) {
|
|
||||||
return run(str, code(3, 23));
|
|
||||||
}
|
|
||||||
|
|
||||||
function yellow(str) {
|
|
||||||
return run(str, code(33, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
function cyan(str) {
|
|
||||||
return run(str, code(36, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
function red(str) {
|
|
||||||
return run(str, code(31, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
function green(str) {
|
|
||||||
return run(str, code(32, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
function bgRed(str) {
|
|
||||||
return run(str, code(41, 49));
|
|
||||||
}
|
|
||||||
|
|
||||||
function white(str) {
|
|
||||||
return run(str, code(37, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
function gray(str) {
|
|
||||||
return run(str, code(90, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
function magenta(str) {
|
|
||||||
return run(str, code(35, 39));
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js
|
|
||||||
const ANSI_PATTERN = new RegExp(
|
|
||||||
ArrayPrototypeJoin([
|
|
||||||
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
|
||||||
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))",
|
|
||||||
], "|"),
|
|
||||||
"g",
|
|
||||||
);
|
|
||||||
|
|
||||||
function stripColor(string) {
|
|
||||||
return StringPrototypeReplace(string, ANSI_PATTERN, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeColor(fn) {
|
|
||||||
return !noColor ? fn : (s) => s;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.colors = {
|
|
||||||
bold,
|
|
||||||
italic,
|
|
||||||
yellow,
|
|
||||||
cyan,
|
|
||||||
red,
|
|
||||||
green,
|
|
||||||
bgRed,
|
|
||||||
white,
|
|
||||||
gray,
|
|
||||||
magenta,
|
|
||||||
stripColor,
|
|
||||||
maybeColor,
|
|
||||||
setNoColor,
|
|
||||||
getNoColor,
|
|
||||||
};
|
};
|
||||||
})(this);
|
}
|
||||||
|
|
||||||
|
function run(str, code) {
|
||||||
|
return `${code.open}${
|
||||||
|
StringPrototypeReplace(str, code.regexp, code.open)
|
||||||
|
}${code.close}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function bold(str) {
|
||||||
|
return run(str, code(1, 22));
|
||||||
|
}
|
||||||
|
|
||||||
|
function italic(str) {
|
||||||
|
return run(str, code(3, 23));
|
||||||
|
}
|
||||||
|
|
||||||
|
function yellow(str) {
|
||||||
|
return run(str, code(33, 39));
|
||||||
|
}
|
||||||
|
|
||||||
|
function cyan(str) {
|
||||||
|
return run(str, code(36, 39));
|
||||||
|
}
|
||||||
|
|
||||||
|
function red(str) {
|
||||||
|
return run(str, code(31, 39));
|
||||||
|
}
|
||||||
|
|
||||||
|
function green(str) {
|
||||||
|
return run(str, code(32, 39));
|
||||||
|
}
|
||||||
|
|
||||||
|
function bgRed(str) {
|
||||||
|
return run(str, code(41, 49));
|
||||||
|
}
|
||||||
|
|
||||||
|
function white(str) {
|
||||||
|
return run(str, code(37, 39));
|
||||||
|
}
|
||||||
|
|
||||||
|
function gray(str) {
|
||||||
|
return run(str, code(90, 39));
|
||||||
|
}
|
||||||
|
|
||||||
|
function magenta(str) {
|
||||||
|
return run(str, code(35, 39));
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://github.com/chalk/ansi-regex/blob/02fa893d619d3da85411acc8fd4e2eea0e95a9d9/index.js
|
||||||
|
const ANSI_PATTERN = new RegExp(
|
||||||
|
ArrayPrototypeJoin([
|
||||||
|
"[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)",
|
||||||
|
"(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))",
|
||||||
|
], "|"),
|
||||||
|
"g",
|
||||||
|
);
|
||||||
|
|
||||||
|
function stripColor(string) {
|
||||||
|
return StringPrototypeReplace(string, ANSI_PATTERN, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function maybeColor(fn) {
|
||||||
|
return !noColor ? fn : (s) => s;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
bgRed,
|
||||||
|
bold,
|
||||||
|
cyan,
|
||||||
|
getNoColor,
|
||||||
|
gray,
|
||||||
|
green,
|
||||||
|
italic,
|
||||||
|
magenta,
|
||||||
|
maybeColor,
|
||||||
|
red,
|
||||||
|
setNoColor,
|
||||||
|
stripColor,
|
||||||
|
white,
|
||||||
|
yellow,
|
||||||
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
16
ext/console/internal.d.ts
vendored
16
ext/console/internal.d.ts
vendored
|
@ -3,14 +3,10 @@
|
||||||
/// <reference no-default-lib="true" />
|
/// <reference no-default-lib="true" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
declare namespace globalThis {
|
declare module "internal:ext/console/02_console.js" {
|
||||||
declare namespace __bootstrap {
|
function createFilteredInspectProxy<TObject>(params: {
|
||||||
declare namespace console {
|
object: TObject;
|
||||||
declare function createFilteredInspectProxy<TObject>(params: {
|
keys: (keyof TObject)[];
|
||||||
object: TObject;
|
evaluate: boolean;
|
||||||
keys: (keyof TObject)[];
|
}): Record<string, unknown>;
|
||||||
evaluate: boolean;
|
|
||||||
}): Record<string, unknown>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::path::PathBuf;
|
||||||
|
|
||||||
pub fn init() -> Extension {
|
pub fn init() -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/console",
|
prefix "internal:ext/console",
|
||||||
"01_colors.js",
|
"01_colors.js",
|
||||||
"02_console.js",
|
"02_console.js",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -4,484 +4,481 @@
|
||||||
/// <reference path="../../core/lib.deno_core.d.ts" />
|
/// <reference path="../../core/lib.deno_core.d.ts" />
|
||||||
/// <reference path="../webidl/internal.d.ts" />
|
/// <reference path="../webidl/internal.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import { CryptoKey } from "internal:ext/crypto/00_crypto.js";
|
||||||
|
const {
|
||||||
|
ArrayBufferIsView,
|
||||||
|
ArrayBufferPrototype,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
SafeArrayIterator,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
webidl.converters.AlgorithmIdentifier = (V, opts) => {
|
||||||
const webidl = window.__bootstrap.webidl;
|
// Union for (object or DOMString)
|
||||||
const { CryptoKey } = window.__bootstrap.crypto;
|
if (webidl.type(V) == "Object") {
|
||||||
const {
|
return webidl.converters.object(V, opts);
|
||||||
ArrayBufferIsView,
|
}
|
||||||
ArrayBufferPrototype,
|
return webidl.converters.DOMString(V, opts);
|
||||||
ObjectPrototypeIsPrototypeOf,
|
};
|
||||||
SafeArrayIterator,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
webidl.converters.AlgorithmIdentifier = (V, opts) => {
|
webidl.converters["BufferSource or JsonWebKey"] = (V, opts) => {
|
||||||
// Union for (object or DOMString)
|
// Union for (BufferSource or JsonWebKey)
|
||||||
if (webidl.type(V) == "Object") {
|
if (
|
||||||
return webidl.converters.object(V, opts);
|
ArrayBufferIsView(V) ||
|
||||||
}
|
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V)
|
||||||
return webidl.converters.DOMString(V, opts);
|
) {
|
||||||
};
|
return webidl.converters.BufferSource(V, opts);
|
||||||
|
}
|
||||||
|
return webidl.converters.JsonWebKey(V, opts);
|
||||||
|
};
|
||||||
|
|
||||||
webidl.converters["BufferSource or JsonWebKey"] = (V, opts) => {
|
webidl.converters.KeyType = webidl.createEnumConverter("KeyType", [
|
||||||
// Union for (BufferSource or JsonWebKey)
|
"public",
|
||||||
if (
|
"private",
|
||||||
ArrayBufferIsView(V) ||
|
"secret",
|
||||||
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V)
|
]);
|
||||||
) {
|
|
||||||
return webidl.converters.BufferSource(V, opts);
|
|
||||||
}
|
|
||||||
return webidl.converters.JsonWebKey(V, opts);
|
|
||||||
};
|
|
||||||
|
|
||||||
webidl.converters.KeyType = webidl.createEnumConverter("KeyType", [
|
webidl.converters.KeyFormat = webidl.createEnumConverter("KeyFormat", [
|
||||||
"public",
|
"raw",
|
||||||
"private",
|
"pkcs8",
|
||||||
"secret",
|
"spki",
|
||||||
]);
|
"jwk",
|
||||||
|
]);
|
||||||
|
|
||||||
webidl.converters.KeyFormat = webidl.createEnumConverter("KeyFormat", [
|
webidl.converters.KeyUsage = webidl.createEnumConverter("KeyUsage", [
|
||||||
"raw",
|
"encrypt",
|
||||||
"pkcs8",
|
"decrypt",
|
||||||
"spki",
|
"sign",
|
||||||
"jwk",
|
"verify",
|
||||||
]);
|
"deriveKey",
|
||||||
|
"deriveBits",
|
||||||
|
"wrapKey",
|
||||||
|
"unwrapKey",
|
||||||
|
]);
|
||||||
|
|
||||||
webidl.converters.KeyUsage = webidl.createEnumConverter("KeyUsage", [
|
webidl.converters["sequence<KeyUsage>"] = webidl.createSequenceConverter(
|
||||||
"encrypt",
|
webidl.converters.KeyUsage,
|
||||||
"decrypt",
|
);
|
||||||
"sign",
|
|
||||||
"verify",
|
|
||||||
"deriveKey",
|
|
||||||
"deriveBits",
|
|
||||||
"wrapKey",
|
|
||||||
"unwrapKey",
|
|
||||||
]);
|
|
||||||
|
|
||||||
webidl.converters["sequence<KeyUsage>"] = webidl.createSequenceConverter(
|
webidl.converters.HashAlgorithmIdentifier =
|
||||||
webidl.converters.KeyUsage,
|
webidl.converters.AlgorithmIdentifier;
|
||||||
);
|
|
||||||
|
|
||||||
webidl.converters.HashAlgorithmIdentifier =
|
/** @type {webidl.Dictionary} */
|
||||||
webidl.converters.AlgorithmIdentifier;
|
const dictAlgorithm = [{
|
||||||
|
key: "name",
|
||||||
|
converter: webidl.converters.DOMString,
|
||||||
|
required: true,
|
||||||
|
}];
|
||||||
|
|
||||||
/** @type {__bootstrap.webidl.Dictionary} */
|
webidl.converters.Algorithm = webidl
|
||||||
const dictAlgorithm = [{
|
.createDictionaryConverter("Algorithm", dictAlgorithm);
|
||||||
key: "name",
|
|
||||||
converter: webidl.converters.DOMString,
|
webidl.converters.BigInteger = webidl.converters.Uint8Array;
|
||||||
|
|
||||||
|
/** @type {webidl.Dictionary} */
|
||||||
|
const dictRsaKeyGenParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "modulusLength",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
required: true,
|
required: true,
|
||||||
}];
|
},
|
||||||
|
{
|
||||||
|
key: "publicExponent",
|
||||||
|
converter: webidl.converters.BigInteger,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
webidl.converters.Algorithm = webidl
|
webidl.converters.RsaKeyGenParams = webidl
|
||||||
.createDictionaryConverter("Algorithm", dictAlgorithm);
|
.createDictionaryConverter("RsaKeyGenParams", dictRsaKeyGenParams);
|
||||||
|
|
||||||
webidl.converters.BigInteger = webidl.converters.Uint8Array;
|
const dictRsaHashedKeyGenParams = [
|
||||||
|
...new SafeArrayIterator(dictRsaKeyGenParams),
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
/** @type {__bootstrap.webidl.Dictionary} */
|
webidl.converters.RsaHashedKeyGenParams = webidl.createDictionaryConverter(
|
||||||
const dictRsaKeyGenParams = [
|
"RsaHashedKeyGenParams",
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
dictRsaHashedKeyGenParams,
|
||||||
{
|
);
|
||||||
key: "modulusLength",
|
|
||||||
converter: (V, opts) =>
|
|
||||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "publicExponent",
|
|
||||||
converter: webidl.converters.BigInteger,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.RsaKeyGenParams = webidl
|
const dictRsaHashedImportParams = [
|
||||||
.createDictionaryConverter("RsaKeyGenParams", dictRsaKeyGenParams);
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const dictRsaHashedKeyGenParams = [
|
webidl.converters.RsaHashedImportParams = webidl.createDictionaryConverter(
|
||||||
...new SafeArrayIterator(dictRsaKeyGenParams),
|
"RsaHashedImportParams",
|
||||||
{
|
dictRsaHashedImportParams,
|
||||||
key: "hash",
|
);
|
||||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.RsaHashedKeyGenParams = webidl.createDictionaryConverter(
|
webidl.converters.NamedCurve = webidl.converters.DOMString;
|
||||||
"RsaHashedKeyGenParams",
|
|
||||||
dictRsaHashedKeyGenParams,
|
const dictEcKeyImportParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "namedCurve",
|
||||||
|
converter: webidl.converters.NamedCurve,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.EcKeyImportParams = webidl.createDictionaryConverter(
|
||||||
|
"EcKeyImportParams",
|
||||||
|
dictEcKeyImportParams,
|
||||||
|
);
|
||||||
|
|
||||||
|
const dictEcKeyGenParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "namedCurve",
|
||||||
|
converter: webidl.converters.NamedCurve,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.EcKeyGenParams = webidl
|
||||||
|
.createDictionaryConverter("EcKeyGenParams", dictEcKeyGenParams);
|
||||||
|
|
||||||
|
const dictAesKeyGenParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "length",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned short"](V, { ...opts, enforceRange: true }),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.AesKeyGenParams = webidl
|
||||||
|
.createDictionaryConverter("AesKeyGenParams", dictAesKeyGenParams);
|
||||||
|
|
||||||
|
const dictHmacKeyGenParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "length",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.HmacKeyGenParams = webidl
|
||||||
|
.createDictionaryConverter("HmacKeyGenParams", dictHmacKeyGenParams);
|
||||||
|
|
||||||
|
const dictRsaPssParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "saltLength",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.RsaPssParams = webidl
|
||||||
|
.createDictionaryConverter("RsaPssParams", dictRsaPssParams);
|
||||||
|
|
||||||
|
const dictRsaOaepParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "label",
|
||||||
|
converter: webidl.converters["BufferSource"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.RsaOaepParams = webidl
|
||||||
|
.createDictionaryConverter("RsaOaepParams", dictRsaOaepParams);
|
||||||
|
|
||||||
|
const dictEcdsaParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters["EcdsaParams"] = webidl
|
||||||
|
.createDictionaryConverter("EcdsaParams", dictEcdsaParams);
|
||||||
|
|
||||||
|
const dictHmacImportParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "length",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.HmacImportParams = webidl
|
||||||
|
.createDictionaryConverter("HmacImportParams", dictHmacImportParams);
|
||||||
|
|
||||||
|
const dictRsaOtherPrimesInfo = [
|
||||||
|
{
|
||||||
|
key: "r",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "d",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "t",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
webidl.converters.RsaOtherPrimesInfo = webidl.createDictionaryConverter(
|
||||||
|
"RsaOtherPrimesInfo",
|
||||||
|
dictRsaOtherPrimesInfo,
|
||||||
|
);
|
||||||
|
webidl.converters["sequence<RsaOtherPrimesInfo>"] = webidl
|
||||||
|
.createSequenceConverter(
|
||||||
|
webidl.converters.RsaOtherPrimesInfo,
|
||||||
);
|
);
|
||||||
|
|
||||||
const dictRsaHashedImportParams = [
|
const dictJsonWebKey = [
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
// Sections 4.2 and 4.3 of RFC7517.
|
||||||
{
|
// https://datatracker.ietf.org/doc/html/rfc7517#section-4
|
||||||
key: "hash",
|
{
|
||||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
key: "kty",
|
||||||
required: true,
|
converter: webidl.converters["DOMString"],
|
||||||
},
|
},
|
||||||
];
|
{
|
||||||
|
key: "use",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "key_ops",
|
||||||
|
converter: webidl.converters["sequence<DOMString>"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "alg",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
// JSON Web Key Parameters Registration
|
||||||
|
{
|
||||||
|
key: "ext",
|
||||||
|
converter: webidl.converters["boolean"],
|
||||||
|
},
|
||||||
|
// Section 6 of RFC7518 JSON Web Algorithms
|
||||||
|
// https://datatracker.ietf.org/doc/html/rfc7518#section-6
|
||||||
|
{
|
||||||
|
key: "crv",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "x",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "y",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "d",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "n",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "e",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "p",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "q",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "dp",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "dq",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "qi",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "oth",
|
||||||
|
converter: webidl.converters["sequence<RsaOtherPrimesInfo>"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "k",
|
||||||
|
converter: webidl.converters["DOMString"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
webidl.converters.RsaHashedImportParams = webidl.createDictionaryConverter(
|
webidl.converters.JsonWebKey = webidl.createDictionaryConverter(
|
||||||
"RsaHashedImportParams",
|
"JsonWebKey",
|
||||||
dictRsaHashedImportParams,
|
dictJsonWebKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
webidl.converters.NamedCurve = webidl.converters.DOMString;
|
const dictHkdfParams = [
|
||||||
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "salt",
|
||||||
|
converter: webidl.converters["BufferSource"],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "info",
|
||||||
|
converter: webidl.converters["BufferSource"],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const dictEcKeyImportParams = [
|
webidl.converters.HkdfParams = webidl
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
.createDictionaryConverter("HkdfParams", dictHkdfParams);
|
||||||
{
|
|
||||||
key: "namedCurve",
|
|
||||||
converter: webidl.converters.NamedCurve,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.EcKeyImportParams = webidl.createDictionaryConverter(
|
const dictPbkdf2Params = [
|
||||||
"EcKeyImportParams",
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
dictEcKeyImportParams,
|
{
|
||||||
);
|
key: "hash",
|
||||||
|
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "iterations",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "salt",
|
||||||
|
converter: webidl.converters["BufferSource"],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const dictEcKeyGenParams = [
|
webidl.converters.Pbkdf2Params = webidl
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
.createDictionaryConverter("Pbkdf2Params", dictPbkdf2Params);
|
||||||
{
|
|
||||||
key: "namedCurve",
|
|
||||||
converter: webidl.converters.NamedCurve,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.EcKeyGenParams = webidl
|
const dictAesDerivedKeyParams = [
|
||||||
.createDictionaryConverter("EcKeyGenParams", dictEcKeyGenParams);
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "length",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const dictAesKeyGenParams = [
|
const dictAesCbcParams = [
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
{
|
{
|
||||||
key: "length",
|
key: "iv",
|
||||||
converter: (V, opts) =>
|
converter: webidl.converters["BufferSource"],
|
||||||
webidl.converters["unsigned short"](V, { ...opts, enforceRange: true }),
|
required: true,
|
||||||
required: true,
|
},
|
||||||
},
|
];
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.AesKeyGenParams = webidl
|
const dictAesGcmParams = [
|
||||||
.createDictionaryConverter("AesKeyGenParams", dictAesKeyGenParams);
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
|
{
|
||||||
|
key: "iv",
|
||||||
|
converter: webidl.converters["BufferSource"],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "tagLength",
|
||||||
|
converter: (V, opts) =>
|
||||||
|
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "additionalData",
|
||||||
|
converter: webidl.converters["BufferSource"],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const dictHmacKeyGenParams = [
|
const dictAesCtrParams = [
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
{
|
{
|
||||||
key: "hash",
|
key: "counter",
|
||||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
converter: webidl.converters["BufferSource"],
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "length",
|
key: "length",
|
||||||
converter: (V, opts) =>
|
converter: (V, opts) =>
|
||||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
webidl.converters["unsigned short"](V, { ...opts, enforceRange: true }),
|
||||||
},
|
required: true,
|
||||||
];
|
},
|
||||||
|
];
|
||||||
|
|
||||||
webidl.converters.HmacKeyGenParams = webidl
|
webidl.converters.AesDerivedKeyParams = webidl
|
||||||
.createDictionaryConverter("HmacKeyGenParams", dictHmacKeyGenParams);
|
.createDictionaryConverter("AesDerivedKeyParams", dictAesDerivedKeyParams);
|
||||||
|
|
||||||
const dictRsaPssParams = [
|
webidl.converters.AesCbcParams = webidl
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
.createDictionaryConverter("AesCbcParams", dictAesCbcParams);
|
||||||
{
|
|
||||||
key: "saltLength",
|
|
||||||
converter: (V, opts) =>
|
|
||||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.RsaPssParams = webidl
|
webidl.converters.AesGcmParams = webidl
|
||||||
.createDictionaryConverter("RsaPssParams", dictRsaPssParams);
|
.createDictionaryConverter("AesGcmParams", dictAesGcmParams);
|
||||||
|
|
||||||
const dictRsaOaepParams = [
|
webidl.converters.AesCtrParams = webidl
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
.createDictionaryConverter("AesCtrParams", dictAesCtrParams);
|
||||||
{
|
|
||||||
key: "label",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.RsaOaepParams = webidl
|
webidl.converters.CryptoKey = webidl.createInterfaceConverter(
|
||||||
.createDictionaryConverter("RsaOaepParams", dictRsaOaepParams);
|
"CryptoKey",
|
||||||
|
CryptoKey.prototype,
|
||||||
|
);
|
||||||
|
|
||||||
const dictEcdsaParams = [
|
const dictCryptoKeyPair = [
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
{
|
||||||
{
|
key: "publicKey",
|
||||||
key: "hash",
|
converter: webidl.converters.CryptoKey,
|
||||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
},
|
||||||
required: true,
|
{
|
||||||
},
|
key: "privateKey",
|
||||||
];
|
converter: webidl.converters.CryptoKey,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
webidl.converters["EcdsaParams"] = webidl
|
webidl.converters.CryptoKeyPair = webidl
|
||||||
.createDictionaryConverter("EcdsaParams", dictEcdsaParams);
|
.createDictionaryConverter("CryptoKeyPair", dictCryptoKeyPair);
|
||||||
|
|
||||||
const dictHmacImportParams = [
|
const dictEcdhKeyDeriveParams = [
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
...new SafeArrayIterator(dictAlgorithm),
|
||||||
{
|
{
|
||||||
key: "hash",
|
key: "public",
|
||||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
converter: webidl.converters.CryptoKey,
|
||||||
required: true,
|
required: true,
|
||||||
},
|
},
|
||||||
{
|
];
|
||||||
key: "length",
|
|
||||||
converter: (V, opts) =>
|
|
||||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.HmacImportParams = webidl
|
webidl.converters.EcdhKeyDeriveParams = webidl
|
||||||
.createDictionaryConverter("HmacImportParams", dictHmacImportParams);
|
.createDictionaryConverter("EcdhKeyDeriveParams", dictEcdhKeyDeriveParams);
|
||||||
|
|
||||||
const dictRsaOtherPrimesInfo = [
|
|
||||||
{
|
|
||||||
key: "r",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "d",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "t",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.RsaOtherPrimesInfo = webidl.createDictionaryConverter(
|
|
||||||
"RsaOtherPrimesInfo",
|
|
||||||
dictRsaOtherPrimesInfo,
|
|
||||||
);
|
|
||||||
webidl.converters["sequence<RsaOtherPrimesInfo>"] = webidl
|
|
||||||
.createSequenceConverter(
|
|
||||||
webidl.converters.RsaOtherPrimesInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
const dictJsonWebKey = [
|
|
||||||
// Sections 4.2 and 4.3 of RFC7517.
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc7517#section-4
|
|
||||||
{
|
|
||||||
key: "kty",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "use",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "key_ops",
|
|
||||||
converter: webidl.converters["sequence<DOMString>"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "alg",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
// JSON Web Key Parameters Registration
|
|
||||||
{
|
|
||||||
key: "ext",
|
|
||||||
converter: webidl.converters["boolean"],
|
|
||||||
},
|
|
||||||
// Section 6 of RFC7518 JSON Web Algorithms
|
|
||||||
// https://datatracker.ietf.org/doc/html/rfc7518#section-6
|
|
||||||
{
|
|
||||||
key: "crv",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "x",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "y",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "d",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "n",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "e",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "p",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "q",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "dp",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "dq",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "qi",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "oth",
|
|
||||||
converter: webidl.converters["sequence<RsaOtherPrimesInfo>"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "k",
|
|
||||||
converter: webidl.converters["DOMString"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.JsonWebKey = webidl.createDictionaryConverter(
|
|
||||||
"JsonWebKey",
|
|
||||||
dictJsonWebKey,
|
|
||||||
);
|
|
||||||
|
|
||||||
const dictHkdfParams = [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: "hash",
|
|
||||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "salt",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "info",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.HkdfParams = webidl
|
|
||||||
.createDictionaryConverter("HkdfParams", dictHkdfParams);
|
|
||||||
|
|
||||||
const dictPbkdf2Params = [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: "hash",
|
|
||||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "iterations",
|
|
||||||
converter: (V, opts) =>
|
|
||||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "salt",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.Pbkdf2Params = webidl
|
|
||||||
.createDictionaryConverter("Pbkdf2Params", dictPbkdf2Params);
|
|
||||||
|
|
||||||
const dictAesDerivedKeyParams = [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: "length",
|
|
||||||
converter: (V, opts) =>
|
|
||||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const dictAesCbcParams = [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: "iv",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const dictAesGcmParams = [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: "iv",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "tagLength",
|
|
||||||
converter: (V, opts) =>
|
|
||||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "additionalData",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const dictAesCtrParams = [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: "counter",
|
|
||||||
converter: webidl.converters["BufferSource"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "length",
|
|
||||||
converter: (V, opts) =>
|
|
||||||
webidl.converters["unsigned short"](V, { ...opts, enforceRange: true }),
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.AesDerivedKeyParams = webidl
|
|
||||||
.createDictionaryConverter("AesDerivedKeyParams", dictAesDerivedKeyParams);
|
|
||||||
|
|
||||||
webidl.converters.AesCbcParams = webidl
|
|
||||||
.createDictionaryConverter("AesCbcParams", dictAesCbcParams);
|
|
||||||
|
|
||||||
webidl.converters.AesGcmParams = webidl
|
|
||||||
.createDictionaryConverter("AesGcmParams", dictAesGcmParams);
|
|
||||||
|
|
||||||
webidl.converters.AesCtrParams = webidl
|
|
||||||
.createDictionaryConverter("AesCtrParams", dictAesCtrParams);
|
|
||||||
|
|
||||||
webidl.converters.CryptoKey = webidl.createInterfaceConverter(
|
|
||||||
"CryptoKey",
|
|
||||||
CryptoKey.prototype,
|
|
||||||
);
|
|
||||||
|
|
||||||
const dictCryptoKeyPair = [
|
|
||||||
{
|
|
||||||
key: "publicKey",
|
|
||||||
converter: webidl.converters.CryptoKey,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "privateKey",
|
|
||||||
converter: webidl.converters.CryptoKey,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.CryptoKeyPair = webidl
|
|
||||||
.createDictionaryConverter("CryptoKeyPair", dictCryptoKeyPair);
|
|
||||||
|
|
||||||
const dictEcdhKeyDeriveParams = [
|
|
||||||
...new SafeArrayIterator(dictAlgorithm),
|
|
||||||
{
|
|
||||||
key: "public",
|
|
||||||
converter: webidl.converters.CryptoKey,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
webidl.converters.EcdhKeyDeriveParams = webidl
|
|
||||||
.createDictionaryConverter("EcdhKeyDeriveParams", dictEcdhKeyDeriveParams);
|
|
||||||
})(this);
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ use crate::shared::RawKeyData;
|
||||||
pub fn init(maybe_seed: Option<u64>) -> Extension {
|
pub fn init(maybe_seed: Option<u64>) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl", "deno_web"])
|
.dependencies(vec!["deno_webidl", "deno_web"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/crypto",
|
prefix "internal:ext/crypto",
|
||||||
"00_crypto.js",
|
"00_crypto.js",
|
||||||
"01_webidl.js",
|
"01_webidl.js",
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
|
||||||
const { TypeError } = window.__bootstrap.primordials;
|
|
||||||
function requiredArguments(
|
|
||||||
name,
|
|
||||||
length,
|
|
||||||
required,
|
|
||||||
) {
|
|
||||||
if (length < required) {
|
|
||||||
const errMsg = `${name} requires at least ${required} argument${
|
|
||||||
required === 1 ? "" : "s"
|
|
||||||
}, but only ${length} present`;
|
|
||||||
throw new TypeError(errMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.fetchUtil = {
|
|
||||||
requiredArguments,
|
|
||||||
};
|
|
||||||
})(this);
|
|
|
@ -8,97 +8,369 @@
|
||||||
/// <reference path="../web/06_streams_types.d.ts" />
|
/// <reference path="../web/06_streams_types.d.ts" />
|
||||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = window.__bootstrap.webidl;
|
import {
|
||||||
const {
|
byteLowerCase,
|
||||||
HTTP_TAB_OR_SPACE_PREFIX_RE,
|
collectHttpQuotedString,
|
||||||
HTTP_TAB_OR_SPACE_SUFFIX_RE,
|
collectSequenceOfCodepoints,
|
||||||
HTTP_TOKEN_CODE_POINT_RE,
|
HTTP_TAB_OR_SPACE_PREFIX_RE,
|
||||||
byteLowerCase,
|
HTTP_TAB_OR_SPACE_SUFFIX_RE,
|
||||||
collectSequenceOfCodepoints,
|
HTTP_TOKEN_CODE_POINT_RE,
|
||||||
collectHttpQuotedString,
|
httpTrim,
|
||||||
httpTrim,
|
} from "internal:ext/web/00_infra.js";
|
||||||
} = window.__bootstrap.infra;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
ArrayIsArray,
|
ArrayIsArray,
|
||||||
ArrayPrototypeMap,
|
ArrayPrototypeMap,
|
||||||
ArrayPrototypePush,
|
ArrayPrototypePush,
|
||||||
ArrayPrototypeSort,
|
ArrayPrototypeSort,
|
||||||
ArrayPrototypeJoin,
|
ArrayPrototypeJoin,
|
||||||
ArrayPrototypeSplice,
|
ArrayPrototypeSplice,
|
||||||
ArrayPrototypeFilter,
|
ArrayPrototypeFilter,
|
||||||
ObjectPrototypeHasOwnProperty,
|
ObjectPrototypeHasOwnProperty,
|
||||||
ObjectEntries,
|
ObjectEntries,
|
||||||
RegExpPrototypeTest,
|
RegExpPrototypeTest,
|
||||||
SafeArrayIterator,
|
SafeArrayIterator,
|
||||||
Symbol,
|
Symbol,
|
||||||
SymbolFor,
|
SymbolFor,
|
||||||
SymbolIterator,
|
SymbolIterator,
|
||||||
StringPrototypeReplaceAll,
|
StringPrototypeReplaceAll,
|
||||||
TypeError,
|
TypeError,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
|
|
||||||
const _headerList = Symbol("header list");
|
const _headerList = Symbol("header list");
|
||||||
const _iterableHeaders = Symbol("iterable headers");
|
const _iterableHeaders = Symbol("iterable headers");
|
||||||
const _guard = Symbol("guard");
|
const _guard = Symbol("guard");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Header
|
* @typedef Header
|
||||||
* @type {[string, string]}
|
* @type {[string, string]}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef HeaderList
|
* @typedef HeaderList
|
||||||
* @type {Header[]}
|
* @type {Header[]}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} potentialValue
|
* @param {string} potentialValue
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function normalizeHeaderValue(potentialValue) {
|
function normalizeHeaderValue(potentialValue) {
|
||||||
return httpTrim(potentialValue);
|
return httpTrim(potentialValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Headers} headers
|
||||||
|
* @param {HeadersInit} object
|
||||||
|
*/
|
||||||
|
function fillHeaders(headers, object) {
|
||||||
|
if (ArrayIsArray(object)) {
|
||||||
|
for (let i = 0; i < object.length; ++i) {
|
||||||
|
const header = object[i];
|
||||||
|
if (header.length !== 2) {
|
||||||
|
throw new TypeError(
|
||||||
|
`Invalid header. Length must be 2, but is ${header.length}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
appendHeader(headers, header[0], header[1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const key in object) {
|
||||||
|
if (!ObjectPrototypeHasOwnProperty(object, key)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
appendHeader(headers, key, object[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Regex matching illegal chars in a header value
|
||||||
|
// deno-lint-ignore no-control-regex
|
||||||
|
const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://fetch.spec.whatwg.org/#concept-headers-append
|
||||||
|
* @param {Headers} headers
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
function appendHeader(headers, name, value) {
|
||||||
|
// 1.
|
||||||
|
value = normalizeHeaderValue(value);
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||||
|
throw new TypeError("Header name is not valid.");
|
||||||
|
}
|
||||||
|
if (RegExpPrototypeTest(ILLEGAL_VALUE_CHARS, value)) {
|
||||||
|
throw new TypeError("Header value is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.
|
||||||
|
if (headers[_guard] == "immutable") {
|
||||||
|
throw new TypeError("Headers are immutable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7.
|
||||||
|
const list = headers[_headerList];
|
||||||
|
const lowercaseName = byteLowerCase(name);
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
||||||
|
name = list[i][0];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ArrayPrototypePush(list, [name, value]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://fetch.spec.whatwg.org/#concept-header-list-get
|
||||||
|
* @param {HeaderList} list
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
function getHeader(list, name) {
|
||||||
|
const lowercaseName = byteLowerCase(name);
|
||||||
|
const entries = ArrayPrototypeMap(
|
||||||
|
ArrayPrototypeFilter(
|
||||||
|
list,
|
||||||
|
(entry) => byteLowerCase(entry[0]) === lowercaseName,
|
||||||
|
),
|
||||||
|
(entry) => entry[1],
|
||||||
|
);
|
||||||
|
if (entries.length === 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return ArrayPrototypeJoin(entries, "\x2C\x20");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split
|
||||||
|
* @param {HeaderList} list
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {string[] | null}
|
||||||
|
*/
|
||||||
|
function getDecodeSplitHeader(list, name) {
|
||||||
|
const initialValue = getHeader(list, name);
|
||||||
|
if (initialValue === null) return null;
|
||||||
|
const input = initialValue;
|
||||||
|
let position = 0;
|
||||||
|
const values = [];
|
||||||
|
let value = "";
|
||||||
|
while (position < initialValue.length) {
|
||||||
|
// 7.1. collect up to " or ,
|
||||||
|
const res = collectSequenceOfCodepoints(
|
||||||
|
initialValue,
|
||||||
|
position,
|
||||||
|
(c) => c !== "\u0022" && c !== "\u002C",
|
||||||
|
);
|
||||||
|
value += res.result;
|
||||||
|
position = res.position;
|
||||||
|
|
||||||
|
if (position < initialValue.length) {
|
||||||
|
if (input[position] === "\u0022") {
|
||||||
|
const res = collectHttpQuotedString(input, position, false);
|
||||||
|
value += res.result;
|
||||||
|
position = res.position;
|
||||||
|
if (position < initialValue.length) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (input[position] !== "\u002C") throw new TypeError("Unreachable");
|
||||||
|
position += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_PREFIX_RE, "");
|
||||||
|
value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_SUFFIX_RE, "");
|
||||||
|
|
||||||
|
ArrayPrototypePush(values, value);
|
||||||
|
value = "";
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Headers {
|
||||||
|
/** @type {HeaderList} */
|
||||||
|
[_headerList] = [];
|
||||||
|
/** @type {"immutable" | "request" | "request-no-cors" | "response" | "none"} */
|
||||||
|
[_guard];
|
||||||
|
|
||||||
|
get [_iterableHeaders]() {
|
||||||
|
const list = this[_headerList];
|
||||||
|
|
||||||
|
// The order of steps are not similar to the ones suggested by the
|
||||||
|
// spec but produce the same result.
|
||||||
|
const headers = {};
|
||||||
|
const cookies = [];
|
||||||
|
for (let i = 0; i < list.length; ++i) {
|
||||||
|
const entry = list[i];
|
||||||
|
const name = byteLowerCase(entry[0]);
|
||||||
|
const value = entry[1];
|
||||||
|
if (value === null) throw new TypeError("Unreachable");
|
||||||
|
// The following if statement is not spec compliant.
|
||||||
|
// `set-cookie` is the only header that can not be concatenated,
|
||||||
|
// so must be given to the user as multiple headers.
|
||||||
|
// The else block of the if statement is spec compliant again.
|
||||||
|
if (name === "set-cookie") {
|
||||||
|
ArrayPrototypePush(cookies, [name, value]);
|
||||||
|
} else {
|
||||||
|
// The following code has the same behaviour as getHeader()
|
||||||
|
// at the end of loop. But it avoids looping through the entire
|
||||||
|
// list to combine multiple values with same header name. It
|
||||||
|
// instead gradually combines them as they are found.
|
||||||
|
let header = headers[name];
|
||||||
|
if (header && header.length > 0) {
|
||||||
|
header += "\x2C\x20" + value;
|
||||||
|
} else {
|
||||||
|
header = value;
|
||||||
|
}
|
||||||
|
headers[name] = header;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ArrayPrototypeSort(
|
||||||
|
[
|
||||||
|
...new SafeArrayIterator(ObjectEntries(headers)),
|
||||||
|
...new SafeArrayIterator(cookies),
|
||||||
|
],
|
||||||
|
(a, b) => {
|
||||||
|
const akey = a[0];
|
||||||
|
const bkey = b[0];
|
||||||
|
if (akey > bkey) return 1;
|
||||||
|
if (akey < bkey) return -1;
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {HeadersInit} [init] */
|
||||||
|
constructor(init = undefined) {
|
||||||
|
const prefix = "Failed to construct 'Headers'";
|
||||||
|
if (init !== undefined) {
|
||||||
|
init = webidl.converters["HeadersInit"](init, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
this[_guard] = "none";
|
||||||
|
if (init !== undefined) {
|
||||||
|
fillHeaders(this, init);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Headers} headers
|
* @param {string} name
|
||||||
* @param {HeadersInit} object
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
function fillHeaders(headers, object) {
|
append(name, value) {
|
||||||
if (ArrayIsArray(object)) {
|
webidl.assertBranded(this, HeadersPrototype);
|
||||||
for (let i = 0; i < object.length; ++i) {
|
const prefix = "Failed to execute 'append' on 'Headers'";
|
||||||
const header = object[i];
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
if (header.length !== 2) {
|
name = webidl.converters["ByteString"](name, {
|
||||||
throw new TypeError(
|
prefix,
|
||||||
`Invalid header. Length must be 2, but is ${header.length}`,
|
context: "Argument 1",
|
||||||
);
|
});
|
||||||
}
|
value = webidl.converters["ByteString"](value, {
|
||||||
appendHeader(headers, header[0], header[1]);
|
prefix,
|
||||||
}
|
context: "Argument 2",
|
||||||
} else {
|
});
|
||||||
for (const key in object) {
|
appendHeader(this, name, value);
|
||||||
if (!ObjectPrototypeHasOwnProperty(object, key)) {
|
}
|
||||||
continue;
|
|
||||||
}
|
/**
|
||||||
appendHeader(headers, key, object[key]);
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
delete(name) {
|
||||||
|
const prefix = "Failed to execute 'delete' on 'Headers'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
name = webidl.converters["ByteString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||||
|
throw new TypeError("Header name is not valid.");
|
||||||
|
}
|
||||||
|
if (this[_guard] == "immutable") {
|
||||||
|
throw new TypeError("Headers are immutable.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = this[_headerList];
|
||||||
|
const lowercaseName = byteLowerCase(name);
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
||||||
|
ArrayPrototypeSplice(list, i, 1);
|
||||||
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Regex matching illegal chars in a header value
|
/**
|
||||||
// deno-lint-ignore no-control-regex
|
* @param {string} name
|
||||||
const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/;
|
*/
|
||||||
|
get(name) {
|
||||||
|
const prefix = "Failed to execute 'get' on 'Headers'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
name = webidl.converters["ByteString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||||
|
throw new TypeError("Header name is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = this[_headerList];
|
||||||
|
return getHeader(list, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
*/
|
||||||
|
has(name) {
|
||||||
|
const prefix = "Failed to execute 'has' on 'Headers'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
name = webidl.converters["ByteString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
||||||
|
throw new TypeError("Header name is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = this[_headerList];
|
||||||
|
const lowercaseName = byteLowerCase(name);
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://fetch.spec.whatwg.org/#concept-headers-append
|
|
||||||
* @param {Headers} headers
|
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {string} value
|
* @param {string} value
|
||||||
*/
|
*/
|
||||||
function appendHeader(headers, name, value) {
|
set(name, value) {
|
||||||
// 1.
|
webidl.assertBranded(this, HeadersPrototype);
|
||||||
|
const prefix = "Failed to execute 'set' on 'Headers'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
name = webidl.converters["ByteString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
value = webidl.converters["ByteString"](value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
|
||||||
value = normalizeHeaderValue(value);
|
value = normalizeHeaderValue(value);
|
||||||
|
|
||||||
// 2.
|
// 2.
|
||||||
|
@ -109,371 +381,97 @@
|
||||||
throw new TypeError("Header value is not valid.");
|
throw new TypeError("Header value is not valid.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3.
|
if (this[_guard] == "immutable") {
|
||||||
if (headers[_guard] == "immutable") {
|
|
||||||
throw new TypeError("Headers are immutable.");
|
throw new TypeError("Headers are immutable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7.
|
const list = this[_headerList];
|
||||||
const list = headers[_headerList];
|
|
||||||
const lowercaseName = byteLowerCase(name);
|
const lowercaseName = byteLowerCase(name);
|
||||||
|
let added = false;
|
||||||
for (let i = 0; i < list.length; i++) {
|
for (let i = 0; i < list.length; i++) {
|
||||||
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
||||||
name = list[i][0];
|
if (!added) {
|
||||||
break;
|
list[i][1] = value;
|
||||||
}
|
added = true;
|
||||||
}
|
|
||||||
ArrayPrototypePush(list, [name, value]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://fetch.spec.whatwg.org/#concept-header-list-get
|
|
||||||
* @param {HeaderList} list
|
|
||||||
* @param {string} name
|
|
||||||
*/
|
|
||||||
function getHeader(list, name) {
|
|
||||||
const lowercaseName = byteLowerCase(name);
|
|
||||||
const entries = ArrayPrototypeMap(
|
|
||||||
ArrayPrototypeFilter(
|
|
||||||
list,
|
|
||||||
(entry) => byteLowerCase(entry[0]) === lowercaseName,
|
|
||||||
),
|
|
||||||
(entry) => entry[1],
|
|
||||||
);
|
|
||||||
if (entries.length === 0) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return ArrayPrototypeJoin(entries, "\x2C\x20");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://fetch.spec.whatwg.org/#concept-header-list-get-decode-split
|
|
||||||
* @param {HeaderList} list
|
|
||||||
* @param {string} name
|
|
||||||
* @returns {string[] | null}
|
|
||||||
*/
|
|
||||||
function getDecodeSplitHeader(list, name) {
|
|
||||||
const initialValue = getHeader(list, name);
|
|
||||||
if (initialValue === null) return null;
|
|
||||||
const input = initialValue;
|
|
||||||
let position = 0;
|
|
||||||
const values = [];
|
|
||||||
let value = "";
|
|
||||||
while (position < initialValue.length) {
|
|
||||||
// 7.1. collect up to " or ,
|
|
||||||
const res = collectSequenceOfCodepoints(
|
|
||||||
initialValue,
|
|
||||||
position,
|
|
||||||
(c) => c !== "\u0022" && c !== "\u002C",
|
|
||||||
);
|
|
||||||
value += res.result;
|
|
||||||
position = res.position;
|
|
||||||
|
|
||||||
if (position < initialValue.length) {
|
|
||||||
if (input[position] === "\u0022") {
|
|
||||||
const res = collectHttpQuotedString(input, position, false);
|
|
||||||
value += res.result;
|
|
||||||
position = res.position;
|
|
||||||
if (position < initialValue.length) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (input[position] !== "\u002C") throw new TypeError("Unreachable");
|
|
||||||
position += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_PREFIX_RE, "");
|
|
||||||
value = StringPrototypeReplaceAll(value, HTTP_TAB_OR_SPACE_SUFFIX_RE, "");
|
|
||||||
|
|
||||||
ArrayPrototypePush(values, value);
|
|
||||||
value = "";
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Headers {
|
|
||||||
/** @type {HeaderList} */
|
|
||||||
[_headerList] = [];
|
|
||||||
/** @type {"immutable" | "request" | "request-no-cors" | "response" | "none"} */
|
|
||||||
[_guard];
|
|
||||||
|
|
||||||
get [_iterableHeaders]() {
|
|
||||||
const list = this[_headerList];
|
|
||||||
|
|
||||||
// The order of steps are not similar to the ones suggested by the
|
|
||||||
// spec but produce the same result.
|
|
||||||
const headers = {};
|
|
||||||
const cookies = [];
|
|
||||||
for (let i = 0; i < list.length; ++i) {
|
|
||||||
const entry = list[i];
|
|
||||||
const name = byteLowerCase(entry[0]);
|
|
||||||
const value = entry[1];
|
|
||||||
if (value === null) throw new TypeError("Unreachable");
|
|
||||||
// The following if statement is not spec compliant.
|
|
||||||
// `set-cookie` is the only header that can not be concatenated,
|
|
||||||
// so must be given to the user as multiple headers.
|
|
||||||
// The else block of the if statement is spec compliant again.
|
|
||||||
if (name === "set-cookie") {
|
|
||||||
ArrayPrototypePush(cookies, [name, value]);
|
|
||||||
} else {
|
|
||||||
// The following code has the same behaviour as getHeader()
|
|
||||||
// at the end of loop. But it avoids looping through the entire
|
|
||||||
// list to combine multiple values with same header name. It
|
|
||||||
// instead gradually combines them as they are found.
|
|
||||||
let header = headers[name];
|
|
||||||
if (header && header.length > 0) {
|
|
||||||
header += "\x2C\x20" + value;
|
|
||||||
} else {
|
|
||||||
header = value;
|
|
||||||
}
|
|
||||||
headers[name] = header;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ArrayPrototypeSort(
|
|
||||||
[
|
|
||||||
...new SafeArrayIterator(ObjectEntries(headers)),
|
|
||||||
...new SafeArrayIterator(cookies),
|
|
||||||
],
|
|
||||||
(a, b) => {
|
|
||||||
const akey = a[0];
|
|
||||||
const bkey = b[0];
|
|
||||||
if (akey > bkey) return 1;
|
|
||||||
if (akey < bkey) return -1;
|
|
||||||
return 0;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {HeadersInit} [init] */
|
|
||||||
constructor(init = undefined) {
|
|
||||||
const prefix = "Failed to construct 'Headers'";
|
|
||||||
if (init !== undefined) {
|
|
||||||
init = webidl.converters["HeadersInit"](init, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
this[_guard] = "none";
|
|
||||||
if (init !== undefined) {
|
|
||||||
fillHeaders(this, init);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @param {string} value
|
|
||||||
*/
|
|
||||||
append(name, value) {
|
|
||||||
webidl.assertBranded(this, HeadersPrototype);
|
|
||||||
const prefix = "Failed to execute 'append' on 'Headers'";
|
|
||||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
|
||||||
name = webidl.converters["ByteString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
value = webidl.converters["ByteString"](value, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
appendHeader(this, name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
*/
|
|
||||||
delete(name) {
|
|
||||||
const prefix = "Failed to execute 'delete' on 'Headers'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
name = webidl.converters["ByteString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
|
||||||
throw new TypeError("Header name is not valid.");
|
|
||||||
}
|
|
||||||
if (this[_guard] == "immutable") {
|
|
||||||
throw new TypeError("Headers are immutable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = this[_headerList];
|
|
||||||
const lowercaseName = byteLowerCase(name);
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
|
||||||
ArrayPrototypeSplice(list, i, 1);
|
ArrayPrototypeSplice(list, i, 1);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!added) {
|
||||||
/**
|
ArrayPrototypePush(list, [name, value]);
|
||||||
* @param {string} name
|
|
||||||
*/
|
|
||||||
get(name) {
|
|
||||||
const prefix = "Failed to execute 'get' on 'Headers'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
name = webidl.converters["ByteString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
|
||||||
throw new TypeError("Header name is not valid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = this[_headerList];
|
|
||||||
return getHeader(list, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
*/
|
|
||||||
has(name) {
|
|
||||||
const prefix = "Failed to execute 'has' on 'Headers'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
name = webidl.converters["ByteString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
|
||||||
throw new TypeError("Header name is not valid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = this[_headerList];
|
|
||||||
const lowercaseName = byteLowerCase(name);
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @param {string} value
|
|
||||||
*/
|
|
||||||
set(name, value) {
|
|
||||||
webidl.assertBranded(this, HeadersPrototype);
|
|
||||||
const prefix = "Failed to execute 'set' on 'Headers'";
|
|
||||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
|
||||||
name = webidl.converters["ByteString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
value = webidl.converters["ByteString"](value, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
|
|
||||||
value = normalizeHeaderValue(value);
|
|
||||||
|
|
||||||
// 2.
|
|
||||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, name)) {
|
|
||||||
throw new TypeError("Header name is not valid.");
|
|
||||||
}
|
|
||||||
if (RegExpPrototypeTest(ILLEGAL_VALUE_CHARS, value)) {
|
|
||||||
throw new TypeError("Header value is not valid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this[_guard] == "immutable") {
|
|
||||||
throw new TypeError("Headers are immutable.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const list = this[_headerList];
|
|
||||||
const lowercaseName = byteLowerCase(name);
|
|
||||||
let added = false;
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (byteLowerCase(list[i][0]) === lowercaseName) {
|
|
||||||
if (!added) {
|
|
||||||
list[i][1] = value;
|
|
||||||
added = true;
|
|
||||||
} else {
|
|
||||||
ArrayPrototypeSplice(list, i, 1);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!added) {
|
|
||||||
ArrayPrototypePush(list, [name, value]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
|
||||||
const headers = {};
|
|
||||||
// deno-lint-ignore prefer-primordials
|
|
||||||
for (const header of this) {
|
|
||||||
headers[header[0]] = header[1];
|
|
||||||
}
|
|
||||||
return `Headers ${inspect(headers)}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
|
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
||||||
|
const headers = {};
|
||||||
webidl.configurePrototype(Headers);
|
// deno-lint-ignore prefer-primordials
|
||||||
const HeadersPrototype = Headers.prototype;
|
for (const header of this) {
|
||||||
|
headers[header[0]] = header[1];
|
||||||
webidl.converters["HeadersInit"] = (V, opts) => {
|
|
||||||
// Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
|
|
||||||
if (webidl.type(V) === "Object" && V !== null) {
|
|
||||||
if (V[SymbolIterator] !== undefined) {
|
|
||||||
return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
|
|
||||||
}
|
|
||||||
return webidl.converters["record<ByteString, ByteString>"](V, opts);
|
|
||||||
}
|
}
|
||||||
throw webidl.makeException(
|
return `Headers ${inspect(headers)}`;
|
||||||
TypeError,
|
}
|
||||||
"The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'",
|
}
|
||||||
opts,
|
|
||||||
);
|
webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
|
||||||
};
|
|
||||||
webidl.converters["Headers"] = webidl.createInterfaceConverter(
|
webidl.configurePrototype(Headers);
|
||||||
"Headers",
|
const HeadersPrototype = Headers.prototype;
|
||||||
Headers.prototype,
|
|
||||||
|
webidl.converters["HeadersInit"] = (V, opts) => {
|
||||||
|
// Union for (sequence<sequence<ByteString>> or record<ByteString, ByteString>)
|
||||||
|
if (webidl.type(V) === "Object" && V !== null) {
|
||||||
|
if (V[SymbolIterator] !== undefined) {
|
||||||
|
return webidl.converters["sequence<sequence<ByteString>>"](V, opts);
|
||||||
|
}
|
||||||
|
return webidl.converters["record<ByteString, ByteString>"](V, opts);
|
||||||
|
}
|
||||||
|
throw webidl.makeException(
|
||||||
|
TypeError,
|
||||||
|
"The provided value is not of type '(sequence<sequence<ByteString>> or record<ByteString, ByteString>)'",
|
||||||
|
opts,
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
webidl.converters["Headers"] = webidl.createInterfaceConverter(
|
||||||
|
"Headers",
|
||||||
|
Headers.prototype,
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {HeaderList} list
|
* @param {HeaderList} list
|
||||||
* @param {"immutable" | "request" | "request-no-cors" | "response" | "none"} guard
|
* @param {"immutable" | "request" | "request-no-cors" | "response" | "none"} guard
|
||||||
* @returns {Headers}
|
* @returns {Headers}
|
||||||
*/
|
*/
|
||||||
function headersFromHeaderList(list, guard) {
|
function headersFromHeaderList(list, guard) {
|
||||||
const headers = webidl.createBranded(Headers);
|
const headers = webidl.createBranded(Headers);
|
||||||
headers[_headerList] = list;
|
headers[_headerList] = list;
|
||||||
headers[_guard] = guard;
|
headers[_guard] = guard;
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Headers}
|
* @param {Headers} headers
|
||||||
* @returns {HeaderList}
|
* @returns {HeaderList}
|
||||||
*/
|
*/
|
||||||
function headerListFromHeaders(headers) {
|
function headerListFromHeaders(headers) {
|
||||||
return headers[_headerList];
|
return headers[_headerList];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Headers}
|
* @param {Headers} headers
|
||||||
* @returns {"immutable" | "request" | "request-no-cors" | "response" | "none"}
|
* @returns {"immutable" | "request" | "request-no-cors" | "response" | "none"}
|
||||||
*/
|
*/
|
||||||
function guardFromHeaders(headers) {
|
function guardFromHeaders(headers) {
|
||||||
return headers[_guard];
|
return headers[_guard];
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.headers = {
|
export {
|
||||||
headersFromHeaderList,
|
fillHeaders,
|
||||||
headerListFromHeaders,
|
getDecodeSplitHeader,
|
||||||
getDecodeSplitHeader,
|
getHeader,
|
||||||
guardFromHeaders,
|
guardFromHeaders,
|
||||||
fillHeaders,
|
headerListFromHeaders,
|
||||||
getHeader,
|
Headers,
|
||||||
Headers,
|
headersFromHeaderList,
|
||||||
};
|
};
|
||||||
})(this);
|
|
||||||
|
|
|
@ -8,516 +8,518 @@
|
||||||
/// <reference path="../web/06_streams_types.d.ts" />
|
/// <reference path="../web/06_streams_types.d.ts" />
|
||||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = globalThis.__bootstrap.webidl;
|
import {
|
||||||
const { Blob, BlobPrototype, File, FilePrototype } =
|
Blob,
|
||||||
globalThis.__bootstrap.file;
|
BlobPrototype,
|
||||||
const {
|
File,
|
||||||
ArrayPrototypePush,
|
FilePrototype,
|
||||||
ArrayPrototypeSlice,
|
} from "internal:ext/web/09_file.js";
|
||||||
ArrayPrototypeSplice,
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
Map,
|
const {
|
||||||
MapPrototypeGet,
|
ArrayPrototypePush,
|
||||||
MapPrototypeSet,
|
ArrayPrototypeSlice,
|
||||||
MathRandom,
|
ArrayPrototypeSplice,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
Map,
|
||||||
Symbol,
|
MapPrototypeGet,
|
||||||
StringFromCharCode,
|
MapPrototypeSet,
|
||||||
StringPrototypeTrim,
|
MathRandom,
|
||||||
StringPrototypeSlice,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
StringPrototypeSplit,
|
Symbol,
|
||||||
StringPrototypeReplace,
|
StringFromCharCode,
|
||||||
StringPrototypeIndexOf,
|
StringPrototypeTrim,
|
||||||
StringPrototypePadStart,
|
StringPrototypeSlice,
|
||||||
StringPrototypeCodePointAt,
|
StringPrototypeSplit,
|
||||||
StringPrototypeReplaceAll,
|
StringPrototypeReplace,
|
||||||
TypeError,
|
StringPrototypeIndexOf,
|
||||||
TypedArrayPrototypeSubarray,
|
StringPrototypePadStart,
|
||||||
} = window.__bootstrap.primordials;
|
StringPrototypeCodePointAt,
|
||||||
|
StringPrototypeReplaceAll,
|
||||||
|
TypeError,
|
||||||
|
TypedArrayPrototypeSubarray,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
const entryList = Symbol("entry list");
|
const entryList = Symbol("entry list");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
* @param {string | Blob} value
|
* @param {string | Blob} value
|
||||||
* @param {string | undefined} filename
|
* @param {string | undefined} filename
|
||||||
* @returns {FormDataEntry}
|
* @returns {FormDataEntry}
|
||||||
*/
|
*/
|
||||||
function createEntry(name, value, filename) {
|
function createEntry(name, value, filename) {
|
||||||
if (
|
if (
|
||||||
ObjectPrototypeIsPrototypeOf(BlobPrototype, value) &&
|
ObjectPrototypeIsPrototypeOf(BlobPrototype, value) &&
|
||||||
!ObjectPrototypeIsPrototypeOf(FilePrototype, value)
|
!ObjectPrototypeIsPrototypeOf(FilePrototype, value)
|
||||||
) {
|
) {
|
||||||
value = new File([value], "blob", { type: value.type });
|
value = new File([value], "blob", { type: value.type });
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(FilePrototype, value) &&
|
||||||
|
filename !== undefined
|
||||||
|
) {
|
||||||
|
value = new File([value], filename, {
|
||||||
|
type: value.type,
|
||||||
|
lastModified: value.lastModified,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
// @ts-expect-error because TS is not smart enough
|
||||||
|
value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef FormDataEntry
|
||||||
|
* @property {string} name
|
||||||
|
* @property {FormDataEntryValue} value
|
||||||
|
*/
|
||||||
|
|
||||||
|
class FormData {
|
||||||
|
/** @type {FormDataEntry[]} */
|
||||||
|
[entryList] = [];
|
||||||
|
|
||||||
|
/** @param {void} form */
|
||||||
|
constructor(form) {
|
||||||
|
if (form !== undefined) {
|
||||||
|
webidl.illegalConstructor();
|
||||||
}
|
}
|
||||||
if (
|
this[webidl.brand] = webidl.brand;
|
||||||
ObjectPrototypeIsPrototypeOf(FilePrototype, value) &&
|
|
||||||
filename !== undefined
|
|
||||||
) {
|
|
||||||
value = new File([value], filename, {
|
|
||||||
type: value.type,
|
|
||||||
lastModified: value.lastModified,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
name,
|
|
||||||
// @ts-expect-error because TS is not smart enough
|
|
||||||
value,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef FormDataEntry
|
* @param {string} name
|
||||||
* @property {string} name
|
* @param {string | Blob} valueOrBlobValue
|
||||||
* @property {FormDataEntryValue} value
|
* @param {string} [filename]
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
|
append(name, valueOrBlobValue, filename) {
|
||||||
|
webidl.assertBranded(this, FormDataPrototype);
|
||||||
|
const prefix = "Failed to execute 'append' on 'FormData'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
|
||||||
class FormData {
|
name = webidl.converters["USVString"](name, {
|
||||||
/** @type {FormDataEntry[]} */
|
prefix,
|
||||||
[entryList] = [];
|
context: "Argument 1",
|
||||||
|
});
|
||||||
/** @param {void} form */
|
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
|
||||||
constructor(form) {
|
valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
|
||||||
if (form !== undefined) {
|
|
||||||
webidl.illegalConstructor();
|
|
||||||
}
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @param {string | Blob} valueOrBlobValue
|
|
||||||
* @param {string} [filename]
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
append(name, valueOrBlobValue, filename) {
|
|
||||||
webidl.assertBranded(this, FormDataPrototype);
|
|
||||||
const prefix = "Failed to execute 'append' on 'FormData'";
|
|
||||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
|
||||||
|
|
||||||
name = webidl.converters["USVString"](name, {
|
|
||||||
prefix,
|
prefix,
|
||||||
context: "Argument 1",
|
context: "Argument 2",
|
||||||
});
|
});
|
||||||
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
|
if (filename !== undefined) {
|
||||||
valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
|
filename = webidl.converters["USVString"](filename, {
|
||||||
prefix,
|
prefix,
|
||||||
context: "Argument 2",
|
context: "Argument 3",
|
||||||
});
|
|
||||||
if (filename !== undefined) {
|
|
||||||
filename = webidl.converters["USVString"](filename, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 3",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
const entry = createEntry(name, valueOrBlobValue, filename);
|
valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
|
||||||
|
prefix,
|
||||||
ArrayPrototypePush(this[entryList], entry);
|
context: "Argument 2",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
const entry = createEntry(name, valueOrBlobValue, filename);
|
||||||
* @param {string} name
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
delete(name) {
|
|
||||||
webidl.assertBranded(this, FormDataPrototype);
|
|
||||||
const prefix = "Failed to execute 'name' on 'FormData'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
|
|
||||||
name = webidl.converters["USVString"](name, {
|
ArrayPrototypePush(this[entryList], entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
delete(name) {
|
||||||
|
webidl.assertBranded(this, FormDataPrototype);
|
||||||
|
const prefix = "Failed to execute 'name' on 'FormData'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
|
||||||
|
name = webidl.converters["USVString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
const list = this[entryList];
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (list[i].name === name) {
|
||||||
|
ArrayPrototypeSplice(list, i, 1);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {FormDataEntryValue | null}
|
||||||
|
*/
|
||||||
|
get(name) {
|
||||||
|
webidl.assertBranded(this, FormDataPrototype);
|
||||||
|
const prefix = "Failed to execute 'get' on 'FormData'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
|
||||||
|
name = webidl.converters["USVString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
const entries = this[entryList];
|
||||||
|
for (let i = 0; i < entries.length; ++i) {
|
||||||
|
const entry = entries[i];
|
||||||
|
if (entry.name === name) return entry.value;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {FormDataEntryValue[]}
|
||||||
|
*/
|
||||||
|
getAll(name) {
|
||||||
|
webidl.assertBranded(this, FormDataPrototype);
|
||||||
|
const prefix = "Failed to execute 'getAll' on 'FormData'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
|
||||||
|
name = webidl.converters["USVString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
const returnList = [];
|
||||||
|
const entries = this[entryList];
|
||||||
|
for (let i = 0; i < entries.length; ++i) {
|
||||||
|
const entry = entries[i];
|
||||||
|
if (entry.name === name) ArrayPrototypePush(returnList, entry.value);
|
||||||
|
}
|
||||||
|
return returnList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
has(name) {
|
||||||
|
webidl.assertBranded(this, FormDataPrototype);
|
||||||
|
const prefix = "Failed to execute 'has' on 'FormData'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
|
||||||
|
name = webidl.converters["USVString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
const entries = this[entryList];
|
||||||
|
for (let i = 0; i < entries.length; ++i) {
|
||||||
|
const entry = entries[i];
|
||||||
|
if (entry.name === name) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} name
|
||||||
|
* @param {string | Blob} valueOrBlobValue
|
||||||
|
* @param {string} [filename]
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
set(name, valueOrBlobValue, filename) {
|
||||||
|
webidl.assertBranded(this, FormDataPrototype);
|
||||||
|
const prefix = "Failed to execute 'set' on 'FormData'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
|
||||||
|
name = webidl.converters["USVString"](name, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
|
||||||
|
valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
|
||||||
prefix,
|
prefix,
|
||||||
context: "Argument 1",
|
context: "Argument 2",
|
||||||
});
|
});
|
||||||
|
if (filename !== undefined) {
|
||||||
|
filename = webidl.converters["USVString"](filename, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 3",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const list = this[entryList];
|
const entry = createEntry(name, valueOrBlobValue, filename);
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (list[i].name === name) {
|
const list = this[entryList];
|
||||||
|
let added = false;
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (list[i].name === name) {
|
||||||
|
if (!added) {
|
||||||
|
list[i] = entry;
|
||||||
|
added = true;
|
||||||
|
} else {
|
||||||
ArrayPrototypeSplice(list, i, 1);
|
ArrayPrototypeSplice(list, i, 1);
|
||||||
i--;
|
i--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!added) {
|
||||||
/**
|
ArrayPrototypePush(list, entry);
|
||||||
* @param {string} name
|
|
||||||
* @returns {FormDataEntryValue | null}
|
|
||||||
*/
|
|
||||||
get(name) {
|
|
||||||
webidl.assertBranded(this, FormDataPrototype);
|
|
||||||
const prefix = "Failed to execute 'get' on 'FormData'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
|
|
||||||
name = webidl.converters["USVString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
const entries = this[entryList];
|
|
||||||
for (let i = 0; i < entries.length; ++i) {
|
|
||||||
const entry = entries[i];
|
|
||||||
if (entry.name === name) return entry.value;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @returns {FormDataEntryValue[]}
|
|
||||||
*/
|
|
||||||
getAll(name) {
|
|
||||||
webidl.assertBranded(this, FormDataPrototype);
|
|
||||||
const prefix = "Failed to execute 'getAll' on 'FormData'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
|
|
||||||
name = webidl.converters["USVString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
const returnList = [];
|
|
||||||
const entries = this[entryList];
|
|
||||||
for (let i = 0; i < entries.length; ++i) {
|
|
||||||
const entry = entries[i];
|
|
||||||
if (entry.name === name) ArrayPrototypePush(returnList, entry.value);
|
|
||||||
}
|
|
||||||
return returnList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
has(name) {
|
|
||||||
webidl.assertBranded(this, FormDataPrototype);
|
|
||||||
const prefix = "Failed to execute 'has' on 'FormData'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
|
|
||||||
name = webidl.converters["USVString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
const entries = this[entryList];
|
|
||||||
for (let i = 0; i < entries.length; ++i) {
|
|
||||||
const entry = entries[i];
|
|
||||||
if (entry.name === name) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @param {string | Blob} valueOrBlobValue
|
|
||||||
* @param {string} [filename]
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
set(name, valueOrBlobValue, filename) {
|
|
||||||
webidl.assertBranded(this, FormDataPrototype);
|
|
||||||
const prefix = "Failed to execute 'set' on 'FormData'";
|
|
||||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
|
||||||
|
|
||||||
name = webidl.converters["USVString"](name, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
|
|
||||||
valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
if (filename !== undefined) {
|
|
||||||
filename = webidl.converters["USVString"](filename, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 3",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const entry = createEntry(name, valueOrBlobValue, filename);
|
|
||||||
|
|
||||||
const list = this[entryList];
|
|
||||||
let added = false;
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (list[i].name === name) {
|
|
||||||
if (!added) {
|
|
||||||
list[i] = entry;
|
|
||||||
added = true;
|
|
||||||
} else {
|
|
||||||
ArrayPrototypeSplice(list, i, 1);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!added) {
|
|
||||||
ArrayPrototypePush(list, entry);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
|
webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
|
||||||
|
|
||||||
webidl.configurePrototype(FormData);
|
webidl.configurePrototype(FormData);
|
||||||
const FormDataPrototype = FormData.prototype;
|
const FormDataPrototype = FormData.prototype;
|
||||||
|
|
||||||
const escape = (str, isFilename) => {
|
const escape = (str, isFilename) => {
|
||||||
const escapeMap = {
|
const escapeMap = {
|
||||||
"\n": "%0A",
|
"\n": "%0A",
|
||||||
"\r": "%0D",
|
"\r": "%0D",
|
||||||
'"': "%22",
|
'"': "%22",
|
||||||
};
|
|
||||||
|
|
||||||
return StringPrototypeReplace(
|
|
||||||
isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n"),
|
|
||||||
/([\n\r"])/g,
|
|
||||||
(c) => escapeMap[c],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
return StringPrototypeReplace(
|
||||||
* convert FormData to a Blob synchronous without reading all of the files
|
isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n"),
|
||||||
* @param {globalThis.FormData} formData
|
/([\n\r"])/g,
|
||||||
*/
|
(c) => escapeMap[c],
|
||||||
function formDataToBlob(formData) {
|
);
|
||||||
const boundary = StringPrototypePadStart(
|
};
|
||||||
StringPrototypeSlice(
|
|
||||||
StringPrototypeReplaceAll(`${MathRandom()}${MathRandom()}`, ".", ""),
|
|
||||||
-28,
|
|
||||||
),
|
|
||||||
32,
|
|
||||||
"-",
|
|
||||||
);
|
|
||||||
const chunks = [];
|
|
||||||
const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="`;
|
|
||||||
|
|
||||||
// deno-lint-ignore prefer-primordials
|
/**
|
||||||
for (const { 0: name, 1: value } of formData) {
|
* convert FormData to a Blob synchronous without reading all of the files
|
||||||
if (typeof value === "string") {
|
* @param {globalThis.FormData} formData
|
||||||
ArrayPrototypePush(
|
*/
|
||||||
chunks,
|
function formDataToBlob(formData) {
|
||||||
prefix + escape(name) + '"' + CRLF + CRLF +
|
const boundary = StringPrototypePadStart(
|
||||||
StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
|
StringPrototypeSlice(
|
||||||
);
|
StringPrototypeReplaceAll(`${MathRandom()}${MathRandom()}`, ".", ""),
|
||||||
} else {
|
-28,
|
||||||
ArrayPrototypePush(
|
),
|
||||||
chunks,
|
32,
|
||||||
prefix + escape(name) + `"; filename="${escape(value.name, true)}"` +
|
"-",
|
||||||
CRLF +
|
);
|
||||||
`Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`,
|
const chunks = [];
|
||||||
value,
|
const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="`;
|
||||||
CRLF,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayPrototypePush(chunks, `--${boundary}--`);
|
// deno-lint-ignore prefer-primordials
|
||||||
|
for (const { 0: name, 1: value } of formData) {
|
||||||
return new Blob(chunks, {
|
if (typeof value === "string") {
|
||||||
type: "multipart/form-data; boundary=" + boundary,
|
ArrayPrototypePush(
|
||||||
});
|
chunks,
|
||||||
}
|
prefix + escape(name) + '"' + CRLF + CRLF +
|
||||||
|
StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
|
||||||
/**
|
);
|
||||||
* @param {string} value
|
} else {
|
||||||
* @returns {Map<string, string>}
|
ArrayPrototypePush(
|
||||||
*/
|
chunks,
|
||||||
function parseContentDisposition(value) {
|
prefix + escape(name) + `"; filename="${escape(value.name, true)}"` +
|
||||||
/** @type {Map<string, string>} */
|
CRLF +
|
||||||
const params = new Map();
|
`Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`,
|
||||||
// Forced to do so for some Map constructor param mismatch
|
value,
|
||||||
const values = ArrayPrototypeSlice(StringPrototypeSplit(value, ";"), 1);
|
CRLF,
|
||||||
for (let i = 0; i < values.length; i++) {
|
|
||||||
const entries = StringPrototypeSplit(StringPrototypeTrim(values[i]), "=");
|
|
||||||
if (entries.length > 1) {
|
|
||||||
MapPrototypeSet(
|
|
||||||
params,
|
|
||||||
entries[0],
|
|
||||||
StringPrototypeReplace(entries[1], /^"([^"]*)"$/, "$1"),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CRLF = "\r\n";
|
|
||||||
const LF = StringPrototypeCodePointAt(CRLF, 1);
|
|
||||||
const CR = StringPrototypeCodePointAt(CRLF, 0);
|
|
||||||
|
|
||||||
class MultipartParser {
|
|
||||||
/**
|
|
||||||
* @param {Uint8Array} body
|
|
||||||
* @param {string | undefined} boundary
|
|
||||||
*/
|
|
||||||
constructor(body, boundary) {
|
|
||||||
if (!boundary) {
|
|
||||||
throw new TypeError("multipart/form-data must provide a boundary");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.boundary = `--${boundary}`;
|
|
||||||
this.body = body;
|
|
||||||
this.boundaryChars = core.encode(this.boundary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} headersText
|
|
||||||
* @returns {{ headers: Headers, disposition: Map<string, string> }}
|
|
||||||
*/
|
|
||||||
#parseHeaders(headersText) {
|
|
||||||
const headers = new Headers();
|
|
||||||
const rawHeaders = StringPrototypeSplit(headersText, "\r\n");
|
|
||||||
for (let i = 0; i < rawHeaders.length; ++i) {
|
|
||||||
const rawHeader = rawHeaders[i];
|
|
||||||
const sepIndex = StringPrototypeIndexOf(rawHeader, ":");
|
|
||||||
if (sepIndex < 0) {
|
|
||||||
continue; // Skip this header
|
|
||||||
}
|
|
||||||
const key = StringPrototypeSlice(rawHeader, 0, sepIndex);
|
|
||||||
const value = StringPrototypeSlice(rawHeader, sepIndex + 1);
|
|
||||||
headers.set(key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const disposition = parseContentDisposition(
|
|
||||||
headers.get("Content-Disposition") ?? "",
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return { headers, disposition };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {FormData}
|
|
||||||
*/
|
|
||||||
parse() {
|
|
||||||
// To have fields body must be at least 2 boundaries + \r\n + --
|
|
||||||
// on the last boundary.
|
|
||||||
if (this.body.length < (this.boundary.length * 2) + 4) {
|
|
||||||
const decodedBody = core.decode(this.body);
|
|
||||||
const lastBoundary = this.boundary + "--";
|
|
||||||
// check if it's an empty valid form data
|
|
||||||
if (
|
|
||||||
decodedBody === lastBoundary ||
|
|
||||||
decodedBody === lastBoundary + "\r\n"
|
|
||||||
) {
|
|
||||||
return new FormData();
|
|
||||||
}
|
|
||||||
throw new TypeError("Unable to parse body as form data.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const formData = new FormData();
|
|
||||||
let headerText = "";
|
|
||||||
let boundaryIndex = 0;
|
|
||||||
let state = 0;
|
|
||||||
let fileStart = 0;
|
|
||||||
|
|
||||||
for (let i = 0; i < this.body.length; i++) {
|
|
||||||
const byte = this.body[i];
|
|
||||||
const prevByte = this.body[i - 1];
|
|
||||||
const isNewLine = byte === LF && prevByte === CR;
|
|
||||||
|
|
||||||
if (state === 1 || state === 2 || state == 3) {
|
|
||||||
headerText += StringFromCharCode(byte);
|
|
||||||
}
|
|
||||||
if (state === 0 && isNewLine) {
|
|
||||||
state = 1;
|
|
||||||
} else if (state === 1 && isNewLine) {
|
|
||||||
state = 2;
|
|
||||||
const headersDone = this.body[i + 1] === CR &&
|
|
||||||
this.body[i + 2] === LF;
|
|
||||||
|
|
||||||
if (headersDone) {
|
|
||||||
state = 3;
|
|
||||||
}
|
|
||||||
} else if (state === 2 && isNewLine) {
|
|
||||||
state = 3;
|
|
||||||
} else if (state === 3 && isNewLine) {
|
|
||||||
state = 4;
|
|
||||||
fileStart = i + 1;
|
|
||||||
} else if (state === 4) {
|
|
||||||
if (this.boundaryChars[boundaryIndex] !== byte) {
|
|
||||||
boundaryIndex = 0;
|
|
||||||
} else {
|
|
||||||
boundaryIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (boundaryIndex >= this.boundary.length) {
|
|
||||||
const { headers, disposition } = this.#parseHeaders(headerText);
|
|
||||||
const content = TypedArrayPrototypeSubarray(
|
|
||||||
this.body,
|
|
||||||
fileStart,
|
|
||||||
i - boundaryIndex - 1,
|
|
||||||
);
|
|
||||||
// https://fetch.spec.whatwg.org/#ref-for-dom-body-formdata
|
|
||||||
const filename = MapPrototypeGet(disposition, "filename");
|
|
||||||
const name = MapPrototypeGet(disposition, "name");
|
|
||||||
|
|
||||||
state = 5;
|
|
||||||
// Reset
|
|
||||||
boundaryIndex = 0;
|
|
||||||
headerText = "";
|
|
||||||
|
|
||||||
if (!name) {
|
|
||||||
continue; // Skip, unknown name
|
|
||||||
}
|
|
||||||
|
|
||||||
if (filename) {
|
|
||||||
const blob = new Blob([content], {
|
|
||||||
type: headers.get("Content-Type") || "application/octet-stream",
|
|
||||||
});
|
|
||||||
formData.append(name, blob, filename);
|
|
||||||
} else {
|
|
||||||
formData.append(name, core.decode(content));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (state === 5 && isNewLine) {
|
|
||||||
state = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return formData;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ArrayPrototypePush(chunks, `--${boundary}--`);
|
||||||
|
|
||||||
|
return new Blob(chunks, {
|
||||||
|
type: "multipart/form-data; boundary=" + boundary,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} value
|
||||||
|
* @returns {Map<string, string>}
|
||||||
|
*/
|
||||||
|
function parseContentDisposition(value) {
|
||||||
|
/** @type {Map<string, string>} */
|
||||||
|
const params = new Map();
|
||||||
|
// Forced to do so for some Map constructor param mismatch
|
||||||
|
const values = ArrayPrototypeSlice(StringPrototypeSplit(value, ";"), 1);
|
||||||
|
for (let i = 0; i < values.length; i++) {
|
||||||
|
const entries = StringPrototypeSplit(StringPrototypeTrim(values[i]), "=");
|
||||||
|
if (entries.length > 1) {
|
||||||
|
MapPrototypeSet(
|
||||||
|
params,
|
||||||
|
entries[0],
|
||||||
|
StringPrototypeReplace(entries[1], /^"([^"]*)"$/, "$1"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CRLF = "\r\n";
|
||||||
|
const LF = StringPrototypeCodePointAt(CRLF, 1);
|
||||||
|
const CR = StringPrototypeCodePointAt(CRLF, 0);
|
||||||
|
|
||||||
|
class MultipartParser {
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} body
|
* @param {Uint8Array} body
|
||||||
* @param {string | undefined} boundary
|
* @param {string | undefined} boundary
|
||||||
* @returns {FormData}
|
|
||||||
*/
|
*/
|
||||||
function parseFormData(body, boundary) {
|
constructor(body, boundary) {
|
||||||
const parser = new MultipartParser(body, boundary);
|
if (!boundary) {
|
||||||
return parser.parse();
|
throw new TypeError("multipart/form-data must provide a boundary");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.boundary = `--${boundary}`;
|
||||||
|
this.body = body;
|
||||||
|
this.boundaryChars = core.encode(this.boundary);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {FormDataEntry[]} entries
|
* @param {string} headersText
|
||||||
* @returns {FormData}
|
* @returns {{ headers: Headers, disposition: Map<string, string> }}
|
||||||
*/
|
*/
|
||||||
function formDataFromEntries(entries) {
|
#parseHeaders(headersText) {
|
||||||
const fd = new FormData();
|
const headers = new Headers();
|
||||||
fd[entryList] = entries;
|
const rawHeaders = StringPrototypeSplit(headersText, "\r\n");
|
||||||
return fd;
|
for (let i = 0; i < rawHeaders.length; ++i) {
|
||||||
|
const rawHeader = rawHeaders[i];
|
||||||
|
const sepIndex = StringPrototypeIndexOf(rawHeader, ":");
|
||||||
|
if (sepIndex < 0) {
|
||||||
|
continue; // Skip this header
|
||||||
|
}
|
||||||
|
const key = StringPrototypeSlice(rawHeader, 0, sepIndex);
|
||||||
|
const value = StringPrototypeSlice(rawHeader, sepIndex + 1);
|
||||||
|
headers.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const disposition = parseContentDisposition(
|
||||||
|
headers.get("Content-Disposition") ?? "",
|
||||||
|
);
|
||||||
|
|
||||||
|
return { headers, disposition };
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.converters["FormData"] = webidl
|
/**
|
||||||
.createInterfaceConverter("FormData", FormDataPrototype);
|
* @returns {FormData}
|
||||||
|
*/
|
||||||
|
parse() {
|
||||||
|
// To have fields body must be at least 2 boundaries + \r\n + --
|
||||||
|
// on the last boundary.
|
||||||
|
if (this.body.length < (this.boundary.length * 2) + 4) {
|
||||||
|
const decodedBody = core.decode(this.body);
|
||||||
|
const lastBoundary = this.boundary + "--";
|
||||||
|
// check if it's an empty valid form data
|
||||||
|
if (
|
||||||
|
decodedBody === lastBoundary ||
|
||||||
|
decodedBody === lastBoundary + "\r\n"
|
||||||
|
) {
|
||||||
|
return new FormData();
|
||||||
|
}
|
||||||
|
throw new TypeError("Unable to parse body as form data.");
|
||||||
|
}
|
||||||
|
|
||||||
globalThis.__bootstrap.formData = {
|
const formData = new FormData();
|
||||||
FormData,
|
let headerText = "";
|
||||||
FormDataPrototype,
|
let boundaryIndex = 0;
|
||||||
formDataToBlob,
|
let state = 0;
|
||||||
parseFormData,
|
let fileStart = 0;
|
||||||
formDataFromEntries,
|
|
||||||
};
|
for (let i = 0; i < this.body.length; i++) {
|
||||||
})(globalThis);
|
const byte = this.body[i];
|
||||||
|
const prevByte = this.body[i - 1];
|
||||||
|
const isNewLine = byte === LF && prevByte === CR;
|
||||||
|
|
||||||
|
if (state === 1 || state === 2 || state == 3) {
|
||||||
|
headerText += StringFromCharCode(byte);
|
||||||
|
}
|
||||||
|
if (state === 0 && isNewLine) {
|
||||||
|
state = 1;
|
||||||
|
} else if (state === 1 && isNewLine) {
|
||||||
|
state = 2;
|
||||||
|
const headersDone = this.body[i + 1] === CR &&
|
||||||
|
this.body[i + 2] === LF;
|
||||||
|
|
||||||
|
if (headersDone) {
|
||||||
|
state = 3;
|
||||||
|
}
|
||||||
|
} else if (state === 2 && isNewLine) {
|
||||||
|
state = 3;
|
||||||
|
} else if (state === 3 && isNewLine) {
|
||||||
|
state = 4;
|
||||||
|
fileStart = i + 1;
|
||||||
|
} else if (state === 4) {
|
||||||
|
if (this.boundaryChars[boundaryIndex] !== byte) {
|
||||||
|
boundaryIndex = 0;
|
||||||
|
} else {
|
||||||
|
boundaryIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (boundaryIndex >= this.boundary.length) {
|
||||||
|
const { headers, disposition } = this.#parseHeaders(headerText);
|
||||||
|
const content = TypedArrayPrototypeSubarray(
|
||||||
|
this.body,
|
||||||
|
fileStart,
|
||||||
|
i - boundaryIndex - 1,
|
||||||
|
);
|
||||||
|
// https://fetch.spec.whatwg.org/#ref-for-dom-body-formdata
|
||||||
|
const filename = MapPrototypeGet(disposition, "filename");
|
||||||
|
const name = MapPrototypeGet(disposition, "name");
|
||||||
|
|
||||||
|
state = 5;
|
||||||
|
// Reset
|
||||||
|
boundaryIndex = 0;
|
||||||
|
headerText = "";
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
continue; // Skip, unknown name
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filename) {
|
||||||
|
const blob = new Blob([content], {
|
||||||
|
type: headers.get("Content-Type") || "application/octet-stream",
|
||||||
|
});
|
||||||
|
formData.append(name, blob, filename);
|
||||||
|
} else {
|
||||||
|
formData.append(name, core.decode(content));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (state === 5 && isNewLine) {
|
||||||
|
state = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} body
|
||||||
|
* @param {string | undefined} boundary
|
||||||
|
* @returns {FormData}
|
||||||
|
*/
|
||||||
|
function parseFormData(body, boundary) {
|
||||||
|
const parser = new MultipartParser(body, boundary);
|
||||||
|
return parser.parse();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {FormDataEntry[]} entries
|
||||||
|
* @returns {FormData}
|
||||||
|
*/
|
||||||
|
function formDataFromEntries(entries) {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd[entryList] = entries;
|
||||||
|
return fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.converters["FormData"] = webidl
|
||||||
|
.createInterfaceConverter("FormData", FormDataPrototype);
|
||||||
|
|
||||||
|
export {
|
||||||
|
FormData,
|
||||||
|
formDataFromEntries,
|
||||||
|
FormDataPrototype,
|
||||||
|
formDataToBlob,
|
||||||
|
parseFormData,
|
||||||
|
};
|
||||||
|
|
|
@ -10,462 +10,462 @@
|
||||||
/// <reference path="../web/06_streams_types.d.ts" />
|
/// <reference path="../web/06_streams_types.d.ts" />
|
||||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = globalThis.__bootstrap.webidl;
|
import {
|
||||||
const { parseUrlEncoded } = globalThis.__bootstrap.url;
|
parseUrlEncoded,
|
||||||
const { URLSearchParamsPrototype } = globalThis.__bootstrap.url;
|
URLSearchParamsPrototype,
|
||||||
const {
|
} from "internal:ext/url/00_url.js";
|
||||||
parseFormData,
|
import {
|
||||||
formDataFromEntries,
|
formDataFromEntries,
|
||||||
formDataToBlob,
|
FormDataPrototype,
|
||||||
FormDataPrototype,
|
formDataToBlob,
|
||||||
} = globalThis.__bootstrap.formData;
|
parseFormData,
|
||||||
const mimesniff = globalThis.__bootstrap.mimesniff;
|
} from "internal:ext/fetch/21_formdata.js";
|
||||||
const { BlobPrototype } = globalThis.__bootstrap.file;
|
import * as mimesniff from "internal:ext/web/01_mimesniff.js";
|
||||||
const {
|
import { BlobPrototype } from "internal:ext/web/09_file.js";
|
||||||
isReadableStreamDisturbed,
|
import {
|
||||||
errorReadableStream,
|
createProxy,
|
||||||
readableStreamClose,
|
errorReadableStream,
|
||||||
readableStreamDisturb,
|
isReadableStreamDisturbed,
|
||||||
readableStreamCollectIntoUint8Array,
|
readableStreamClose,
|
||||||
readableStreamThrowIfErrored,
|
readableStreamCollectIntoUint8Array,
|
||||||
createProxy,
|
readableStreamDisturb,
|
||||||
ReadableStreamPrototype,
|
ReadableStreamPrototype,
|
||||||
} = globalThis.__bootstrap.streams;
|
readableStreamThrowIfErrored,
|
||||||
const {
|
} from "internal:ext/web/06_streams.js";
|
||||||
ArrayBufferPrototype,
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
ArrayBufferIsView,
|
const {
|
||||||
ArrayPrototypeMap,
|
ArrayBufferPrototype,
|
||||||
JSONParse,
|
ArrayBufferIsView,
|
||||||
ObjectDefineProperties,
|
ArrayPrototypeMap,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
JSONParse,
|
||||||
// TODO(lucacasonato): add SharedArrayBuffer to primordials
|
ObjectDefineProperties,
|
||||||
// SharedArrayBufferPrototype
|
ObjectPrototypeIsPrototypeOf,
|
||||||
TypedArrayPrototypeSlice,
|
// TODO(lucacasonato): add SharedArrayBuffer to primordials
|
||||||
TypeError,
|
// SharedArrayBufferPrototype
|
||||||
Uint8Array,
|
TypedArrayPrototypeSlice,
|
||||||
Uint8ArrayPrototype,
|
TypeError,
|
||||||
} = window.__bootstrap.primordials;
|
Uint8Array,
|
||||||
|
Uint8ArrayPrototype,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array | string} chunk
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
function chunkToU8(chunk) {
|
||||||
|
return typeof chunk === "string" ? core.encode(chunk) : chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array | string} chunk
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function chunkToString(chunk) {
|
||||||
|
return typeof chunk === "string" ? chunk : core.decode(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
class InnerBody {
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array | string} chunk
|
* @param {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} stream
|
||||||
* @returns {Uint8Array}
|
|
||||||
*/
|
*/
|
||||||
function chunkToU8(chunk) {
|
constructor(stream) {
|
||||||
return typeof chunk === "string" ? core.encode(chunk) : chunk;
|
/** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
|
||||||
|
this.streamOrStatic = stream ??
|
||||||
|
{ body: new Uint8Array(), consumed: false };
|
||||||
|
/** @type {null | Uint8Array | string | Blob | FormData} */
|
||||||
|
this.source = null;
|
||||||
|
/** @type {null | number} */
|
||||||
|
this.length = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
get stream() {
|
||||||
* @param {Uint8Array | string} chunk
|
if (
|
||||||
* @returns {string}
|
!ObjectPrototypeIsPrototypeOf(
|
||||||
*/
|
ReadableStreamPrototype,
|
||||||
function chunkToString(chunk) {
|
this.streamOrStatic,
|
||||||
return typeof chunk === "string" ? chunk : core.decode(chunk);
|
)
|
||||||
}
|
) {
|
||||||
|
const { body, consumed } = this.streamOrStatic;
|
||||||
class InnerBody {
|
if (consumed) {
|
||||||
/**
|
this.streamOrStatic = new ReadableStream();
|
||||||
* @param {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} stream
|
this.streamOrStatic.getReader();
|
||||||
*/
|
readableStreamDisturb(this.streamOrStatic);
|
||||||
constructor(stream) {
|
readableStreamClose(this.streamOrStatic);
|
||||||
/** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
|
|
||||||
this.streamOrStatic = stream ??
|
|
||||||
{ body: new Uint8Array(), consumed: false };
|
|
||||||
/** @type {null | Uint8Array | string | Blob | FormData} */
|
|
||||||
this.source = null;
|
|
||||||
/** @type {null | number} */
|
|
||||||
this.length = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
get stream() {
|
|
||||||
if (
|
|
||||||
!ObjectPrototypeIsPrototypeOf(
|
|
||||||
ReadableStreamPrototype,
|
|
||||||
this.streamOrStatic,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
const { body, consumed } = this.streamOrStatic;
|
|
||||||
if (consumed) {
|
|
||||||
this.streamOrStatic = new ReadableStream();
|
|
||||||
this.streamOrStatic.getReader();
|
|
||||||
readableStreamDisturb(this.streamOrStatic);
|
|
||||||
readableStreamClose(this.streamOrStatic);
|
|
||||||
} else {
|
|
||||||
this.streamOrStatic = new ReadableStream({
|
|
||||||
start(controller) {
|
|
||||||
controller.enqueue(chunkToU8(body));
|
|
||||||
controller.close();
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.streamOrStatic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://fetch.spec.whatwg.org/#body-unusable
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
unusable() {
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
ReadableStreamPrototype,
|
|
||||||
this.streamOrStatic,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return this.streamOrStatic.locked ||
|
|
||||||
isReadableStreamDisturbed(this.streamOrStatic);
|
|
||||||
}
|
|
||||||
return this.streamOrStatic.consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
consumed() {
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
ReadableStreamPrototype,
|
|
||||||
this.streamOrStatic,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
return isReadableStreamDisturbed(this.streamOrStatic);
|
|
||||||
}
|
|
||||||
return this.streamOrStatic.consumed;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://fetch.spec.whatwg.org/#concept-body-consume-body
|
|
||||||
* @returns {Promise<Uint8Array>}
|
|
||||||
*/
|
|
||||||
consume() {
|
|
||||||
if (this.unusable()) throw new TypeError("Body already consumed.");
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
ReadableStreamPrototype,
|
|
||||||
this.streamOrStatic,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
readableStreamThrowIfErrored(this.stream);
|
|
||||||
return readableStreamCollectIntoUint8Array(this.stream);
|
|
||||||
} else {
|
} else {
|
||||||
this.streamOrStatic.consumed = true;
|
this.streamOrStatic = new ReadableStream({
|
||||||
return this.streamOrStatic.body;
|
start(controller) {
|
||||||
}
|
controller.enqueue(chunkToU8(body));
|
||||||
}
|
controller.close();
|
||||||
|
},
|
||||||
cancel(error) {
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
ReadableStreamPrototype,
|
|
||||||
this.streamOrStatic,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
this.streamOrStatic.cancel(error);
|
|
||||||
} else {
|
|
||||||
this.streamOrStatic.consumed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
error(error) {
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
ReadableStreamPrototype,
|
|
||||||
this.streamOrStatic,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
errorReadableStream(this.streamOrStatic, error);
|
|
||||||
} else {
|
|
||||||
this.streamOrStatic.consumed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {InnerBody}
|
|
||||||
*/
|
|
||||||
clone() {
|
|
||||||
const { 0: out1, 1: out2 } = this.stream.tee();
|
|
||||||
this.streamOrStatic = out1;
|
|
||||||
const second = new InnerBody(out2);
|
|
||||||
second.source = core.deserialize(core.serialize(this.source));
|
|
||||||
second.length = this.length;
|
|
||||||
return second;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {InnerBody}
|
|
||||||
*/
|
|
||||||
createProxy() {
|
|
||||||
let proxyStreamOrStatic;
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
ReadableStreamPrototype,
|
|
||||||
this.streamOrStatic,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
proxyStreamOrStatic = createProxy(this.streamOrStatic);
|
|
||||||
} else {
|
|
||||||
proxyStreamOrStatic = { ...this.streamOrStatic };
|
|
||||||
this.streamOrStatic.consumed = true;
|
|
||||||
}
|
|
||||||
const proxy = new InnerBody(proxyStreamOrStatic);
|
|
||||||
proxy.source = this.source;
|
|
||||||
proxy.length = this.length;
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {any} prototype
|
|
||||||
* @param {symbol} bodySymbol
|
|
||||||
* @param {symbol} mimeTypeSymbol
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
|
|
||||||
async function consumeBody(object, type) {
|
|
||||||
webidl.assertBranded(object, prototype);
|
|
||||||
|
|
||||||
const body = object[bodySymbol] !== null
|
|
||||||
? await object[bodySymbol].consume()
|
|
||||||
: new Uint8Array();
|
|
||||||
|
|
||||||
const mimeType = type === "Blob" || type === "FormData"
|
|
||||||
? object[mimeTypeSymbol]
|
|
||||||
: null;
|
|
||||||
return packageData(body, type, mimeType);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {PropertyDescriptorMap} */
|
|
||||||
const mixin = {
|
|
||||||
body: {
|
|
||||||
/**
|
|
||||||
* @returns {ReadableStream<Uint8Array> | null}
|
|
||||||
*/
|
|
||||||
get() {
|
|
||||||
webidl.assertBranded(this, prototype);
|
|
||||||
if (this[bodySymbol] === null) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return this[bodySymbol].stream;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
bodyUsed: {
|
|
||||||
/**
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
get() {
|
|
||||||
webidl.assertBranded(this, prototype);
|
|
||||||
if (this[bodySymbol] !== null) {
|
|
||||||
return this[bodySymbol].consumed();
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
arrayBuffer: {
|
|
||||||
/** @returns {Promise<ArrayBuffer>} */
|
|
||||||
value: function arrayBuffer() {
|
|
||||||
return consumeBody(this, "ArrayBuffer");
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
blob: {
|
|
||||||
/** @returns {Promise<Blob>} */
|
|
||||||
value: function blob() {
|
|
||||||
return consumeBody(this, "Blob");
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
formData: {
|
|
||||||
/** @returns {Promise<FormData>} */
|
|
||||||
value: function formData() {
|
|
||||||
return consumeBody(this, "FormData");
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
json: {
|
|
||||||
/** @returns {Promise<any>} */
|
|
||||||
value: function json() {
|
|
||||||
return consumeBody(this, "JSON");
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
text: {
|
|
||||||
/** @returns {Promise<string>} */
|
|
||||||
value: function text() {
|
|
||||||
return consumeBody(this, "text");
|
|
||||||
},
|
|
||||||
writable: true,
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return ObjectDefineProperties(prototype, mixin);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://fetch.spec.whatwg.org/#concept-body-package-data
|
|
||||||
* @param {Uint8Array | string} bytes
|
|
||||||
* @param {"ArrayBuffer" | "Blob" | "FormData" | "JSON" | "text"} type
|
|
||||||
* @param {MimeType | null} [mimeType]
|
|
||||||
*/
|
|
||||||
function packageData(bytes, type, mimeType) {
|
|
||||||
switch (type) {
|
|
||||||
case "ArrayBuffer":
|
|
||||||
return chunkToU8(bytes).buffer;
|
|
||||||
case "Blob":
|
|
||||||
return new Blob([bytes], {
|
|
||||||
type: mimeType !== null ? mimesniff.serializeMimeType(mimeType) : "",
|
|
||||||
});
|
});
|
||||||
case "FormData": {
|
}
|
||||||
if (mimeType !== null) {
|
}
|
||||||
const essence = mimesniff.essence(mimeType);
|
return this.streamOrStatic;
|
||||||
if (essence === "multipart/form-data") {
|
}
|
||||||
const boundary = mimeType.parameters.get("boundary");
|
|
||||||
if (boundary === null) {
|
/**
|
||||||
throw new TypeError(
|
* https://fetch.spec.whatwg.org/#body-unusable
|
||||||
"Missing boundary parameter in mime type of multipart formdata.",
|
* @returns {boolean}
|
||||||
);
|
*/
|
||||||
}
|
unusable() {
|
||||||
return parseFormData(chunkToU8(bytes), boundary);
|
if (
|
||||||
} else if (essence === "application/x-www-form-urlencoded") {
|
ObjectPrototypeIsPrototypeOf(
|
||||||
// TODO(@AaronO): pass as-is with StringOrBuffer in op-layer
|
ReadableStreamPrototype,
|
||||||
const entries = parseUrlEncoded(chunkToU8(bytes));
|
this.streamOrStatic,
|
||||||
return formDataFromEntries(
|
)
|
||||||
ArrayPrototypeMap(
|
) {
|
||||||
entries,
|
return this.streamOrStatic.locked ||
|
||||||
(x) => ({ name: x[0], value: x[1] }),
|
isReadableStreamDisturbed(this.streamOrStatic);
|
||||||
),
|
}
|
||||||
|
return this.streamOrStatic.consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
consumed() {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(
|
||||||
|
ReadableStreamPrototype,
|
||||||
|
this.streamOrStatic,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return isReadableStreamDisturbed(this.streamOrStatic);
|
||||||
|
}
|
||||||
|
return this.streamOrStatic.consumed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://fetch.spec.whatwg.org/#concept-body-consume-body
|
||||||
|
* @returns {Promise<Uint8Array>}
|
||||||
|
*/
|
||||||
|
consume() {
|
||||||
|
if (this.unusable()) throw new TypeError("Body already consumed.");
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(
|
||||||
|
ReadableStreamPrototype,
|
||||||
|
this.streamOrStatic,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
readableStreamThrowIfErrored(this.stream);
|
||||||
|
return readableStreamCollectIntoUint8Array(this.stream);
|
||||||
|
} else {
|
||||||
|
this.streamOrStatic.consumed = true;
|
||||||
|
return this.streamOrStatic.body;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancel(error) {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(
|
||||||
|
ReadableStreamPrototype,
|
||||||
|
this.streamOrStatic,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
this.streamOrStatic.cancel(error);
|
||||||
|
} else {
|
||||||
|
this.streamOrStatic.consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
error(error) {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(
|
||||||
|
ReadableStreamPrototype,
|
||||||
|
this.streamOrStatic,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
errorReadableStream(this.streamOrStatic, error);
|
||||||
|
} else {
|
||||||
|
this.streamOrStatic.consumed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {InnerBody}
|
||||||
|
*/
|
||||||
|
clone() {
|
||||||
|
const { 0: out1, 1: out2 } = this.stream.tee();
|
||||||
|
this.streamOrStatic = out1;
|
||||||
|
const second = new InnerBody(out2);
|
||||||
|
second.source = core.deserialize(core.serialize(this.source));
|
||||||
|
second.length = this.length;
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {InnerBody}
|
||||||
|
*/
|
||||||
|
createProxy() {
|
||||||
|
let proxyStreamOrStatic;
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(
|
||||||
|
ReadableStreamPrototype,
|
||||||
|
this.streamOrStatic,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
proxyStreamOrStatic = createProxy(this.streamOrStatic);
|
||||||
|
} else {
|
||||||
|
proxyStreamOrStatic = { ...this.streamOrStatic };
|
||||||
|
this.streamOrStatic.consumed = true;
|
||||||
|
}
|
||||||
|
const proxy = new InnerBody(proxyStreamOrStatic);
|
||||||
|
proxy.source = this.source;
|
||||||
|
proxy.length = this.length;
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} prototype
|
||||||
|
* @param {symbol} bodySymbol
|
||||||
|
* @param {symbol} mimeTypeSymbol
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function mixinBody(prototype, bodySymbol, mimeTypeSymbol) {
|
||||||
|
async function consumeBody(object, type) {
|
||||||
|
webidl.assertBranded(object, prototype);
|
||||||
|
|
||||||
|
const body = object[bodySymbol] !== null
|
||||||
|
? await object[bodySymbol].consume()
|
||||||
|
: new Uint8Array();
|
||||||
|
|
||||||
|
const mimeType = type === "Blob" || type === "FormData"
|
||||||
|
? object[mimeTypeSymbol]
|
||||||
|
: null;
|
||||||
|
return packageData(body, type, mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {PropertyDescriptorMap} */
|
||||||
|
const mixin = {
|
||||||
|
body: {
|
||||||
|
/**
|
||||||
|
* @returns {ReadableStream<Uint8Array> | null}
|
||||||
|
*/
|
||||||
|
get() {
|
||||||
|
webidl.assertBranded(this, prototype);
|
||||||
|
if (this[bodySymbol] === null) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return this[bodySymbol].stream;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
bodyUsed: {
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get() {
|
||||||
|
webidl.assertBranded(this, prototype);
|
||||||
|
if (this[bodySymbol] !== null) {
|
||||||
|
return this[bodySymbol].consumed();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
arrayBuffer: {
|
||||||
|
/** @returns {Promise<ArrayBuffer>} */
|
||||||
|
value: function arrayBuffer() {
|
||||||
|
return consumeBody(this, "ArrayBuffer");
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
blob: {
|
||||||
|
/** @returns {Promise<Blob>} */
|
||||||
|
value: function blob() {
|
||||||
|
return consumeBody(this, "Blob");
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
formData: {
|
||||||
|
/** @returns {Promise<FormData>} */
|
||||||
|
value: function formData() {
|
||||||
|
return consumeBody(this, "FormData");
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
json: {
|
||||||
|
/** @returns {Promise<any>} */
|
||||||
|
value: function json() {
|
||||||
|
return consumeBody(this, "JSON");
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
/** @returns {Promise<string>} */
|
||||||
|
value: function text() {
|
||||||
|
return consumeBody(this, "text");
|
||||||
|
},
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return ObjectDefineProperties(prototype, mixin);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://fetch.spec.whatwg.org/#concept-body-package-data
|
||||||
|
* @param {Uint8Array | string} bytes
|
||||||
|
* @param {"ArrayBuffer" | "Blob" | "FormData" | "JSON" | "text"} type
|
||||||
|
* @param {MimeType | null} [mimeType]
|
||||||
|
*/
|
||||||
|
function packageData(bytes, type, mimeType) {
|
||||||
|
switch (type) {
|
||||||
|
case "ArrayBuffer":
|
||||||
|
return chunkToU8(bytes).buffer;
|
||||||
|
case "Blob":
|
||||||
|
return new Blob([bytes], {
|
||||||
|
type: mimeType !== null ? mimesniff.serializeMimeType(mimeType) : "",
|
||||||
|
});
|
||||||
|
case "FormData": {
|
||||||
|
if (mimeType !== null) {
|
||||||
|
const essence = mimesniff.essence(mimeType);
|
||||||
|
if (essence === "multipart/form-data") {
|
||||||
|
const boundary = mimeType.parameters.get("boundary");
|
||||||
|
if (boundary === null) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Missing boundary parameter in mime type of multipart formdata.",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
throw new TypeError("Body can not be decoded as form data");
|
return parseFormData(chunkToU8(bytes), boundary);
|
||||||
|
} else if (essence === "application/x-www-form-urlencoded") {
|
||||||
|
// TODO(@AaronO): pass as-is with StringOrBuffer in op-layer
|
||||||
|
const entries = parseUrlEncoded(chunkToU8(bytes));
|
||||||
|
return formDataFromEntries(
|
||||||
|
ArrayPrototypeMap(
|
||||||
|
entries,
|
||||||
|
(x) => ({ name: x[0], value: x[1] }),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
throw new TypeError("Missing content type");
|
throw new TypeError("Body can not be decoded as form data");
|
||||||
}
|
}
|
||||||
case "JSON":
|
throw new TypeError("Missing content type");
|
||||||
return JSONParse(chunkToString(bytes));
|
}
|
||||||
case "text":
|
case "JSON":
|
||||||
return chunkToString(bytes);
|
return JSONParse(chunkToString(bytes));
|
||||||
|
case "text":
|
||||||
|
return chunkToString(bytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {BodyInit} object
|
||||||
|
* @returns {{body: InnerBody, contentType: string | null}}
|
||||||
|
*/
|
||||||
|
function extractBody(object) {
|
||||||
|
/** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
|
||||||
|
let stream;
|
||||||
|
let source = null;
|
||||||
|
let length = null;
|
||||||
|
let contentType = null;
|
||||||
|
if (typeof object === "string") {
|
||||||
|
source = object;
|
||||||
|
contentType = "text/plain;charset=UTF-8";
|
||||||
|
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, object)) {
|
||||||
|
stream = object.stream();
|
||||||
|
source = object;
|
||||||
|
length = object.size;
|
||||||
|
if (object.type.length !== 0) {
|
||||||
|
contentType = object.type;
|
||||||
|
}
|
||||||
|
} else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, object)) {
|
||||||
|
// Fast(er) path for common case of Uint8Array
|
||||||
|
const copy = TypedArrayPrototypeSlice(object, 0, object.byteLength);
|
||||||
|
source = copy;
|
||||||
|
} else if (
|
||||||
|
ArrayBufferIsView(object) ||
|
||||||
|
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, object)
|
||||||
|
) {
|
||||||
|
const u8 = ArrayBufferIsView(object)
|
||||||
|
? new Uint8Array(
|
||||||
|
object.buffer,
|
||||||
|
object.byteOffset,
|
||||||
|
object.byteLength,
|
||||||
|
)
|
||||||
|
: new Uint8Array(object);
|
||||||
|
const copy = TypedArrayPrototypeSlice(u8, 0, u8.byteLength);
|
||||||
|
source = copy;
|
||||||
|
} else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, object)) {
|
||||||
|
const res = formDataToBlob(object);
|
||||||
|
stream = res.stream();
|
||||||
|
source = res;
|
||||||
|
length = res.size;
|
||||||
|
contentType = res.type;
|
||||||
|
} else if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, object)
|
||||||
|
) {
|
||||||
|
// TODO(@satyarohith): not sure what primordial here.
|
||||||
|
source = object.toString();
|
||||||
|
contentType = "application/x-www-form-urlencoded;charset=UTF-8";
|
||||||
|
} else if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, object)) {
|
||||||
|
stream = object;
|
||||||
|
if (object.locked || isReadableStreamDisturbed(object)) {
|
||||||
|
throw new TypeError("ReadableStream is locked or disturbed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (typeof source === "string") {
|
||||||
/**
|
// WARNING: this deviates from spec (expects length to be set)
|
||||||
* @param {BodyInit} object
|
// https://fetch.spec.whatwg.org/#bodyinit > 7.
|
||||||
* @returns {{body: InnerBody, contentType: string | null}}
|
// no observable side-effect for users so far, but could change
|
||||||
*/
|
stream = { body: source, consumed: false };
|
||||||
function extractBody(object) {
|
length = null; // NOTE: string length != byte length
|
||||||
/** @type {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} */
|
} else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, source)) {
|
||||||
let stream;
|
stream = { body: source, consumed: false };
|
||||||
let source = null;
|
length = source.byteLength;
|
||||||
let length = null;
|
|
||||||
let contentType = null;
|
|
||||||
if (typeof object === "string") {
|
|
||||||
source = object;
|
|
||||||
contentType = "text/plain;charset=UTF-8";
|
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, object)) {
|
|
||||||
stream = object.stream();
|
|
||||||
source = object;
|
|
||||||
length = object.size;
|
|
||||||
if (object.type.length !== 0) {
|
|
||||||
contentType = object.type;
|
|
||||||
}
|
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, object)) {
|
|
||||||
// Fast(er) path for common case of Uint8Array
|
|
||||||
const copy = TypedArrayPrototypeSlice(object, 0, object.byteLength);
|
|
||||||
source = copy;
|
|
||||||
} else if (
|
|
||||||
ArrayBufferIsView(object) ||
|
|
||||||
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, object)
|
|
||||||
) {
|
|
||||||
const u8 = ArrayBufferIsView(object)
|
|
||||||
? new Uint8Array(
|
|
||||||
object.buffer,
|
|
||||||
object.byteOffset,
|
|
||||||
object.byteLength,
|
|
||||||
)
|
|
||||||
: new Uint8Array(object);
|
|
||||||
const copy = TypedArrayPrototypeSlice(u8, 0, u8.byteLength);
|
|
||||||
source = copy;
|
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, object)) {
|
|
||||||
const res = formDataToBlob(object);
|
|
||||||
stream = res.stream();
|
|
||||||
source = res;
|
|
||||||
length = res.size;
|
|
||||||
contentType = res.type;
|
|
||||||
} else if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, object)
|
|
||||||
) {
|
|
||||||
// TODO(@satyarohith): not sure what primordial here.
|
|
||||||
source = object.toString();
|
|
||||||
contentType = "application/x-www-form-urlencoded;charset=UTF-8";
|
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, object)) {
|
|
||||||
stream = object;
|
|
||||||
if (object.locked || isReadableStreamDisturbed(object)) {
|
|
||||||
throw new TypeError("ReadableStream is locked or disturbed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (typeof source === "string") {
|
|
||||||
// WARNING: this deviates from spec (expects length to be set)
|
|
||||||
// https://fetch.spec.whatwg.org/#bodyinit > 7.
|
|
||||||
// no observable side-effect for users so far, but could change
|
|
||||||
stream = { body: source, consumed: false };
|
|
||||||
length = null; // NOTE: string length != byte length
|
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, source)) {
|
|
||||||
stream = { body: source, consumed: false };
|
|
||||||
length = source.byteLength;
|
|
||||||
}
|
|
||||||
const body = new InnerBody(stream);
|
|
||||||
body.source = source;
|
|
||||||
body.length = length;
|
|
||||||
return { body, contentType };
|
|
||||||
}
|
}
|
||||||
|
const body = new InnerBody(stream);
|
||||||
|
body.source = source;
|
||||||
|
body.length = length;
|
||||||
|
return { body, contentType };
|
||||||
|
}
|
||||||
|
|
||||||
webidl.converters["BodyInit_DOMString"] = (V, opts) => {
|
webidl.converters["BodyInit_DOMString"] = (V, opts) => {
|
||||||
// Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
|
// Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
|
||||||
if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, V)) {
|
if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, V)) {
|
||||||
return webidl.converters["ReadableStream"](V, opts);
|
return webidl.converters["ReadableStream"](V, opts);
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
|
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
|
||||||
return webidl.converters["Blob"](V, opts);
|
return webidl.converters["Blob"](V, opts);
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, V)) {
|
} else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, V)) {
|
||||||
return webidl.converters["FormData"](V, opts);
|
return webidl.converters["FormData"](V, opts);
|
||||||
} else if (ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, V)) {
|
} else if (ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, V)) {
|
||||||
return webidl.converters["URLSearchParams"](V, opts);
|
return webidl.converters["URLSearchParams"](V, opts);
|
||||||
|
}
|
||||||
|
if (typeof V === "object") {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) ||
|
||||||
|
// deno-lint-ignore prefer-primordials
|
||||||
|
ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V)
|
||||||
|
) {
|
||||||
|
return webidl.converters["ArrayBuffer"](V, opts);
|
||||||
}
|
}
|
||||||
if (typeof V === "object") {
|
if (ArrayBufferIsView(V)) {
|
||||||
if (
|
return webidl.converters["ArrayBufferView"](V, opts);
|
||||||
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V) ||
|
|
||||||
// deno-lint-ignore prefer-primordials
|
|
||||||
ObjectPrototypeIsPrototypeOf(SharedArrayBuffer.prototype, V)
|
|
||||||
) {
|
|
||||||
return webidl.converters["ArrayBuffer"](V, opts);
|
|
||||||
}
|
|
||||||
if (ArrayBufferIsView(V)) {
|
|
||||||
return webidl.converters["ArrayBufferView"](V, opts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// BodyInit conversion is passed to extractBody(), which calls core.encode().
|
}
|
||||||
// core.encode() will UTF-8 encode strings with replacement, being equivalent to the USV normalization.
|
// BodyInit conversion is passed to extractBody(), which calls core.encode().
|
||||||
// Therefore we can convert to DOMString instead of USVString and avoid a costly redundant conversion.
|
// core.encode() will UTF-8 encode strings with replacement, being equivalent to the USV normalization.
|
||||||
return webidl.converters["DOMString"](V, opts);
|
// Therefore we can convert to DOMString instead of USVString and avoid a costly redundant conversion.
|
||||||
};
|
return webidl.converters["DOMString"](V, opts);
|
||||||
webidl.converters["BodyInit_DOMString?"] = webidl.createNullableConverter(
|
};
|
||||||
webidl.converters["BodyInit_DOMString"],
|
webidl.converters["BodyInit_DOMString?"] = webidl.createNullableConverter(
|
||||||
);
|
webidl.converters["BodyInit_DOMString"],
|
||||||
|
);
|
||||||
|
|
||||||
window.__bootstrap.fetchBody = { mixinBody, InnerBody, extractBody };
|
export { extractBody, InnerBody, mixinBody };
|
||||||
})(globalThis);
|
|
||||||
|
|
|
@ -9,40 +9,34 @@
|
||||||
/// <reference path="../web/06_streams_types.d.ts" />
|
/// <reference path="../web/06_streams_types.d.ts" />
|
||||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Deno.CreateHttpClientOptions} options
|
||||||
|
* @returns {HttpClient}
|
||||||
|
*/
|
||||||
|
function createHttpClient(options) {
|
||||||
|
options.caCerts ??= [];
|
||||||
|
return new HttpClient(
|
||||||
|
ops.op_fetch_custom_client(
|
||||||
|
options,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
class HttpClient {
|
||||||
/**
|
/**
|
||||||
* @param {Deno.CreateHttpClientOptions} options
|
* @param {number} rid
|
||||||
* @returns {HttpClient}
|
|
||||||
*/
|
*/
|
||||||
function createHttpClient(options) {
|
constructor(rid) {
|
||||||
options.caCerts ??= [];
|
this.rid = rid;
|
||||||
return new HttpClient(
|
|
||||||
ops.op_fetch_custom_client(
|
|
||||||
options,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
close() {
|
||||||
class HttpClient {
|
core.close(this.rid);
|
||||||
/**
|
|
||||||
* @param {number} rid
|
|
||||||
*/
|
|
||||||
constructor(rid) {
|
|
||||||
this.rid = rid;
|
|
||||||
}
|
|
||||||
close() {
|
|
||||||
core.close(this.rid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const HttpClientPrototype = HttpClient.prototype;
|
}
|
||||||
|
const HttpClientPrototype = HttpClient.prototype;
|
||||||
|
|
||||||
window.__bootstrap.fetch ??= {};
|
export { createHttpClient, HttpClient, HttpClientPrototype };
|
||||||
window.__bootstrap.fetch.createHttpClient = createHttpClient;
|
|
||||||
window.__bootstrap.fetch.HttpClient = HttpClient;
|
|
||||||
window.__bootstrap.fetch.HttpClientPrototype = HttpClientPrototype;
|
|
||||||
})(globalThis);
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,510 +9,510 @@
|
||||||
/// <reference path="../web/06_streams_types.d.ts" />
|
/// <reference path="../web/06_streams_types.d.ts" />
|
||||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const { isProxy } = Deno.core;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = window.__bootstrap.webidl;
|
import { createFilteredInspectProxy } from "internal:ext/console/02_console.js";
|
||||||
const consoleInternal = window.__bootstrap.console;
|
import {
|
||||||
const {
|
byteLowerCase,
|
||||||
byteLowerCase,
|
HTTP_TAB_OR_SPACE,
|
||||||
} = window.__bootstrap.infra;
|
regexMatcher,
|
||||||
const { HTTP_TAB_OR_SPACE, regexMatcher, serializeJSValueToJSONString } =
|
serializeJSValueToJSONString,
|
||||||
window.__bootstrap.infra;
|
} from "internal:ext/web/00_infra.js";
|
||||||
const { extractBody, mixinBody } = window.__bootstrap.fetchBody;
|
import { extractBody, mixinBody } from "internal:ext/fetch/22_body.js";
|
||||||
const { getLocationHref } = window.__bootstrap.location;
|
import { getLocationHref } from "internal:ext/web/12_location.js";
|
||||||
const { extractMimeType } = window.__bootstrap.mimesniff;
|
import { extractMimeType } from "internal:ext/web/01_mimesniff.js";
|
||||||
const { URL } = window.__bootstrap.url;
|
import { URL } from "internal:ext/url/00_url.js";
|
||||||
const {
|
import {
|
||||||
getDecodeSplitHeader,
|
fillHeaders,
|
||||||
headerListFromHeaders,
|
getDecodeSplitHeader,
|
||||||
headersFromHeaderList,
|
guardFromHeaders,
|
||||||
guardFromHeaders,
|
headerListFromHeaders,
|
||||||
fillHeaders,
|
headersFromHeaderList,
|
||||||
} = window.__bootstrap.headers;
|
} from "internal:ext/fetch/20_headers.js";
|
||||||
const {
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
ArrayPrototypeMap,
|
const {
|
||||||
ArrayPrototypePush,
|
ArrayPrototypeMap,
|
||||||
ObjectDefineProperties,
|
ArrayPrototypePush,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
ObjectDefineProperties,
|
||||||
RangeError,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
RegExp,
|
RangeError,
|
||||||
RegExpPrototypeTest,
|
RegExp,
|
||||||
SafeArrayIterator,
|
RegExpPrototypeTest,
|
||||||
Symbol,
|
SafeArrayIterator,
|
||||||
SymbolFor,
|
Symbol,
|
||||||
TypeError,
|
SymbolFor,
|
||||||
} = window.__bootstrap.primordials;
|
TypeError,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
const VCHAR = ["\x21-\x7E"];
|
const VCHAR = ["\x21-\x7E"];
|
||||||
const OBS_TEXT = ["\x80-\xFF"];
|
const OBS_TEXT = ["\x80-\xFF"];
|
||||||
|
|
||||||
const REASON_PHRASE = [
|
const REASON_PHRASE = [
|
||||||
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
||||||
...new SafeArrayIterator(VCHAR),
|
...new SafeArrayIterator(VCHAR),
|
||||||
...new SafeArrayIterator(OBS_TEXT),
|
...new SafeArrayIterator(OBS_TEXT),
|
||||||
];
|
];
|
||||||
const REASON_PHRASE_MATCHER = regexMatcher(REASON_PHRASE);
|
const REASON_PHRASE_MATCHER = regexMatcher(REASON_PHRASE);
|
||||||
const REASON_PHRASE_RE = new RegExp(`^[${REASON_PHRASE_MATCHER}]*$`);
|
const REASON_PHRASE_RE = new RegExp(`^[${REASON_PHRASE_MATCHER}]*$`);
|
||||||
|
|
||||||
const _response = Symbol("response");
|
const _response = Symbol("response");
|
||||||
const _headers = Symbol("headers");
|
const _headers = Symbol("headers");
|
||||||
const _mimeType = Symbol("mime type");
|
const _mimeType = Symbol("mime type");
|
||||||
const _body = Symbol("body");
|
const _body = Symbol("body");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef InnerResponse
|
* @typedef InnerResponse
|
||||||
* @property {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"} type
|
* @property {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"} type
|
||||||
* @property {() => string | null} url
|
* @property {() => string | null} url
|
||||||
* @property {string[]} urlList
|
* @property {string[]} urlList
|
||||||
* @property {number} status
|
* @property {number} status
|
||||||
* @property {string} statusMessage
|
* @property {string} statusMessage
|
||||||
* @property {[string, string][]} headerList
|
* @property {[string, string][]} headerList
|
||||||
* @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body
|
* @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body
|
||||||
* @property {boolean} aborted
|
* @property {boolean} aborted
|
||||||
* @property {string} [error]
|
* @property {string} [error]
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} status
|
* @param {number} status
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function nullBodyStatus(status) {
|
function nullBodyStatus(status) {
|
||||||
return status === 101 || status === 204 || status === 205 || status === 304;
|
return status === 101 || status === 204 || status === 205 || status === 304;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {number} status
|
* @param {number} status
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function redirectStatus(status) {
|
function redirectStatus(status) {
|
||||||
return status === 301 || status === 302 || status === 303 ||
|
return status === 301 || status === 302 || status === 303 ||
|
||||||
status === 307 || status === 308;
|
status === 307 || status === 308;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* https://fetch.spec.whatwg.org/#concept-response-clone
|
* https://fetch.spec.whatwg.org/#concept-response-clone
|
||||||
* @param {InnerResponse} response
|
* @param {InnerResponse} response
|
||||||
* @returns {InnerResponse}
|
* @returns {InnerResponse}
|
||||||
*/
|
*/
|
||||||
function cloneInnerResponse(response) {
|
function cloneInnerResponse(response) {
|
||||||
const urlList = [...new SafeArrayIterator(response.urlList)];
|
const urlList = [...new SafeArrayIterator(response.urlList)];
|
||||||
const headerList = ArrayPrototypeMap(
|
const headerList = ArrayPrototypeMap(
|
||||||
response.headerList,
|
response.headerList,
|
||||||
(x) => [x[0], x[1]],
|
(x) => [x[0], x[1]],
|
||||||
);
|
|
||||||
|
|
||||||
let body = null;
|
|
||||||
if (response.body !== null) {
|
|
||||||
body = response.body.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
type: response.type,
|
|
||||||
body,
|
|
||||||
headerList,
|
|
||||||
urlList,
|
|
||||||
status: response.status,
|
|
||||||
statusMessage: response.statusMessage,
|
|
||||||
aborted: response.aborted,
|
|
||||||
url() {
|
|
||||||
if (this.urlList.length == 0) return null;
|
|
||||||
return this.urlList[this.urlList.length - 1];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {InnerResponse}
|
|
||||||
*/
|
|
||||||
function newInnerResponse(status = 200, statusMessage = "") {
|
|
||||||
return {
|
|
||||||
type: "default",
|
|
||||||
body: null,
|
|
||||||
headerList: [],
|
|
||||||
urlList: [],
|
|
||||||
status,
|
|
||||||
statusMessage,
|
|
||||||
aborted: false,
|
|
||||||
url() {
|
|
||||||
if (this.urlList.length == 0) return null;
|
|
||||||
return this.urlList[this.urlList.length - 1];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} error
|
|
||||||
* @returns {InnerResponse}
|
|
||||||
*/
|
|
||||||
function networkError(error) {
|
|
||||||
const resp = newInnerResponse(0);
|
|
||||||
resp.type = "error";
|
|
||||||
resp.error = error;
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {InnerResponse}
|
|
||||||
*/
|
|
||||||
function abortedNetworkError() {
|
|
||||||
const resp = networkError("aborted");
|
|
||||||
resp.aborted = true;
|
|
||||||
return resp;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://fetch.spec.whatwg.org#initialize-a-response
|
|
||||||
* @param {Response} response
|
|
||||||
* @param {ResponseInit} init
|
|
||||||
* @param {{ body: __bootstrap.fetchBody.InnerBody, contentType: string | null } | null} bodyWithType
|
|
||||||
*/
|
|
||||||
function initializeAResponse(response, init, bodyWithType) {
|
|
||||||
// 1.
|
|
||||||
if ((init.status < 200 || init.status > 599) && init.status != 101) {
|
|
||||||
throw new RangeError(
|
|
||||||
`The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.
|
|
||||||
if (
|
|
||||||
init.statusText &&
|
|
||||||
!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
|
|
||||||
) {
|
|
||||||
throw new TypeError("Status text is not valid.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3.
|
|
||||||
response[_response].status = init.status;
|
|
||||||
|
|
||||||
// 4.
|
|
||||||
response[_response].statusMessage = init.statusText;
|
|
||||||
// 5.
|
|
||||||
/** @type {__bootstrap.headers.Headers} */
|
|
||||||
const headers = response[_headers];
|
|
||||||
if (init.headers) {
|
|
||||||
fillHeaders(headers, init.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6.
|
|
||||||
if (bodyWithType !== null) {
|
|
||||||
if (nullBodyStatus(response[_response].status)) {
|
|
||||||
throw new TypeError(
|
|
||||||
"Response with null body status cannot have body",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const { body, contentType } = bodyWithType;
|
|
||||||
response[_response].body = body;
|
|
||||||
|
|
||||||
if (contentType !== null) {
|
|
||||||
let hasContentType = false;
|
|
||||||
const list = headerListFromHeaders(headers);
|
|
||||||
for (let i = 0; i < list.length; i++) {
|
|
||||||
if (byteLowerCase(list[i][0]) === "content-type") {
|
|
||||||
hasContentType = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasContentType) {
|
|
||||||
ArrayPrototypePush(list, ["Content-Type", contentType]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Response {
|
|
||||||
get [_mimeType]() {
|
|
||||||
const values = getDecodeSplitHeader(
|
|
||||||
headerListFromHeaders(this[_headers]),
|
|
||||||
"Content-Type",
|
|
||||||
);
|
|
||||||
return extractMimeType(values);
|
|
||||||
}
|
|
||||||
get [_body]() {
|
|
||||||
return this[_response].body;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Response}
|
|
||||||
*/
|
|
||||||
static error() {
|
|
||||||
const inner = newInnerResponse(0);
|
|
||||||
inner.type = "error";
|
|
||||||
const response = webidl.createBranded(Response);
|
|
||||||
response[_response] = inner;
|
|
||||||
response[_headers] = headersFromHeaderList(
|
|
||||||
response[_response].headerList,
|
|
||||||
"immutable",
|
|
||||||
);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} url
|
|
||||||
* @param {number} status
|
|
||||||
* @returns {Response}
|
|
||||||
*/
|
|
||||||
static redirect(url, status = 302) {
|
|
||||||
const prefix = "Failed to call 'Response.redirect'";
|
|
||||||
url = webidl.converters["USVString"](url, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
status = webidl.converters["unsigned short"](status, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
|
|
||||||
const baseURL = getLocationHref();
|
|
||||||
const parsedURL = new URL(url, baseURL);
|
|
||||||
if (!redirectStatus(status)) {
|
|
||||||
throw new RangeError("Invalid redirect status code.");
|
|
||||||
}
|
|
||||||
const inner = newInnerResponse(status);
|
|
||||||
inner.type = "default";
|
|
||||||
ArrayPrototypePush(inner.headerList, ["Location", parsedURL.href]);
|
|
||||||
const response = webidl.createBranded(Response);
|
|
||||||
response[_response] = inner;
|
|
||||||
response[_headers] = headersFromHeaderList(
|
|
||||||
response[_response].headerList,
|
|
||||||
"immutable",
|
|
||||||
);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {any} data
|
|
||||||
* @param {ResponseInit} init
|
|
||||||
* @returns {Response}
|
|
||||||
*/
|
|
||||||
static json(data = undefined, init = {}) {
|
|
||||||
const prefix = "Failed to call 'Response.json'";
|
|
||||||
data = webidl.converters.any(data);
|
|
||||||
init = webidl.converters["ResponseInit_fast"](init, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
|
|
||||||
const str = serializeJSValueToJSONString(data);
|
|
||||||
const res = extractBody(str);
|
|
||||||
res.contentType = "application/json";
|
|
||||||
const response = webidl.createBranded(Response);
|
|
||||||
response[_response] = newInnerResponse();
|
|
||||||
response[_headers] = headersFromHeaderList(
|
|
||||||
response[_response].headerList,
|
|
||||||
"response",
|
|
||||||
);
|
|
||||||
initializeAResponse(response, init, res);
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {BodyInit | null} body
|
|
||||||
* @param {ResponseInit} init
|
|
||||||
*/
|
|
||||||
constructor(body = null, init = undefined) {
|
|
||||||
const prefix = "Failed to construct 'Response'";
|
|
||||||
body = webidl.converters["BodyInit_DOMString?"](body, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
init = webidl.converters["ResponseInit_fast"](init, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
|
|
||||||
this[_response] = newInnerResponse();
|
|
||||||
this[_headers] = headersFromHeaderList(
|
|
||||||
this[_response].headerList,
|
|
||||||
"response",
|
|
||||||
);
|
|
||||||
|
|
||||||
let bodyWithType = null;
|
|
||||||
if (body !== null) {
|
|
||||||
bodyWithType = extractBody(body);
|
|
||||||
}
|
|
||||||
initializeAResponse(this, init, bodyWithType);
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"}
|
|
||||||
*/
|
|
||||||
get type() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
return this[_response].type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
get url() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
const url = this[_response].url();
|
|
||||||
if (url === null) return "";
|
|
||||||
const newUrl = new URL(url);
|
|
||||||
newUrl.hash = "";
|
|
||||||
return newUrl.href;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
get redirected() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
return this[_response].urlList.length > 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {number}
|
|
||||||
*/
|
|
||||||
get status() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
return this[_response].status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
get ok() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
const status = this[_response].status;
|
|
||||||
return status >= 200 && status <= 299;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
get statusText() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
return this[_response].statusMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Headers}
|
|
||||||
*/
|
|
||||||
get headers() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
return this[_headers];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @returns {Response}
|
|
||||||
*/
|
|
||||||
clone() {
|
|
||||||
webidl.assertBranded(this, ResponsePrototype);
|
|
||||||
if (this[_body] && this[_body].unusable()) {
|
|
||||||
throw new TypeError("Body is unusable.");
|
|
||||||
}
|
|
||||||
const second = webidl.createBranded(Response);
|
|
||||||
const newRes = cloneInnerResponse(this[_response]);
|
|
||||||
second[_response] = newRes;
|
|
||||||
second[_headers] = headersFromHeaderList(
|
|
||||||
newRes.headerList,
|
|
||||||
guardFromHeaders(this[_headers]),
|
|
||||||
);
|
|
||||||
return second;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SymbolFor("Deno.customInspect")](inspect) {
|
|
||||||
return inspect(consoleInternal.createFilteredInspectProxy({
|
|
||||||
object: this,
|
|
||||||
evaluate: ObjectPrototypeIsPrototypeOf(ResponsePrototype, this),
|
|
||||||
keys: [
|
|
||||||
"body",
|
|
||||||
"bodyUsed",
|
|
||||||
"headers",
|
|
||||||
"ok",
|
|
||||||
"redirected",
|
|
||||||
"status",
|
|
||||||
"statusText",
|
|
||||||
"url",
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
webidl.configurePrototype(Response);
|
|
||||||
ObjectDefineProperties(Response, {
|
|
||||||
json: { enumerable: true },
|
|
||||||
redirect: { enumerable: true },
|
|
||||||
error: { enumerable: true },
|
|
||||||
});
|
|
||||||
const ResponsePrototype = Response.prototype;
|
|
||||||
mixinBody(ResponsePrototype, _body, _mimeType);
|
|
||||||
|
|
||||||
webidl.converters["Response"] = webidl.createInterfaceConverter(
|
|
||||||
"Response",
|
|
||||||
ResponsePrototype,
|
|
||||||
);
|
);
|
||||||
webidl.converters["ResponseInit"] = webidl.createDictionaryConverter(
|
|
||||||
"ResponseInit",
|
let body = null;
|
||||||
[{
|
if (response.body !== null) {
|
||||||
key: "status",
|
body = response.body.clone();
|
||||||
defaultValue: 200,
|
}
|
||||||
converter: webidl.converters["unsigned short"],
|
|
||||||
}, {
|
return {
|
||||||
key: "statusText",
|
type: response.type,
|
||||||
defaultValue: "",
|
body,
|
||||||
converter: webidl.converters["ByteString"],
|
headerList,
|
||||||
}, {
|
urlList,
|
||||||
key: "headers",
|
status: response.status,
|
||||||
converter: webidl.converters["HeadersInit"],
|
statusMessage: response.statusMessage,
|
||||||
}],
|
aborted: response.aborted,
|
||||||
);
|
url() {
|
||||||
webidl.converters["ResponseInit_fast"] = function (init, opts) {
|
if (this.urlList.length == 0) return null;
|
||||||
if (init === undefined || init === null) {
|
return this.urlList[this.urlList.length - 1];
|
||||||
return { status: 200, statusText: "", headers: undefined };
|
},
|
||||||
}
|
|
||||||
// Fast path, if not a proxy
|
|
||||||
if (typeof init === "object" && !isProxy(init)) {
|
|
||||||
// Not a proxy fast path
|
|
||||||
const status = init.status !== undefined
|
|
||||||
? webidl.converters["unsigned short"](init.status)
|
|
||||||
: 200;
|
|
||||||
const statusText = init.statusText !== undefined
|
|
||||||
? webidl.converters["ByteString"](init.statusText)
|
|
||||||
: "";
|
|
||||||
const headers = init.headers !== undefined
|
|
||||||
? webidl.converters["HeadersInit"](init.headers)
|
|
||||||
: undefined;
|
|
||||||
return { status, statusText, headers };
|
|
||||||
}
|
|
||||||
// Slow default path
|
|
||||||
return webidl.converters["ResponseInit"](init, opts);
|
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Response} response
|
* @returns {InnerResponse}
|
||||||
* @returns {InnerResponse}
|
*/
|
||||||
*/
|
function newInnerResponse(status = 200, statusMessage = "") {
|
||||||
function toInnerResponse(response) {
|
return {
|
||||||
return response[_response];
|
type: "default",
|
||||||
|
body: null,
|
||||||
|
headerList: [],
|
||||||
|
urlList: [],
|
||||||
|
status,
|
||||||
|
statusMessage,
|
||||||
|
aborted: false,
|
||||||
|
url() {
|
||||||
|
if (this.urlList.length == 0) return null;
|
||||||
|
return this.urlList[this.urlList.length - 1];
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} error
|
||||||
|
* @returns {InnerResponse}
|
||||||
|
*/
|
||||||
|
function networkError(error) {
|
||||||
|
const resp = newInnerResponse(0);
|
||||||
|
resp.type = "error";
|
||||||
|
resp.error = error;
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {InnerResponse}
|
||||||
|
*/
|
||||||
|
function abortedNetworkError() {
|
||||||
|
const resp = networkError("aborted");
|
||||||
|
resp.aborted = true;
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://fetch.spec.whatwg.org#initialize-a-response
|
||||||
|
* @param {Response} response
|
||||||
|
* @param {ResponseInit} init
|
||||||
|
* @param {{ body: fetchBody.InnerBody, contentType: string | null } | null} bodyWithType
|
||||||
|
*/
|
||||||
|
function initializeAResponse(response, init, bodyWithType) {
|
||||||
|
// 1.
|
||||||
|
if ((init.status < 200 || init.status > 599) && init.status != 101) {
|
||||||
|
throw new RangeError(
|
||||||
|
`The status provided (${init.status}) is not equal to 101 and outside the range [200, 599].`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2.
|
||||||
|
if (
|
||||||
|
init.statusText &&
|
||||||
|
!RegExpPrototypeTest(REASON_PHRASE_RE, init.statusText)
|
||||||
|
) {
|
||||||
|
throw new TypeError("Status text is not valid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3.
|
||||||
|
response[_response].status = init.status;
|
||||||
|
|
||||||
|
// 4.
|
||||||
|
response[_response].statusMessage = init.statusText;
|
||||||
|
// 5.
|
||||||
|
/** @type {headers.Headers} */
|
||||||
|
const headers = response[_headers];
|
||||||
|
if (init.headers) {
|
||||||
|
fillHeaders(headers, init.headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6.
|
||||||
|
if (bodyWithType !== null) {
|
||||||
|
if (nullBodyStatus(response[_response].status)) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Response with null body status cannot have body",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { body, contentType } = bodyWithType;
|
||||||
|
response[_response].body = body;
|
||||||
|
|
||||||
|
if (contentType !== null) {
|
||||||
|
let hasContentType = false;
|
||||||
|
const list = headerListFromHeaders(headers);
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (byteLowerCase(list[i][0]) === "content-type") {
|
||||||
|
hasContentType = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasContentType) {
|
||||||
|
ArrayPrototypePush(list, ["Content-Type", contentType]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Response {
|
||||||
|
get [_mimeType]() {
|
||||||
|
const values = getDecodeSplitHeader(
|
||||||
|
headerListFromHeaders(this[_headers]),
|
||||||
|
"Content-Type",
|
||||||
|
);
|
||||||
|
return extractMimeType(values);
|
||||||
|
}
|
||||||
|
get [_body]() {
|
||||||
|
return this[_response].body;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {InnerResponse} inner
|
|
||||||
* @param {"request" | "immutable" | "request-no-cors" | "response" | "none"} guard
|
|
||||||
* @returns {Response}
|
* @returns {Response}
|
||||||
*/
|
*/
|
||||||
function fromInnerResponse(inner, guard) {
|
static error() {
|
||||||
|
const inner = newInnerResponse(0);
|
||||||
|
inner.type = "error";
|
||||||
const response = webidl.createBranded(Response);
|
const response = webidl.createBranded(Response);
|
||||||
response[_response] = inner;
|
response[_response] = inner;
|
||||||
response[_headers] = headersFromHeaderList(inner.headerList, guard);
|
response[_headers] = headersFromHeaderList(
|
||||||
|
response[_response].headerList,
|
||||||
|
"immutable",
|
||||||
|
);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.fetch ??= {};
|
/**
|
||||||
window.__bootstrap.fetch.Response = Response;
|
* @param {string} url
|
||||||
window.__bootstrap.fetch.ResponsePrototype = ResponsePrototype;
|
* @param {number} status
|
||||||
window.__bootstrap.fetch.newInnerResponse = newInnerResponse;
|
* @returns {Response}
|
||||||
window.__bootstrap.fetch.toInnerResponse = toInnerResponse;
|
*/
|
||||||
window.__bootstrap.fetch.fromInnerResponse = fromInnerResponse;
|
static redirect(url, status = 302) {
|
||||||
window.__bootstrap.fetch.redirectStatus = redirectStatus;
|
const prefix = "Failed to call 'Response.redirect'";
|
||||||
window.__bootstrap.fetch.nullBodyStatus = nullBodyStatus;
|
url = webidl.converters["USVString"](url, {
|
||||||
window.__bootstrap.fetch.networkError = networkError;
|
prefix,
|
||||||
window.__bootstrap.fetch.abortedNetworkError = abortedNetworkError;
|
context: "Argument 1",
|
||||||
})(globalThis);
|
});
|
||||||
|
status = webidl.converters["unsigned short"](status, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
|
||||||
|
const baseURL = getLocationHref();
|
||||||
|
const parsedURL = new URL(url, baseURL);
|
||||||
|
if (!redirectStatus(status)) {
|
||||||
|
throw new RangeError("Invalid redirect status code.");
|
||||||
|
}
|
||||||
|
const inner = newInnerResponse(status);
|
||||||
|
inner.type = "default";
|
||||||
|
ArrayPrototypePush(inner.headerList, ["Location", parsedURL.href]);
|
||||||
|
const response = webidl.createBranded(Response);
|
||||||
|
response[_response] = inner;
|
||||||
|
response[_headers] = headersFromHeaderList(
|
||||||
|
response[_response].headerList,
|
||||||
|
"immutable",
|
||||||
|
);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} data
|
||||||
|
* @param {ResponseInit} init
|
||||||
|
* @returns {Response}
|
||||||
|
*/
|
||||||
|
static json(data = undefined, init = {}) {
|
||||||
|
const prefix = "Failed to call 'Response.json'";
|
||||||
|
data = webidl.converters.any(data);
|
||||||
|
init = webidl.converters["ResponseInit_fast"](init, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
|
||||||
|
const str = serializeJSValueToJSONString(data);
|
||||||
|
const res = extractBody(str);
|
||||||
|
res.contentType = "application/json";
|
||||||
|
const response = webidl.createBranded(Response);
|
||||||
|
response[_response] = newInnerResponse();
|
||||||
|
response[_headers] = headersFromHeaderList(
|
||||||
|
response[_response].headerList,
|
||||||
|
"response",
|
||||||
|
);
|
||||||
|
initializeAResponse(response, init, res);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {BodyInit | null} body
|
||||||
|
* @param {ResponseInit} init
|
||||||
|
*/
|
||||||
|
constructor(body = null, init = undefined) {
|
||||||
|
const prefix = "Failed to construct 'Response'";
|
||||||
|
body = webidl.converters["BodyInit_DOMString?"](body, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
init = webidl.converters["ResponseInit_fast"](init, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
|
||||||
|
this[_response] = newInnerResponse();
|
||||||
|
this[_headers] = headersFromHeaderList(
|
||||||
|
this[_response].headerList,
|
||||||
|
"response",
|
||||||
|
);
|
||||||
|
|
||||||
|
let bodyWithType = null;
|
||||||
|
if (body !== null) {
|
||||||
|
bodyWithType = extractBody(body);
|
||||||
|
}
|
||||||
|
initializeAResponse(this, init, bodyWithType);
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"}
|
||||||
|
*/
|
||||||
|
get type() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
return this[_response].type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get url() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
const url = this[_response].url();
|
||||||
|
if (url === null) return "";
|
||||||
|
const newUrl = new URL(url);
|
||||||
|
newUrl.hash = "";
|
||||||
|
return newUrl.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get redirected() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
return this[_response].urlList.length > 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
get status() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
return this[_response].status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
get ok() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
const status = this[_response].status;
|
||||||
|
return status >= 200 && status <= 299;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
get statusText() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
return this[_response].statusMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Headers}
|
||||||
|
*/
|
||||||
|
get headers() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
return this[_headers];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {Response}
|
||||||
|
*/
|
||||||
|
clone() {
|
||||||
|
webidl.assertBranded(this, ResponsePrototype);
|
||||||
|
if (this[_body] && this[_body].unusable()) {
|
||||||
|
throw new TypeError("Body is unusable.");
|
||||||
|
}
|
||||||
|
const second = webidl.createBranded(Response);
|
||||||
|
const newRes = cloneInnerResponse(this[_response]);
|
||||||
|
second[_response] = newRes;
|
||||||
|
second[_headers] = headersFromHeaderList(
|
||||||
|
newRes.headerList,
|
||||||
|
guardFromHeaders(this[_headers]),
|
||||||
|
);
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SymbolFor("Deno.customInspect")](inspect) {
|
||||||
|
return inspect(createFilteredInspectProxy({
|
||||||
|
object: this,
|
||||||
|
evaluate: ObjectPrototypeIsPrototypeOf(ResponsePrototype, this),
|
||||||
|
keys: [
|
||||||
|
"body",
|
||||||
|
"bodyUsed",
|
||||||
|
"headers",
|
||||||
|
"ok",
|
||||||
|
"redirected",
|
||||||
|
"status",
|
||||||
|
"statusText",
|
||||||
|
"url",
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(Response);
|
||||||
|
ObjectDefineProperties(Response, {
|
||||||
|
json: { enumerable: true },
|
||||||
|
redirect: { enumerable: true },
|
||||||
|
error: { enumerable: true },
|
||||||
|
});
|
||||||
|
const ResponsePrototype = Response.prototype;
|
||||||
|
mixinBody(ResponsePrototype, _body, _mimeType);
|
||||||
|
|
||||||
|
webidl.converters["Response"] = webidl.createInterfaceConverter(
|
||||||
|
"Response",
|
||||||
|
ResponsePrototype,
|
||||||
|
);
|
||||||
|
webidl.converters["ResponseInit"] = webidl.createDictionaryConverter(
|
||||||
|
"ResponseInit",
|
||||||
|
[{
|
||||||
|
key: "status",
|
||||||
|
defaultValue: 200,
|
||||||
|
converter: webidl.converters["unsigned short"],
|
||||||
|
}, {
|
||||||
|
key: "statusText",
|
||||||
|
defaultValue: "",
|
||||||
|
converter: webidl.converters["ByteString"],
|
||||||
|
}, {
|
||||||
|
key: "headers",
|
||||||
|
converter: webidl.converters["HeadersInit"],
|
||||||
|
}],
|
||||||
|
);
|
||||||
|
webidl.converters["ResponseInit_fast"] = function (init, opts) {
|
||||||
|
if (init === undefined || init === null) {
|
||||||
|
return { status: 200, statusText: "", headers: undefined };
|
||||||
|
}
|
||||||
|
// Fast path, if not a proxy
|
||||||
|
if (typeof init === "object" && !core.isProxy(init)) {
|
||||||
|
// Not a proxy fast path
|
||||||
|
const status = init.status !== undefined
|
||||||
|
? webidl.converters["unsigned short"](init.status)
|
||||||
|
: 200;
|
||||||
|
const statusText = init.statusText !== undefined
|
||||||
|
? webidl.converters["ByteString"](init.statusText)
|
||||||
|
: "";
|
||||||
|
const headers = init.headers !== undefined
|
||||||
|
? webidl.converters["HeadersInit"](init.headers)
|
||||||
|
: undefined;
|
||||||
|
return { status, statusText, headers };
|
||||||
|
}
|
||||||
|
// Slow default path
|
||||||
|
return webidl.converters["ResponseInit"](init, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Response} response
|
||||||
|
* @returns {InnerResponse}
|
||||||
|
*/
|
||||||
|
function toInnerResponse(response) {
|
||||||
|
return response[_response];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {InnerResponse} inner
|
||||||
|
* @param {"request" | "immutable" | "request-no-cors" | "response" | "none"} guard
|
||||||
|
* @returns {Response}
|
||||||
|
*/
|
||||||
|
function fromInnerResponse(inner, guard) {
|
||||||
|
const response = webidl.createBranded(Response);
|
||||||
|
response[_response] = inner;
|
||||||
|
response[_headers] = headersFromHeaderList(inner.headerList, guard);
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
abortedNetworkError,
|
||||||
|
fromInnerResponse,
|
||||||
|
networkError,
|
||||||
|
newInnerResponse,
|
||||||
|
nullBodyStatus,
|
||||||
|
redirectStatus,
|
||||||
|
Response,
|
||||||
|
ResponsePrototype,
|
||||||
|
toInnerResponse,
|
||||||
|
};
|
||||||
|
|
File diff suppressed because it is too large
Load diff
192
ext/fetch/internal.d.ts
vendored
192
ext/fetch/internal.d.ts
vendored
|
@ -5,106 +5,98 @@
|
||||||
/// <reference no-default-lib="true" />
|
/// <reference no-default-lib="true" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
declare namespace globalThis {
|
declare var domIterable: {
|
||||||
declare namespace __bootstrap {
|
DomIterableMixin(base: any, dataSymbol: symbol): any;
|
||||||
declare var fetchUtil: {
|
};
|
||||||
requiredArguments(name: string, length: number, required: number): void;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare var domIterable: {
|
declare module "internal:ext/fetch/20_headers.js" {
|
||||||
DomIterableMixin(base: any, dataSymbol: symbol): any;
|
class Headers {
|
||||||
};
|
|
||||||
|
|
||||||
declare namespace headers {
|
|
||||||
class Headers {
|
|
||||||
}
|
|
||||||
type HeaderList = [string, string][];
|
|
||||||
function headersFromHeaderList(
|
|
||||||
list: HeaderList,
|
|
||||||
guard:
|
|
||||||
| "immutable"
|
|
||||||
| "request"
|
|
||||||
| "request-no-cors"
|
|
||||||
| "response"
|
|
||||||
| "none",
|
|
||||||
): Headers;
|
|
||||||
function headerListFromHeaders(headers: Headers): HeaderList;
|
|
||||||
function fillHeaders(headers: Headers, object: HeadersInit): void;
|
|
||||||
function getDecodeSplitHeader(
|
|
||||||
list: HeaderList,
|
|
||||||
name: string,
|
|
||||||
): string[] | null;
|
|
||||||
function guardFromHeaders(
|
|
||||||
headers: Headers,
|
|
||||||
): "immutable" | "request" | "request-no-cors" | "response" | "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
declare namespace formData {
|
|
||||||
declare type FormData = typeof FormData;
|
|
||||||
declare function formDataToBlob(
|
|
||||||
formData: globalThis.FormData,
|
|
||||||
): Blob;
|
|
||||||
declare function parseFormData(
|
|
||||||
body: Uint8Array,
|
|
||||||
boundary: string | undefined,
|
|
||||||
): FormData;
|
|
||||||
declare function formDataFromEntries(entries: FormDataEntry[]): FormData;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare namespace fetchBody {
|
|
||||||
function mixinBody(
|
|
||||||
prototype: any,
|
|
||||||
bodySymbol: symbol,
|
|
||||||
mimeTypeSymbol: symbol,
|
|
||||||
): void;
|
|
||||||
class InnerBody {
|
|
||||||
constructor(stream?: ReadableStream<Uint8Array>);
|
|
||||||
stream: ReadableStream<Uint8Array>;
|
|
||||||
source: null | Uint8Array | Blob | FormData;
|
|
||||||
length: null | number;
|
|
||||||
unusable(): boolean;
|
|
||||||
consume(): Promise<Uint8Array>;
|
|
||||||
clone(): InnerBody;
|
|
||||||
}
|
|
||||||
function extractBody(object: BodyInit): {
|
|
||||||
body: InnerBody;
|
|
||||||
contentType: string | null;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
declare namespace fetch {
|
|
||||||
function toInnerRequest(request: Request): InnerRequest;
|
|
||||||
function fromInnerRequest(
|
|
||||||
inner: InnerRequest,
|
|
||||||
signal: AbortSignal | null,
|
|
||||||
guard:
|
|
||||||
| "request"
|
|
||||||
| "immutable"
|
|
||||||
| "request-no-cors"
|
|
||||||
| "response"
|
|
||||||
| "none",
|
|
||||||
skipBody: boolean,
|
|
||||||
flash: boolean,
|
|
||||||
): Request;
|
|
||||||
function redirectStatus(status: number): boolean;
|
|
||||||
function nullBodyStatus(status: number): boolean;
|
|
||||||
function newInnerRequest(
|
|
||||||
method: string,
|
|
||||||
url: any,
|
|
||||||
headerList?: [string, string][],
|
|
||||||
body?: globalThis.__bootstrap.fetchBody.InnerBody,
|
|
||||||
): InnerResponse;
|
|
||||||
function toInnerResponse(response: Response): InnerResponse;
|
|
||||||
function fromInnerResponse(
|
|
||||||
inner: InnerResponse,
|
|
||||||
guard:
|
|
||||||
| "request"
|
|
||||||
| "immutable"
|
|
||||||
| "request-no-cors"
|
|
||||||
| "response"
|
|
||||||
| "none",
|
|
||||||
): Response;
|
|
||||||
function networkError(error: string): InnerResponse;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
type HeaderList = [string, string][];
|
||||||
|
function headersFromHeaderList(
|
||||||
|
list: HeaderList,
|
||||||
|
guard:
|
||||||
|
| "immutable"
|
||||||
|
| "request"
|
||||||
|
| "request-no-cors"
|
||||||
|
| "response"
|
||||||
|
| "none",
|
||||||
|
): Headers;
|
||||||
|
function headerListFromHeaders(headers: Headers): HeaderList;
|
||||||
|
function fillHeaders(headers: Headers, object: HeadersInit): void;
|
||||||
|
function getDecodeSplitHeader(
|
||||||
|
list: HeaderList,
|
||||||
|
name: string,
|
||||||
|
): string[] | null;
|
||||||
|
function guardFromHeaders(
|
||||||
|
headers: Headers,
|
||||||
|
): "immutable" | "request" | "request-no-cors" | "response" | "none";
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "internal:ext/fetch/21_formdata.js" {
|
||||||
|
type FormData = typeof FormData;
|
||||||
|
function formDataToBlob(
|
||||||
|
formData: FormData,
|
||||||
|
): Blob;
|
||||||
|
function parseFormData(
|
||||||
|
body: Uint8Array,
|
||||||
|
boundary: string | undefined,
|
||||||
|
): FormData;
|
||||||
|
function formDataFromEntries(entries: FormDataEntry[]): FormData;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "internal:ext/fetch/22_body.js" {
|
||||||
|
function mixinBody(
|
||||||
|
prototype: any,
|
||||||
|
bodySymbol: symbol,
|
||||||
|
mimeTypeSymbol: symbol,
|
||||||
|
): void;
|
||||||
|
class InnerBody {
|
||||||
|
constructor(stream?: ReadableStream<Uint8Array>);
|
||||||
|
stream: ReadableStream<Uint8Array>;
|
||||||
|
source: null | Uint8Array | Blob | FormData;
|
||||||
|
length: null | number;
|
||||||
|
unusable(): boolean;
|
||||||
|
consume(): Promise<Uint8Array>;
|
||||||
|
clone(): InnerBody;
|
||||||
|
}
|
||||||
|
function extractBody(object: BodyInit): {
|
||||||
|
body: InnerBody;
|
||||||
|
contentType: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "internal:ext/fetch/26_fetch.js" {
|
||||||
|
function toInnerRequest(request: Request): InnerRequest;
|
||||||
|
function fromInnerRequest(
|
||||||
|
inner: InnerRequest,
|
||||||
|
signal: AbortSignal | null,
|
||||||
|
guard:
|
||||||
|
| "request"
|
||||||
|
| "immutable"
|
||||||
|
| "request-no-cors"
|
||||||
|
| "response"
|
||||||
|
| "none",
|
||||||
|
skipBody: boolean,
|
||||||
|
flash: boolean,
|
||||||
|
): Request;
|
||||||
|
function redirectStatus(status: number): boolean;
|
||||||
|
function nullBodyStatus(status: number): boolean;
|
||||||
|
function newInnerRequest(
|
||||||
|
method: string,
|
||||||
|
url: any,
|
||||||
|
headerList?: [string, string][],
|
||||||
|
body?: fetchBody.InnerBody,
|
||||||
|
): InnerResponse;
|
||||||
|
function toInnerResponse(response: Response): InnerResponse;
|
||||||
|
function fromInnerResponse(
|
||||||
|
inner: InnerResponse,
|
||||||
|
guard:
|
||||||
|
| "request"
|
||||||
|
| "immutable"
|
||||||
|
| "request-no-cors"
|
||||||
|
| "response"
|
||||||
|
| "none",
|
||||||
|
): Response;
|
||||||
|
function networkError(error: string): InnerResponse;
|
||||||
}
|
}
|
||||||
|
|
|
@ -97,9 +97,8 @@ where
|
||||||
{
|
{
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl", "deno_web", "deno_url", "deno_console"])
|
.dependencies(vec!["deno_webidl", "deno_web", "deno_url", "deno_console"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/fetch",
|
prefix "internal:ext/fetch",
|
||||||
"01_fetch_util.js",
|
|
||||||
"20_headers.js",
|
"20_headers.js",
|
||||||
"21_formdata.js",
|
"21_formdata.js",
|
||||||
"22_body.js",
|
"22_body.js",
|
||||||
|
|
|
@ -1,510 +1,509 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
const internals = globalThis.__bootstrap.internals;
|
||||||
const __bootstrap = window.__bootstrap;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
ArrayPrototypeMap,
|
ArrayPrototypeMap,
|
||||||
ArrayPrototypeJoin,
|
ArrayPrototypeJoin,
|
||||||
ObjectDefineProperty,
|
ObjectDefineProperty,
|
||||||
ObjectPrototypeHasOwnProperty,
|
ObjectPrototypeHasOwnProperty,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
Number,
|
Number,
|
||||||
NumberIsSafeInteger,
|
NumberIsSafeInteger,
|
||||||
TypeError,
|
TypeError,
|
||||||
Uint8Array,
|
Uint8Array,
|
||||||
Int32Array,
|
Int32Array,
|
||||||
Uint32Array,
|
Uint32Array,
|
||||||
BigInt64Array,
|
BigInt64Array,
|
||||||
BigUint64Array,
|
BigUint64Array,
|
||||||
Function,
|
Function,
|
||||||
ReflectHas,
|
ReflectHas,
|
||||||
PromisePrototypeThen,
|
PromisePrototypeThen,
|
||||||
MathMax,
|
MathMax,
|
||||||
MathCeil,
|
MathCeil,
|
||||||
SafeMap,
|
SafeMap,
|
||||||
SafeArrayIterator,
|
SafeArrayIterator,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
|
|
||||||
const U32_BUFFER = new Uint32Array(2);
|
const U32_BUFFER = new Uint32Array(2);
|
||||||
const U64_BUFFER = new BigUint64Array(U32_BUFFER.buffer);
|
const U64_BUFFER = new BigUint64Array(U32_BUFFER.buffer);
|
||||||
const I64_BUFFER = new BigInt64Array(U32_BUFFER.buffer);
|
const I64_BUFFER = new BigInt64Array(U32_BUFFER.buffer);
|
||||||
class UnsafePointerView {
|
class UnsafePointerView {
|
||||||
pointer;
|
pointer;
|
||||||
|
|
||||||
constructor(pointer) {
|
constructor(pointer) {
|
||||||
this.pointer = pointer;
|
this.pointer = pointer;
|
||||||
}
|
|
||||||
|
|
||||||
getBool(offset = 0) {
|
|
||||||
return ops.op_ffi_read_bool(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUint8(offset = 0) {
|
|
||||||
return ops.op_ffi_read_u8(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getInt8(offset = 0) {
|
|
||||||
return ops.op_ffi_read_i8(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUint16(offset = 0) {
|
|
||||||
return ops.op_ffi_read_u16(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getInt16(offset = 0) {
|
|
||||||
return ops.op_ffi_read_i16(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getUint32(offset = 0) {
|
|
||||||
return ops.op_ffi_read_u32(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getInt32(offset = 0) {
|
|
||||||
return ops.op_ffi_read_i32(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getBigUint64(offset = 0) {
|
|
||||||
ops.op_ffi_read_u64(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
U32_BUFFER,
|
|
||||||
);
|
|
||||||
return U64_BUFFER[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
getBigInt64(offset = 0) {
|
|
||||||
ops.op_ffi_read_i64(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
U32_BUFFER,
|
|
||||||
);
|
|
||||||
return I64_BUFFER[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
getFloat32(offset = 0) {
|
|
||||||
return ops.op_ffi_read_f32(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getFloat64(offset = 0) {
|
|
||||||
return ops.op_ffi_read_f64(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getCString(offset = 0) {
|
|
||||||
return ops.op_ffi_cstr_read(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getCString(pointer, offset = 0) {
|
|
||||||
return ops.op_ffi_cstr_read(
|
|
||||||
pointer,
|
|
||||||
offset,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
getArrayBuffer(byteLength, offset = 0) {
|
|
||||||
return ops.op_ffi_get_buf(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
byteLength,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getArrayBuffer(pointer, byteLength, offset = 0) {
|
|
||||||
return ops.op_ffi_get_buf(
|
|
||||||
pointer,
|
|
||||||
offset,
|
|
||||||
byteLength,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
copyInto(destination, offset = 0) {
|
|
||||||
ops.op_ffi_buf_copy_into(
|
|
||||||
this.pointer,
|
|
||||||
offset,
|
|
||||||
destination,
|
|
||||||
destination.byteLength,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
static copyInto(pointer, destination, offset = 0) {
|
|
||||||
ops.op_ffi_buf_copy_into(
|
|
||||||
pointer,
|
|
||||||
offset,
|
|
||||||
destination,
|
|
||||||
destination.byteLength,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const OUT_BUFFER = new Uint32Array(2);
|
getBool(offset = 0) {
|
||||||
const OUT_BUFFER_64 = new BigInt64Array(OUT_BUFFER.buffer);
|
return ops.op_ffi_read_bool(
|
||||||
class UnsafePointer {
|
this.pointer,
|
||||||
static of(value) {
|
offset,
|
||||||
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
);
|
||||||
return value.pointer;
|
|
||||||
}
|
|
||||||
ops.op_ffi_ptr_of(value, OUT_BUFFER);
|
|
||||||
const result = OUT_BUFFER[0] + 2 ** 32 * OUT_BUFFER[1];
|
|
||||||
if (NumberIsSafeInteger(result)) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return OUT_BUFFER_64[0];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnsafeFnPointer {
|
getUint8(offset = 0) {
|
||||||
pointer;
|
return ops.op_ffi_read_u8(
|
||||||
definition;
|
this.pointer,
|
||||||
#structSize;
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(pointer, definition) {
|
getInt8(offset = 0) {
|
||||||
this.pointer = pointer;
|
return ops.op_ffi_read_i8(
|
||||||
this.definition = definition;
|
this.pointer,
|
||||||
this.#structSize = isStruct(definition.result)
|
offset,
|
||||||
? getTypeSizeAndAlignment(definition.result)[0]
|
);
|
||||||
: null;
|
}
|
||||||
|
|
||||||
|
getUint16(offset = 0) {
|
||||||
|
return ops.op_ffi_read_u16(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInt16(offset = 0) {
|
||||||
|
return ops.op_ffi_read_i16(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getUint32(offset = 0) {
|
||||||
|
return ops.op_ffi_read_u32(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getInt32(offset = 0) {
|
||||||
|
return ops.op_ffi_read_i32(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getBigUint64(offset = 0) {
|
||||||
|
ops.op_ffi_read_u64(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
U32_BUFFER,
|
||||||
|
);
|
||||||
|
return U64_BUFFER[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getBigInt64(offset = 0) {
|
||||||
|
ops.op_ffi_read_i64(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
U32_BUFFER,
|
||||||
|
);
|
||||||
|
return I64_BUFFER[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getFloat32(offset = 0) {
|
||||||
|
return ops.op_ffi_read_f32(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getFloat64(offset = 0) {
|
||||||
|
return ops.op_ffi_read_f64(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getCString(offset = 0) {
|
||||||
|
return ops.op_ffi_cstr_read(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getCString(pointer, offset = 0) {
|
||||||
|
return ops.op_ffi_cstr_read(
|
||||||
|
pointer,
|
||||||
|
offset,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
getArrayBuffer(byteLength, offset = 0) {
|
||||||
|
return ops.op_ffi_get_buf(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
byteLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static getArrayBuffer(pointer, byteLength, offset = 0) {
|
||||||
|
return ops.op_ffi_get_buf(
|
||||||
|
pointer,
|
||||||
|
offset,
|
||||||
|
byteLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
copyInto(destination, offset = 0) {
|
||||||
|
ops.op_ffi_buf_copy_into(
|
||||||
|
this.pointer,
|
||||||
|
offset,
|
||||||
|
destination,
|
||||||
|
destination.byteLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static copyInto(pointer, destination, offset = 0) {
|
||||||
|
ops.op_ffi_buf_copy_into(
|
||||||
|
pointer,
|
||||||
|
offset,
|
||||||
|
destination,
|
||||||
|
destination.byteLength,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const OUT_BUFFER = new Uint32Array(2);
|
||||||
|
const OUT_BUFFER_64 = new BigInt64Array(OUT_BUFFER.buffer);
|
||||||
|
class UnsafePointer {
|
||||||
|
static of(value) {
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(UnsafeCallbackPrototype, value)) {
|
||||||
|
return value.pointer;
|
||||||
}
|
}
|
||||||
|
ops.op_ffi_ptr_of(value, OUT_BUFFER);
|
||||||
|
const result = OUT_BUFFER[0] + 2 ** 32 * OUT_BUFFER[1];
|
||||||
|
if (NumberIsSafeInteger(result)) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return OUT_BUFFER_64[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
call(...parameters) {
|
class UnsafeFnPointer {
|
||||||
if (this.definition.nonblocking) {
|
pointer;
|
||||||
if (this.#structSize === null) {
|
definition;
|
||||||
return core.opAsync(
|
#structSize;
|
||||||
|
|
||||||
|
constructor(pointer, definition) {
|
||||||
|
this.pointer = pointer;
|
||||||
|
this.definition = definition;
|
||||||
|
this.#structSize = isStruct(definition.result)
|
||||||
|
? getTypeSizeAndAlignment(definition.result)[0]
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
call(...parameters) {
|
||||||
|
if (this.definition.nonblocking) {
|
||||||
|
if (this.#structSize === null) {
|
||||||
|
return core.opAsync(
|
||||||
|
"op_ffi_call_ptr_nonblocking",
|
||||||
|
this.pointer,
|
||||||
|
this.definition,
|
||||||
|
parameters,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const buffer = new Uint8Array(this.#structSize);
|
||||||
|
return PromisePrototypeThen(
|
||||||
|
core.opAsync(
|
||||||
"op_ffi_call_ptr_nonblocking",
|
"op_ffi_call_ptr_nonblocking",
|
||||||
this.pointer,
|
this.pointer,
|
||||||
this.definition,
|
this.definition,
|
||||||
parameters,
|
parameters,
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const buffer = new Uint8Array(this.#structSize);
|
|
||||||
return PromisePrototypeThen(
|
|
||||||
core.opAsync(
|
|
||||||
"op_ffi_call_ptr_nonblocking",
|
|
||||||
this.pointer,
|
|
||||||
this.definition,
|
|
||||||
parameters,
|
|
||||||
buffer,
|
|
||||||
),
|
|
||||||
() => buffer,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (this.#structSize === null) {
|
|
||||||
return ops.op_ffi_call_ptr(
|
|
||||||
this.pointer,
|
|
||||||
this.definition,
|
|
||||||
parameters,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
const buffer = new Uint8Array(this.#structSize);
|
|
||||||
ops.op_ffi_call_ptr(
|
|
||||||
this.pointer,
|
|
||||||
this.definition,
|
|
||||||
parameters,
|
|
||||||
buffer,
|
buffer,
|
||||||
);
|
),
|
||||||
return buffer;
|
() => buffer,
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isReturnedAsBigInt(type) {
|
|
||||||
return type === "buffer" || type === "pointer" || type === "function" ||
|
|
||||||
type === "u64" || type === "i64" ||
|
|
||||||
type === "usize" || type === "isize";
|
|
||||||
}
|
|
||||||
|
|
||||||
function isI64(type) {
|
|
||||||
return type === "i64" || type === "isize";
|
|
||||||
}
|
|
||||||
|
|
||||||
function isStruct(type) {
|
|
||||||
return typeof type === "object" && type !== null &&
|
|
||||||
typeof type.struct === "object";
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTypeSizeAndAlignment(type, cache = new SafeMap()) {
|
|
||||||
if (isStruct(type)) {
|
|
||||||
const cached = cache.get(type);
|
|
||||||
if (cached !== undefined) {
|
|
||||||
if (cached === null) {
|
|
||||||
throw new TypeError("Recursive struct definition");
|
|
||||||
}
|
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
cache.set(type, null);
|
|
||||||
let size = 0;
|
|
||||||
let alignment = 1;
|
|
||||||
for (const field of new SafeArrayIterator(type.struct)) {
|
|
||||||
const { 0: fieldSize, 1: fieldAlign } = getTypeSizeAndAlignment(
|
|
||||||
field,
|
|
||||||
cache,
|
|
||||||
);
|
|
||||||
alignment = MathMax(alignment, fieldAlign);
|
|
||||||
size = MathCeil(size / fieldAlign) * fieldAlign;
|
|
||||||
size += fieldSize;
|
|
||||||
}
|
|
||||||
size = MathCeil(size / alignment) * alignment;
|
|
||||||
cache.set(type, size);
|
|
||||||
return [size, alignment];
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "bool":
|
|
||||||
case "u8":
|
|
||||||
case "i8":
|
|
||||||
return [1, 1];
|
|
||||||
case "u16":
|
|
||||||
case "i16":
|
|
||||||
return [2, 2];
|
|
||||||
case "u32":
|
|
||||||
case "i32":
|
|
||||||
case "f32":
|
|
||||||
return [4, 4];
|
|
||||||
case "u64":
|
|
||||||
case "i64":
|
|
||||||
case "f64":
|
|
||||||
case "pointer":
|
|
||||||
case "buffer":
|
|
||||||
case "function":
|
|
||||||
case "usize":
|
|
||||||
case "isize":
|
|
||||||
return [8, 8];
|
|
||||||
default:
|
|
||||||
throw new TypeError(`Unsupported type: ${type}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class UnsafeCallback {
|
|
||||||
#refcount;
|
|
||||||
// Internal promise only meant to keep Deno from exiting
|
|
||||||
#refpromise;
|
|
||||||
#rid;
|
|
||||||
definition;
|
|
||||||
callback;
|
|
||||||
pointer;
|
|
||||||
|
|
||||||
constructor(definition, callback) {
|
|
||||||
if (definition.nonblocking) {
|
|
||||||
throw new TypeError(
|
|
||||||
"Invalid UnsafeCallback, cannot be nonblocking",
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
const { 0: rid, 1: pointer } = ops.op_ffi_unsafe_callback_create(
|
} else {
|
||||||
definition,
|
if (this.#structSize === null) {
|
||||||
callback,
|
return ops.op_ffi_call_ptr(
|
||||||
|
this.pointer,
|
||||||
|
this.definition,
|
||||||
|
parameters,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
const buffer = new Uint8Array(this.#structSize);
|
||||||
|
ops.op_ffi_call_ptr(
|
||||||
|
this.pointer,
|
||||||
|
this.definition,
|
||||||
|
parameters,
|
||||||
|
buffer,
|
||||||
|
);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isReturnedAsBigInt(type) {
|
||||||
|
return type === "buffer" || type === "pointer" || type === "function" ||
|
||||||
|
type === "u64" || type === "i64" ||
|
||||||
|
type === "usize" || type === "isize";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isI64(type) {
|
||||||
|
return type === "i64" || type === "isize";
|
||||||
|
}
|
||||||
|
|
||||||
|
function isStruct(type) {
|
||||||
|
return typeof type === "object" && type !== null &&
|
||||||
|
typeof type.struct === "object";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTypeSizeAndAlignment(type, cache = new SafeMap()) {
|
||||||
|
if (isStruct(type)) {
|
||||||
|
const cached = cache.get(type);
|
||||||
|
if (cached !== undefined) {
|
||||||
|
if (cached === null) {
|
||||||
|
throw new TypeError("Recursive struct definition");
|
||||||
|
}
|
||||||
|
return cached;
|
||||||
|
}
|
||||||
|
cache.set(type, null);
|
||||||
|
let size = 0;
|
||||||
|
let alignment = 1;
|
||||||
|
for (const field of new SafeArrayIterator(type.struct)) {
|
||||||
|
const { 0: fieldSize, 1: fieldAlign } = getTypeSizeAndAlignment(
|
||||||
|
field,
|
||||||
|
cache,
|
||||||
);
|
);
|
||||||
this.#refcount = 0;
|
alignment = MathMax(alignment, fieldAlign);
|
||||||
this.#rid = rid;
|
size = MathCeil(size / fieldAlign) * fieldAlign;
|
||||||
this.pointer = pointer;
|
size += fieldSize;
|
||||||
this.definition = definition;
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref() {
|
|
||||||
if (this.#refcount++ === 0) {
|
|
||||||
this.#refpromise = core.opAsync(
|
|
||||||
"op_ffi_unsafe_callback_ref",
|
|
||||||
this.#rid,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return this.#refcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
unref() {
|
|
||||||
// Only decrement refcount if it is positive, and only
|
|
||||||
// unref the callback if refcount reaches zero.
|
|
||||||
if (this.#refcount > 0 && --this.#refcount === 0) {
|
|
||||||
ops.op_ffi_unsafe_callback_unref(this.#rid);
|
|
||||||
}
|
|
||||||
return this.#refcount;
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
this.#refcount = 0;
|
|
||||||
core.close(this.#rid);
|
|
||||||
}
|
}
|
||||||
|
size = MathCeil(size / alignment) * alignment;
|
||||||
|
cache.set(type, size);
|
||||||
|
return [size, alignment];
|
||||||
}
|
}
|
||||||
|
|
||||||
const UnsafeCallbackPrototype = UnsafeCallback.prototype;
|
switch (type) {
|
||||||
|
case "bool":
|
||||||
|
case "u8":
|
||||||
|
case "i8":
|
||||||
|
return [1, 1];
|
||||||
|
case "u16":
|
||||||
|
case "i16":
|
||||||
|
return [2, 2];
|
||||||
|
case "u32":
|
||||||
|
case "i32":
|
||||||
|
case "f32":
|
||||||
|
return [4, 4];
|
||||||
|
case "u64":
|
||||||
|
case "i64":
|
||||||
|
case "f64":
|
||||||
|
case "pointer":
|
||||||
|
case "buffer":
|
||||||
|
case "function":
|
||||||
|
case "usize":
|
||||||
|
case "isize":
|
||||||
|
return [8, 8];
|
||||||
|
default:
|
||||||
|
throw new TypeError(`Unsupported type: ${type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DynamicLibrary {
|
class UnsafeCallback {
|
||||||
#rid;
|
#refcount;
|
||||||
symbols = {};
|
// Internal promise only meant to keep Deno from exiting
|
||||||
|
#refpromise;
|
||||||
|
#rid;
|
||||||
|
definition;
|
||||||
|
callback;
|
||||||
|
pointer;
|
||||||
|
|
||||||
constructor(path, symbols) {
|
constructor(definition, callback) {
|
||||||
({ 0: this.#rid, 1: this.symbols } = ops.op_ffi_load({ path, symbols }));
|
if (definition.nonblocking) {
|
||||||
for (const symbol in symbols) {
|
throw new TypeError(
|
||||||
if (!ObjectPrototypeHasOwnProperty(symbols, symbol)) {
|
"Invalid UnsafeCallback, cannot be nonblocking",
|
||||||
continue;
|
);
|
||||||
|
}
|
||||||
|
const { 0: rid, 1: pointer } = ops.op_ffi_unsafe_callback_create(
|
||||||
|
definition,
|
||||||
|
callback,
|
||||||
|
);
|
||||||
|
this.#refcount = 0;
|
||||||
|
this.#rid = rid;
|
||||||
|
this.pointer = pointer;
|
||||||
|
this.definition = definition;
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref() {
|
||||||
|
if (this.#refcount++ === 0) {
|
||||||
|
this.#refpromise = core.opAsync(
|
||||||
|
"op_ffi_unsafe_callback_ref",
|
||||||
|
this.#rid,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return this.#refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
unref() {
|
||||||
|
// Only decrement refcount if it is positive, and only
|
||||||
|
// unref the callback if refcount reaches zero.
|
||||||
|
if (this.#refcount > 0 && --this.#refcount === 0) {
|
||||||
|
ops.op_ffi_unsafe_callback_unref(this.#rid);
|
||||||
|
}
|
||||||
|
return this.#refcount;
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.#refcount = 0;
|
||||||
|
core.close(this.#rid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const UnsafeCallbackPrototype = UnsafeCallback.prototype;
|
||||||
|
|
||||||
|
class DynamicLibrary {
|
||||||
|
#rid;
|
||||||
|
symbols = {};
|
||||||
|
|
||||||
|
constructor(path, symbols) {
|
||||||
|
({ 0: this.#rid, 1: this.symbols } = ops.op_ffi_load({ path, symbols }));
|
||||||
|
for (const symbol in symbols) {
|
||||||
|
if (!ObjectPrototypeHasOwnProperty(symbols, symbol)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ReflectHas(symbols[symbol], "type")) {
|
||||||
|
const type = symbols[symbol].type;
|
||||||
|
if (type === "void") {
|
||||||
|
throw new TypeError(
|
||||||
|
"Foreign symbol of type 'void' is not supported.",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ReflectHas(symbols[symbol], "type")) {
|
const name = symbols[symbol].name || symbol;
|
||||||
const type = symbols[symbol].type;
|
const value = ops.op_ffi_get_static(
|
||||||
if (type === "void") {
|
this.#rid,
|
||||||
throw new TypeError(
|
name,
|
||||||
"Foreign symbol of type 'void' is not supported.",
|
type,
|
||||||
);
|
);
|
||||||
}
|
ObjectDefineProperty(
|
||||||
|
this.symbols,
|
||||||
|
symbol,
|
||||||
|
{
|
||||||
|
configurable: false,
|
||||||
|
enumerable: true,
|
||||||
|
value,
|
||||||
|
writable: false,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const resultType = symbols[symbol].result;
|
||||||
|
const isStructResult = isStruct(resultType);
|
||||||
|
const structSize = isStructResult
|
||||||
|
? getTypeSizeAndAlignment(resultType)[0]
|
||||||
|
: 0;
|
||||||
|
const needsUnpacking = isReturnedAsBigInt(resultType);
|
||||||
|
|
||||||
const name = symbols[symbol].name || symbol;
|
const isNonBlocking = symbols[symbol].nonblocking;
|
||||||
const value = ops.op_ffi_get_static(
|
if (isNonBlocking) {
|
||||||
this.#rid,
|
ObjectDefineProperty(
|
||||||
name,
|
this.symbols,
|
||||||
type,
|
symbol,
|
||||||
);
|
{
|
||||||
ObjectDefineProperty(
|
configurable: false,
|
||||||
this.symbols,
|
enumerable: true,
|
||||||
symbol,
|
value: (...parameters) => {
|
||||||
{
|
if (isStructResult) {
|
||||||
configurable: false,
|
const buffer = new Uint8Array(structSize);
|
||||||
enumerable: true,
|
const ret = core.opAsync(
|
||||||
value,
|
"op_ffi_call_nonblocking",
|
||||||
writable: false,
|
this.#rid,
|
||||||
|
symbol,
|
||||||
|
parameters,
|
||||||
|
buffer,
|
||||||
|
);
|
||||||
|
return PromisePrototypeThen(
|
||||||
|
ret,
|
||||||
|
() => buffer,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return core.opAsync(
|
||||||
|
"op_ffi_call_nonblocking",
|
||||||
|
this.#rid,
|
||||||
|
symbol,
|
||||||
|
parameters,
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
writable: false,
|
||||||
continue;
|
},
|
||||||
}
|
);
|
||||||
const resultType = symbols[symbol].result;
|
}
|
||||||
const isStructResult = isStruct(resultType);
|
|
||||||
const structSize = isStructResult
|
|
||||||
? getTypeSizeAndAlignment(resultType)[0]
|
|
||||||
: 0;
|
|
||||||
const needsUnpacking = isReturnedAsBigInt(resultType);
|
|
||||||
|
|
||||||
const isNonBlocking = symbols[symbol].nonblocking;
|
if (needsUnpacking && !isNonBlocking) {
|
||||||
if (isNonBlocking) {
|
const call = this.symbols[symbol];
|
||||||
ObjectDefineProperty(
|
const parameters = symbols[symbol].parameters;
|
||||||
this.symbols,
|
const vi = new Int32Array(2);
|
||||||
symbol,
|
const vui = new Uint32Array(vi.buffer);
|
||||||
{
|
const b = new BigInt64Array(vi.buffer);
|
||||||
configurable: false,
|
|
||||||
enumerable: true,
|
|
||||||
value: (...parameters) => {
|
|
||||||
if (isStructResult) {
|
|
||||||
const buffer = new Uint8Array(structSize);
|
|
||||||
const ret = core.opAsync(
|
|
||||||
"op_ffi_call_nonblocking",
|
|
||||||
this.#rid,
|
|
||||||
symbol,
|
|
||||||
parameters,
|
|
||||||
buffer,
|
|
||||||
);
|
|
||||||
return PromisePrototypeThen(
|
|
||||||
ret,
|
|
||||||
() => buffer,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return core.opAsync(
|
|
||||||
"op_ffi_call_nonblocking",
|
|
||||||
this.#rid,
|
|
||||||
symbol,
|
|
||||||
parameters,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
writable: false,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (needsUnpacking && !isNonBlocking) {
|
const params = ArrayPrototypeJoin(
|
||||||
const call = this.symbols[symbol];
|
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
||||||
const parameters = symbols[symbol].parameters;
|
", ",
|
||||||
const vi = new Int32Array(2);
|
);
|
||||||
const vui = new Uint32Array(vi.buffer);
|
// Make sure V8 has no excuse to not optimize this function.
|
||||||
const b = new BigInt64Array(vi.buffer);
|
this.symbols[symbol] = new Function(
|
||||||
|
"vi",
|
||||||
const params = ArrayPrototypeJoin(
|
"vui",
|
||||||
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
"b",
|
||||||
", ",
|
"call",
|
||||||
);
|
"NumberIsSafeInteger",
|
||||||
// Make sure V8 has no excuse to not optimize this function.
|
"Number",
|
||||||
this.symbols[symbol] = new Function(
|
`return function (${params}) {
|
||||||
"vi",
|
|
||||||
"vui",
|
|
||||||
"b",
|
|
||||||
"call",
|
|
||||||
"NumberIsSafeInteger",
|
|
||||||
"Number",
|
|
||||||
`return function (${params}) {
|
|
||||||
call(${params}${parameters.length > 0 ? ", " : ""}vi);
|
call(${params}${parameters.length > 0 ? ", " : ""}vi);
|
||||||
${
|
${
|
||||||
isI64(resultType)
|
isI64(resultType)
|
||||||
? `const n1 = Number(b[0])`
|
? `const n1 = Number(b[0])`
|
||||||
: `const n1 = vui[0] + 2 ** 32 * vui[1]` // Faster path for u64
|
: `const n1 = vui[0] + 2 ** 32 * vui[1]` // Faster path for u64
|
||||||
};
|
};
|
||||||
if (NumberIsSafeInteger(n1)) return n1;
|
if (NumberIsSafeInteger(n1)) return n1;
|
||||||
return b[0];
|
return b[0];
|
||||||
}`,
|
}`,
|
||||||
)(vi, vui, b, call, NumberIsSafeInteger, Number);
|
)(vi, vui, b, call, NumberIsSafeInteger, Number);
|
||||||
} else if (isStructResult && !isNonBlocking) {
|
} else if (isStructResult && !isNonBlocking) {
|
||||||
const call = this.symbols[symbol];
|
const call = this.symbols[symbol];
|
||||||
const parameters = symbols[symbol].parameters;
|
const parameters = symbols[symbol].parameters;
|
||||||
const params = ArrayPrototypeJoin(
|
const params = ArrayPrototypeJoin(
|
||||||
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
||||||
", ",
|
", ",
|
||||||
);
|
);
|
||||||
this.symbols[symbol] = new Function(
|
this.symbols[symbol] = new Function(
|
||||||
"call",
|
"call",
|
||||||
`return function (${params}) {
|
`return function (${params}) {
|
||||||
const buffer = new Uint8Array(${structSize});
|
const buffer = new Uint8Array(${structSize});
|
||||||
call(${params}${parameters.length > 0 ? ", " : ""}buffer);
|
call(${params}${parameters.length > 0 ? ", " : ""}buffer);
|
||||||
return buffer;
|
return buffer;
|
||||||
}`,
|
}`,
|
||||||
)(call);
|
)(call);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
|
||||||
core.close(this.#rid);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function dlopen(path, symbols) {
|
close() {
|
||||||
// URL support is progressively enhanced by util in `runtime/js`.
|
core.close(this.#rid);
|
||||||
const pathFromURL = __bootstrap.util.pathFromURL ?? ((p) => p);
|
|
||||||
return new DynamicLibrary(pathFromURL(path), symbols);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.__bootstrap.ffi = {
|
function dlopen(path, symbols) {
|
||||||
dlopen,
|
// TODO(@crowlKats): remove me
|
||||||
UnsafeCallback,
|
// URL support is progressively enhanced by util in `runtime/js`.
|
||||||
UnsafePointer,
|
const pathFromURL = internals.pathFromURL ?? ((p) => p);
|
||||||
UnsafePointerView,
|
return new DynamicLibrary(pathFromURL(path), symbols);
|
||||||
UnsafeFnPointer,
|
}
|
||||||
};
|
|
||||||
})(this);
|
export {
|
||||||
|
dlopen,
|
||||||
|
UnsafeCallback,
|
||||||
|
UnsafeFnPointer,
|
||||||
|
UnsafePointer,
|
||||||
|
UnsafePointerView,
|
||||||
|
};
|
||||||
|
|
|
@ -84,7 +84,7 @@ pub(crate) struct FfiState {
|
||||||
|
|
||||||
pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
|
pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/ffi",
|
prefix "internal:ext/ffi",
|
||||||
"00_ffi.js",
|
"00_ffi.js",
|
||||||
))
|
))
|
||||||
|
|
1377
ext/flash/01_http.js
1377
ext/flash/01_http.js
File diff suppressed because it is too large
Load diff
|
@ -1514,7 +1514,7 @@ pub fn init<P: FlashPermissions + 'static>(unstable: bool) -> Extension {
|
||||||
"deno_websocket",
|
"deno_websocket",
|
||||||
"deno_http",
|
"deno_http",
|
||||||
])
|
])
|
||||||
.js(deno_core::include_js_files!(
|
.esm(deno_core::include_js_files!(
|
||||||
prefix "internal:ext/flash",
|
prefix "internal:ext/flash",
|
||||||
"01_http.js",
|
"01_http.js",
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,296 +1,318 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const { BadResourcePrototype, InterruptedPrototype, ops } = core;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import { InnerBody } from "internal:ext/fetch/22_body.js";
|
||||||
|
import { Event, setEventTargetData } from "internal:ext/web/02_event.js";
|
||||||
|
import { BlobPrototype } from "internal:ext/web/09_file.js";
|
||||||
|
import {
|
||||||
|
fromInnerResponse,
|
||||||
|
newInnerResponse,
|
||||||
|
ResponsePrototype,
|
||||||
|
toInnerResponse,
|
||||||
|
} from "internal:ext/fetch/23_response.js";
|
||||||
|
import {
|
||||||
|
_flash,
|
||||||
|
fromInnerRequest,
|
||||||
|
newInnerRequest,
|
||||||
|
} from "internal:ext/fetch/23_request.js";
|
||||||
|
import * as abortSignal from "internal:ext/web/03_abort_signal.js";
|
||||||
|
import {
|
||||||
|
_eventLoop,
|
||||||
|
_idleTimeoutDuration,
|
||||||
|
_idleTimeoutTimeout,
|
||||||
|
_protocol,
|
||||||
|
_readyState,
|
||||||
|
_rid,
|
||||||
|
_server,
|
||||||
|
_serverHandleIdleTimeout,
|
||||||
|
WebSocket,
|
||||||
|
} from "internal:ext/websocket/01_websocket.js";
|
||||||
|
import { TcpConn, UnixConn } from "internal:ext/net/01_net.js";
|
||||||
|
import { TlsConn } from "internal:ext/net/02_tls.js";
|
||||||
|
import {
|
||||||
|
Deferred,
|
||||||
|
getReadableStreamResourceBacking,
|
||||||
|
readableStreamClose,
|
||||||
|
readableStreamForRid,
|
||||||
|
ReadableStreamPrototype,
|
||||||
|
} from "internal:ext/web/06_streams.js";
|
||||||
|
const {
|
||||||
|
ArrayPrototypeIncludes,
|
||||||
|
ArrayPrototypePush,
|
||||||
|
ArrayPrototypeSome,
|
||||||
|
Error,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
SafeSetIterator,
|
||||||
|
Set,
|
||||||
|
SetPrototypeAdd,
|
||||||
|
SetPrototypeDelete,
|
||||||
|
StringPrototypeIncludes,
|
||||||
|
StringPrototypeToLowerCase,
|
||||||
|
StringPrototypeSplit,
|
||||||
|
Symbol,
|
||||||
|
SymbolAsyncIterator,
|
||||||
|
TypeError,
|
||||||
|
Uint8Array,
|
||||||
|
Uint8ArrayPrototype,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
const connErrorSymbol = Symbol("connError");
|
||||||
const webidl = window.__bootstrap.webidl;
|
const _deferred = Symbol("upgradeHttpDeferred");
|
||||||
const { InnerBody } = window.__bootstrap.fetchBody;
|
|
||||||
const { Event } = window.__bootstrap.event;
|
|
||||||
const { setEventTargetData } = window.__bootstrap.eventTarget;
|
|
||||||
const { BlobPrototype } = window.__bootstrap.file;
|
|
||||||
const {
|
|
||||||
ResponsePrototype,
|
|
||||||
fromInnerRequest,
|
|
||||||
toInnerResponse,
|
|
||||||
newInnerRequest,
|
|
||||||
newInnerResponse,
|
|
||||||
fromInnerResponse,
|
|
||||||
_flash,
|
|
||||||
} = window.__bootstrap.fetch;
|
|
||||||
const core = window.Deno.core;
|
|
||||||
const { BadResourcePrototype, InterruptedPrototype, ops } = core;
|
|
||||||
const { ReadableStreamPrototype } = window.__bootstrap.streams;
|
|
||||||
const abortSignal = window.__bootstrap.abortSignal;
|
|
||||||
const {
|
|
||||||
WebSocket,
|
|
||||||
_rid,
|
|
||||||
_readyState,
|
|
||||||
_eventLoop,
|
|
||||||
_protocol,
|
|
||||||
_server,
|
|
||||||
_idleTimeoutDuration,
|
|
||||||
_idleTimeoutTimeout,
|
|
||||||
_serverHandleIdleTimeout,
|
|
||||||
} = window.__bootstrap.webSocket;
|
|
||||||
const { TcpConn, UnixConn } = window.__bootstrap.net;
|
|
||||||
const { TlsConn } = window.__bootstrap.tls;
|
|
||||||
const {
|
|
||||||
Deferred,
|
|
||||||
getReadableStreamResourceBacking,
|
|
||||||
readableStreamForRid,
|
|
||||||
readableStreamClose,
|
|
||||||
} = window.__bootstrap.streams;
|
|
||||||
const {
|
|
||||||
ArrayPrototypeIncludes,
|
|
||||||
ArrayPrototypePush,
|
|
||||||
ArrayPrototypeSome,
|
|
||||||
Error,
|
|
||||||
ObjectPrototypeIsPrototypeOf,
|
|
||||||
SafeSetIterator,
|
|
||||||
Set,
|
|
||||||
SetPrototypeAdd,
|
|
||||||
SetPrototypeDelete,
|
|
||||||
StringPrototypeIncludes,
|
|
||||||
StringPrototypeToLowerCase,
|
|
||||||
StringPrototypeSplit,
|
|
||||||
Symbol,
|
|
||||||
SymbolAsyncIterator,
|
|
||||||
TypeError,
|
|
||||||
Uint8Array,
|
|
||||||
Uint8ArrayPrototype,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
const connErrorSymbol = Symbol("connError");
|
class HttpConn {
|
||||||
const _deferred = Symbol("upgradeHttpDeferred");
|
#rid = 0;
|
||||||
|
#closed = false;
|
||||||
|
#remoteAddr;
|
||||||
|
#localAddr;
|
||||||
|
|
||||||
class HttpConn {
|
// This set holds resource ids of resources
|
||||||
#rid = 0;
|
// that were created during lifecycle of this request.
|
||||||
#closed = false;
|
// When the connection is closed these resources should be closed
|
||||||
#remoteAddr;
|
// as well.
|
||||||
#localAddr;
|
managedResources = new Set();
|
||||||
|
|
||||||
// This set holds resource ids of resources
|
constructor(rid, remoteAddr, localAddr) {
|
||||||
// that were created during lifecycle of this request.
|
this.#rid = rid;
|
||||||
// When the connection is closed these resources should be closed
|
this.#remoteAddr = remoteAddr;
|
||||||
// as well.
|
this.#localAddr = localAddr;
|
||||||
managedResources = new Set();
|
}
|
||||||
|
|
||||||
constructor(rid, remoteAddr, localAddr) {
|
/** @returns {number} */
|
||||||
this.#rid = rid;
|
get rid() {
|
||||||
this.#remoteAddr = remoteAddr;
|
return this.#rid;
|
||||||
this.#localAddr = localAddr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {number} */
|
/** @returns {Promise<RequestEvent | null>} */
|
||||||
get rid() {
|
async nextRequest() {
|
||||||
return this.#rid;
|
let nextRequest;
|
||||||
}
|
try {
|
||||||
|
nextRequest = await core.opAsync("op_http_accept", this.#rid);
|
||||||
/** @returns {Promise<RequestEvent | null>} */
|
} catch (error) {
|
||||||
async nextRequest() {
|
this.close();
|
||||||
let nextRequest;
|
// A connection error seen here would cause disrupted responses to throw
|
||||||
try {
|
// a generic `BadResource` error. Instead store this error and replace
|
||||||
nextRequest = await core.opAsync("op_http_accept", this.#rid);
|
// those with it.
|
||||||
} catch (error) {
|
this[connErrorSymbol] = error;
|
||||||
this.close();
|
if (
|
||||||
// A connection error seen here would cause disrupted responses to throw
|
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) ||
|
||||||
// a generic `BadResource` error. Instead store this error and replace
|
ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error) ||
|
||||||
// those with it.
|
StringPrototypeIncludes(error.message, "connection closed")
|
||||||
this[connErrorSymbol] = error;
|
) {
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) ||
|
|
||||||
ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error) ||
|
|
||||||
StringPrototypeIncludes(error.message, "connection closed")
|
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
if (nextRequest == null) {
|
|
||||||
// Work-around for servers (deno_std/http in particular) that call
|
|
||||||
// `nextRequest()` before upgrading a previous request which has a
|
|
||||||
// `connection: upgrade` header.
|
|
||||||
await null;
|
|
||||||
|
|
||||||
this.close();
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
if (nextRequest == null) {
|
||||||
|
// Work-around for servers (deno_std/http in particular) that call
|
||||||
|
// `nextRequest()` before upgrading a previous request which has a
|
||||||
|
// `connection: upgrade` header.
|
||||||
|
await null;
|
||||||
|
|
||||||
const { 0: streamRid, 1: method, 2: url } = nextRequest;
|
this.close();
|
||||||
SetPrototypeAdd(this.managedResources, streamRid);
|
return null;
|
||||||
|
|
||||||
/** @type {ReadableStream<Uint8Array> | undefined} */
|
|
||||||
let body = null;
|
|
||||||
// There might be a body, but we don't expose it for GET/HEAD requests.
|
|
||||||
// It will be closed automatically once the request has been handled and
|
|
||||||
// the response has been sent.
|
|
||||||
if (method !== "GET" && method !== "HEAD") {
|
|
||||||
body = readableStreamForRid(streamRid, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const innerRequest = newInnerRequest(
|
|
||||||
() => method,
|
|
||||||
url,
|
|
||||||
() => ops.op_http_headers(streamRid),
|
|
||||||
body !== null ? new InnerBody(body) : null,
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
const signal = abortSignal.newSignal();
|
|
||||||
const request = fromInnerRequest(
|
|
||||||
innerRequest,
|
|
||||||
signal,
|
|
||||||
"immutable",
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
|
|
||||||
const respondWith = createRespondWith(
|
|
||||||
this,
|
|
||||||
streamRid,
|
|
||||||
request,
|
|
||||||
this.#remoteAddr,
|
|
||||||
this.#localAddr,
|
|
||||||
);
|
|
||||||
|
|
||||||
return { request, respondWith };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {void} */
|
const { 0: streamRid, 1: method, 2: url } = nextRequest;
|
||||||
close() {
|
SetPrototypeAdd(this.managedResources, streamRid);
|
||||||
if (!this.#closed) {
|
|
||||||
this.#closed = true;
|
/** @type {ReadableStream<Uint8Array> | undefined} */
|
||||||
core.close(this.#rid);
|
let body = null;
|
||||||
for (const rid of new SafeSetIterator(this.managedResources)) {
|
// There might be a body, but we don't expose it for GET/HEAD requests.
|
||||||
SetPrototypeDelete(this.managedResources, rid);
|
// It will be closed automatically once the request has been handled and
|
||||||
core.close(rid);
|
// the response has been sent.
|
||||||
}
|
if (method !== "GET" && method !== "HEAD") {
|
||||||
}
|
body = readableStreamForRid(streamRid, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SymbolAsyncIterator]() {
|
const innerRequest = newInnerRequest(
|
||||||
// deno-lint-ignore no-this-alias
|
() => method,
|
||||||
const httpConn = this;
|
url,
|
||||||
return {
|
() => ops.op_http_headers(streamRid),
|
||||||
async next() {
|
body !== null ? new InnerBody(body) : null,
|
||||||
const reqEvt = await httpConn.nextRequest();
|
false,
|
||||||
// Change with caution, current form avoids a v8 deopt
|
);
|
||||||
return { value: reqEvt ?? undefined, done: reqEvt === null };
|
const signal = abortSignal.newSignal();
|
||||||
},
|
const request = fromInnerRequest(
|
||||||
};
|
innerRequest,
|
||||||
|
signal,
|
||||||
|
"immutable",
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
|
const respondWith = createRespondWith(
|
||||||
|
this,
|
||||||
|
streamRid,
|
||||||
|
request,
|
||||||
|
this.#remoteAddr,
|
||||||
|
this.#localAddr,
|
||||||
|
);
|
||||||
|
|
||||||
|
return { request, respondWith };
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {void} */
|
||||||
|
close() {
|
||||||
|
if (!this.#closed) {
|
||||||
|
this.#closed = true;
|
||||||
|
core.close(this.#rid);
|
||||||
|
for (const rid of new SafeSetIterator(this.managedResources)) {
|
||||||
|
SetPrototypeDelete(this.managedResources, rid);
|
||||||
|
core.close(rid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createRespondWith(
|
[SymbolAsyncIterator]() {
|
||||||
httpConn,
|
// deno-lint-ignore no-this-alias
|
||||||
streamRid,
|
const httpConn = this;
|
||||||
request,
|
return {
|
||||||
remoteAddr,
|
async next() {
|
||||||
localAddr,
|
const reqEvt = await httpConn.nextRequest();
|
||||||
) {
|
// Change with caution, current form avoids a v8 deopt
|
||||||
return async function respondWith(resp) {
|
return { value: reqEvt ?? undefined, done: reqEvt === null };
|
||||||
try {
|
},
|
||||||
resp = await resp;
|
};
|
||||||
if (!(ObjectPrototypeIsPrototypeOf(ResponsePrototype, resp))) {
|
}
|
||||||
throw new TypeError(
|
}
|
||||||
"First argument to respondWith must be a Response or a promise resolving to a Response.",
|
|
||||||
);
|
function createRespondWith(
|
||||||
|
httpConn,
|
||||||
|
streamRid,
|
||||||
|
request,
|
||||||
|
remoteAddr,
|
||||||
|
localAddr,
|
||||||
|
) {
|
||||||
|
return async function respondWith(resp) {
|
||||||
|
try {
|
||||||
|
resp = await resp;
|
||||||
|
if (!(ObjectPrototypeIsPrototypeOf(ResponsePrototype, resp))) {
|
||||||
|
throw new TypeError(
|
||||||
|
"First argument to respondWith must be a Response or a promise resolving to a Response.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const innerResp = toInnerResponse(resp);
|
||||||
|
|
||||||
|
// If response body length is known, it will be sent synchronously in a
|
||||||
|
// single op, in other case a "response body" resource will be created and
|
||||||
|
// we'll be streaming it.
|
||||||
|
/** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
|
||||||
|
let respBody = null;
|
||||||
|
if (innerResp.body !== null) {
|
||||||
|
if (innerResp.body.unusable()) {
|
||||||
|
throw new TypeError("Body is unusable.");
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
const innerResp = toInnerResponse(resp);
|
ObjectPrototypeIsPrototypeOf(
|
||||||
|
ReadableStreamPrototype,
|
||||||
// If response body length is known, it will be sent synchronously in a
|
innerResp.body.streamOrStatic,
|
||||||
// single op, in other case a "response body" resource will be created and
|
)
|
||||||
// we'll be streaming it.
|
) {
|
||||||
/** @type {ReadableStream<Uint8Array> | Uint8Array | null} */
|
|
||||||
let respBody = null;
|
|
||||||
if (innerResp.body !== null) {
|
|
||||||
if (innerResp.body.unusable()) {
|
|
||||||
throw new TypeError("Body is unusable.");
|
|
||||||
}
|
|
||||||
if (
|
if (
|
||||||
|
innerResp.body.length === null ||
|
||||||
ObjectPrototypeIsPrototypeOf(
|
ObjectPrototypeIsPrototypeOf(
|
||||||
ReadableStreamPrototype,
|
BlobPrototype,
|
||||||
innerResp.body.streamOrStatic,
|
innerResp.body.source,
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
if (
|
respBody = innerResp.body.stream;
|
||||||
innerResp.body.length === null ||
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
BlobPrototype,
|
|
||||||
innerResp.body.source,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
respBody = innerResp.body.stream;
|
|
||||||
} else {
|
|
||||||
const reader = innerResp.body.stream.getReader();
|
|
||||||
const r1 = await reader.read();
|
|
||||||
if (r1.done) {
|
|
||||||
respBody = new Uint8Array(0);
|
|
||||||
} else {
|
|
||||||
respBody = r1.value;
|
|
||||||
const r2 = await reader.read();
|
|
||||||
if (!r2.done) throw new TypeError("Unreachable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
innerResp.body.streamOrStatic.consumed = true;
|
const reader = innerResp.body.stream.getReader();
|
||||||
respBody = innerResp.body.streamOrStatic.body;
|
const r1 = await reader.read();
|
||||||
|
if (r1.done) {
|
||||||
|
respBody = new Uint8Array(0);
|
||||||
|
} else {
|
||||||
|
respBody = r1.value;
|
||||||
|
const r2 = await reader.read();
|
||||||
|
if (!r2.done) throw new TypeError("Unreachable");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
respBody = new Uint8Array(0);
|
innerResp.body.streamOrStatic.consumed = true;
|
||||||
|
respBody = innerResp.body.streamOrStatic.body;
|
||||||
}
|
}
|
||||||
const isStreamingResponseBody = !(
|
} else {
|
||||||
typeof respBody === "string" ||
|
respBody = new Uint8Array(0);
|
||||||
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, respBody)
|
}
|
||||||
|
const isStreamingResponseBody = !(
|
||||||
|
typeof respBody === "string" ||
|
||||||
|
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, respBody)
|
||||||
|
);
|
||||||
|
try {
|
||||||
|
await core.opAsync(
|
||||||
|
"op_http_write_headers",
|
||||||
|
streamRid,
|
||||||
|
innerResp.status ?? 200,
|
||||||
|
innerResp.headerList,
|
||||||
|
isStreamingResponseBody ? null : respBody,
|
||||||
);
|
);
|
||||||
try {
|
} catch (error) {
|
||||||
await core.opAsync(
|
const connError = httpConn[connErrorSymbol];
|
||||||
"op_http_write_headers",
|
if (
|
||||||
streamRid,
|
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
|
||||||
innerResp.status ?? 200,
|
connError != null
|
||||||
innerResp.headerList,
|
) {
|
||||||
isStreamingResponseBody ? null : respBody,
|
// deno-lint-ignore no-ex-assign
|
||||||
);
|
error = new connError.constructor(connError.message);
|
||||||
} catch (error) {
|
|
||||||
const connError = httpConn[connErrorSymbol];
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
|
|
||||||
connError != null
|
|
||||||
) {
|
|
||||||
// deno-lint-ignore no-ex-assign
|
|
||||||
error = new connError.constructor(connError.message);
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
respBody !== null &&
|
|
||||||
ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
|
|
||||||
) {
|
|
||||||
await respBody.cancel(error);
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
respBody !== null &&
|
||||||
|
ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
|
||||||
|
) {
|
||||||
|
await respBody.cancel(error);
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
if (isStreamingResponseBody) {
|
if (isStreamingResponseBody) {
|
||||||
let success = false;
|
let success = false;
|
||||||
if (
|
if (
|
||||||
respBody === null ||
|
respBody === null ||
|
||||||
!ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
|
!ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
|
||||||
) {
|
) {
|
||||||
throw new TypeError("Unreachable");
|
throw new TypeError("Unreachable");
|
||||||
|
}
|
||||||
|
const resourceBacking = getReadableStreamResourceBacking(respBody);
|
||||||
|
let reader;
|
||||||
|
if (resourceBacking) {
|
||||||
|
if (respBody.locked) {
|
||||||
|
throw new TypeError("ReadableStream is locked.");
|
||||||
}
|
}
|
||||||
const resourceBacking = getReadableStreamResourceBacking(respBody);
|
reader = respBody.getReader(); // Aquire JS lock.
|
||||||
let reader;
|
try {
|
||||||
if (resourceBacking) {
|
await core.opAsync(
|
||||||
if (respBody.locked) {
|
"op_http_write_resource",
|
||||||
throw new TypeError("ReadableStream is locked.");
|
streamRid,
|
||||||
|
resourceBacking.rid,
|
||||||
|
);
|
||||||
|
if (resourceBacking.autoClose) core.tryClose(resourceBacking.rid);
|
||||||
|
readableStreamClose(respBody); // Release JS lock.
|
||||||
|
success = true;
|
||||||
|
} catch (error) {
|
||||||
|
const connError = httpConn[connErrorSymbol];
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
|
||||||
|
connError != null
|
||||||
|
) {
|
||||||
|
// deno-lint-ignore no-ex-assign
|
||||||
|
error = new connError.constructor(connError.message);
|
||||||
|
}
|
||||||
|
await reader.cancel(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
reader = respBody.getReader();
|
||||||
|
while (true) {
|
||||||
|
const { value, done } = await reader.read();
|
||||||
|
if (done) break;
|
||||||
|
if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, value)) {
|
||||||
|
await reader.cancel(new TypeError("Value not a Uint8Array"));
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
reader = respBody.getReader(); // Aquire JS lock.
|
|
||||||
try {
|
try {
|
||||||
await core.opAsync(
|
await core.opAsync("op_http_write", streamRid, value);
|
||||||
"op_http_write_resource",
|
|
||||||
streamRid,
|
|
||||||
resourceBacking.rid,
|
|
||||||
);
|
|
||||||
if (resourceBacking.autoClose) core.tryClose(resourceBacking.rid);
|
|
||||||
readableStreamClose(respBody); // Release JS lock.
|
|
||||||
success = true;
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const connError = httpConn[connErrorSymbol];
|
const connError = httpConn[connErrorSymbol];
|
||||||
if (
|
if (
|
||||||
|
@ -303,176 +325,147 @@
|
||||||
await reader.cancel(error);
|
await reader.cancel(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
reader = respBody.getReader();
|
|
||||||
while (true) {
|
|
||||||
const { value, done } = await reader.read();
|
|
||||||
if (done) break;
|
|
||||||
if (!ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, value)) {
|
|
||||||
await reader.cancel(new TypeError("Value not a Uint8Array"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
await core.opAsync("op_http_write", streamRid, value);
|
|
||||||
} catch (error) {
|
|
||||||
const connError = httpConn[connErrorSymbol];
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) &&
|
|
||||||
connError != null
|
|
||||||
) {
|
|
||||||
// deno-lint-ignore no-ex-assign
|
|
||||||
error = new connError.constructor(connError.message);
|
|
||||||
}
|
|
||||||
await reader.cancel(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
success = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
try {
|
|
||||||
await core.opAsync("op_http_shutdown", streamRid);
|
|
||||||
} catch (error) {
|
|
||||||
await reader.cancel(error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
success = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const deferred = request[_deferred];
|
if (success) {
|
||||||
if (deferred) {
|
try {
|
||||||
const res = await core.opAsync("op_http_upgrade", streamRid);
|
await core.opAsync("op_http_shutdown", streamRid);
|
||||||
let conn;
|
} catch (error) {
|
||||||
if (res.connType === "tcp") {
|
await reader.cancel(error);
|
||||||
conn = new TcpConn(res.connRid, remoteAddr, localAddr);
|
throw error;
|
||||||
} else if (res.connType === "tls") {
|
|
||||||
conn = new TlsConn(res.connRid, remoteAddr, localAddr);
|
|
||||||
} else if (res.connType === "unix") {
|
|
||||||
conn = new UnixConn(res.connRid, remoteAddr, localAddr);
|
|
||||||
} else {
|
|
||||||
throw new Error("unreachable");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deferred.resolve([conn, res.readBuf]);
|
|
||||||
}
|
|
||||||
const ws = resp[_ws];
|
|
||||||
if (ws) {
|
|
||||||
const wsRid = await core.opAsync(
|
|
||||||
"op_http_upgrade_websocket",
|
|
||||||
streamRid,
|
|
||||||
);
|
|
||||||
ws[_rid] = wsRid;
|
|
||||||
ws[_protocol] = resp.headers.get("sec-websocket-protocol");
|
|
||||||
|
|
||||||
httpConn.close();
|
|
||||||
|
|
||||||
ws[_readyState] = WebSocket.OPEN;
|
|
||||||
const event = new Event("open");
|
|
||||||
ws.dispatchEvent(event);
|
|
||||||
|
|
||||||
ws[_eventLoop]();
|
|
||||||
if (ws[_idleTimeoutDuration]) {
|
|
||||||
ws.addEventListener(
|
|
||||||
"close",
|
|
||||||
() => clearTimeout(ws[_idleTimeoutTimeout]),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
ws[_serverHandleIdleTimeout]();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (SetPrototypeDelete(httpConn.managedResources, streamRid)) {
|
|
||||||
core.close(streamRid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const _ws = Symbol("[[associated_ws]]");
|
const deferred = request[_deferred];
|
||||||
|
if (deferred) {
|
||||||
|
const res = await core.opAsync("op_http_upgrade", streamRid);
|
||||||
|
let conn;
|
||||||
|
if (res.connType === "tcp") {
|
||||||
|
conn = new TcpConn(res.connRid, remoteAddr, localAddr);
|
||||||
|
} else if (res.connType === "tls") {
|
||||||
|
conn = new TlsConn(res.connRid, remoteAddr, localAddr);
|
||||||
|
} else if (res.connType === "unix") {
|
||||||
|
conn = new UnixConn(res.connRid, remoteAddr, localAddr);
|
||||||
|
} else {
|
||||||
|
throw new Error("unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
function upgradeWebSocket(request, options = {}) {
|
deferred.resolve([conn, res.readBuf]);
|
||||||
const upgrade = request.headers.get("upgrade");
|
}
|
||||||
const upgradeHasWebSocketOption = upgrade !== null &&
|
const ws = resp[_ws];
|
||||||
ArrayPrototypeSome(
|
if (ws) {
|
||||||
StringPrototypeSplit(upgrade, /\s*,\s*/),
|
const wsRid = await core.opAsync(
|
||||||
(option) => StringPrototypeToLowerCase(option) === "websocket",
|
"op_http_upgrade_websocket",
|
||||||
);
|
streamRid,
|
||||||
if (!upgradeHasWebSocketOption) {
|
|
||||||
throw new TypeError(
|
|
||||||
"Invalid Header: 'upgrade' header must contain 'websocket'",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const connection = request.headers.get("connection");
|
|
||||||
const connectionHasUpgradeOption = connection !== null &&
|
|
||||||
ArrayPrototypeSome(
|
|
||||||
StringPrototypeSplit(connection, /\s*,\s*/),
|
|
||||||
(option) => StringPrototypeToLowerCase(option) === "upgrade",
|
|
||||||
);
|
|
||||||
if (!connectionHasUpgradeOption) {
|
|
||||||
throw new TypeError(
|
|
||||||
"Invalid Header: 'connection' header must contain 'Upgrade'",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const websocketKey = request.headers.get("sec-websocket-key");
|
|
||||||
if (websocketKey === null) {
|
|
||||||
throw new TypeError(
|
|
||||||
"Invalid Header: 'sec-websocket-key' header must be set",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const accept = ops.op_http_websocket_accept_header(websocketKey);
|
|
||||||
|
|
||||||
const r = newInnerResponse(101);
|
|
||||||
r.headerList = [
|
|
||||||
["upgrade", "websocket"],
|
|
||||||
["connection", "Upgrade"],
|
|
||||||
["sec-websocket-accept", accept],
|
|
||||||
];
|
|
||||||
|
|
||||||
const protocolsStr = request.headers.get("sec-websocket-protocol") || "";
|
|
||||||
const protocols = StringPrototypeSplit(protocolsStr, ", ");
|
|
||||||
if (protocols && options.protocol) {
|
|
||||||
if (ArrayPrototypeIncludes(protocols, options.protocol)) {
|
|
||||||
ArrayPrototypePush(r.headerList, [
|
|
||||||
"sec-websocket-protocol",
|
|
||||||
options.protocol,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
throw new TypeError(
|
|
||||||
`Protocol '${options.protocol}' not in the request's protocol list (non negotiable)`,
|
|
||||||
);
|
);
|
||||||
|
ws[_rid] = wsRid;
|
||||||
|
ws[_protocol] = resp.headers.get("sec-websocket-protocol");
|
||||||
|
|
||||||
|
httpConn.close();
|
||||||
|
|
||||||
|
ws[_readyState] = WebSocket.OPEN;
|
||||||
|
const event = new Event("open");
|
||||||
|
ws.dispatchEvent(event);
|
||||||
|
|
||||||
|
ws[_eventLoop]();
|
||||||
|
if (ws[_idleTimeoutDuration]) {
|
||||||
|
ws.addEventListener(
|
||||||
|
"close",
|
||||||
|
() => clearTimeout(ws[_idleTimeoutTimeout]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ws[_serverHandleIdleTimeout]();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (SetPrototypeDelete(httpConn.managedResources, streamRid)) {
|
||||||
|
core.close(streamRid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const response = fromInnerResponse(r, "immutable");
|
const _ws = Symbol("[[associated_ws]]");
|
||||||
|
|
||||||
const socket = webidl.createBranded(WebSocket);
|
function upgradeWebSocket(request, options = {}) {
|
||||||
setEventTargetData(socket);
|
const upgrade = request.headers.get("upgrade");
|
||||||
socket[_server] = true;
|
const upgradeHasWebSocketOption = upgrade !== null &&
|
||||||
response[_ws] = socket;
|
ArrayPrototypeSome(
|
||||||
socket[_idleTimeoutDuration] = options.idleTimeout ?? 120;
|
StringPrototypeSplit(upgrade, /\s*,\s*/),
|
||||||
socket[_idleTimeoutTimeout] = null;
|
(option) => StringPrototypeToLowerCase(option) === "websocket",
|
||||||
|
);
|
||||||
return { response, socket };
|
if (!upgradeHasWebSocketOption) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Invalid Header: 'upgrade' header must contain 'websocket'",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function upgradeHttp(req) {
|
const connection = request.headers.get("connection");
|
||||||
if (req[_flash]) {
|
const connectionHasUpgradeOption = connection !== null &&
|
||||||
|
ArrayPrototypeSome(
|
||||||
|
StringPrototypeSplit(connection, /\s*,\s*/),
|
||||||
|
(option) => StringPrototypeToLowerCase(option) === "upgrade",
|
||||||
|
);
|
||||||
|
if (!connectionHasUpgradeOption) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Invalid Header: 'connection' header must contain 'Upgrade'",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const websocketKey = request.headers.get("sec-websocket-key");
|
||||||
|
if (websocketKey === null) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Invalid Header: 'sec-websocket-key' header must be set",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const accept = ops.op_http_websocket_accept_header(websocketKey);
|
||||||
|
|
||||||
|
const r = newInnerResponse(101);
|
||||||
|
r.headerList = [
|
||||||
|
["upgrade", "websocket"],
|
||||||
|
["connection", "Upgrade"],
|
||||||
|
["sec-websocket-accept", accept],
|
||||||
|
];
|
||||||
|
|
||||||
|
const protocolsStr = request.headers.get("sec-websocket-protocol") || "";
|
||||||
|
const protocols = StringPrototypeSplit(protocolsStr, ", ");
|
||||||
|
if (protocols && options.protocol) {
|
||||||
|
if (ArrayPrototypeIncludes(protocols, options.protocol)) {
|
||||||
|
ArrayPrototypePush(r.headerList, [
|
||||||
|
"sec-websocket-protocol",
|
||||||
|
options.protocol,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
throw new TypeError(
|
throw new TypeError(
|
||||||
"Flash requests can not be upgraded with `upgradeHttp`. Use `upgradeHttpRaw` instead.",
|
`Protocol '${options.protocol}' not in the request's protocol list (non negotiable)`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
req[_deferred] = new Deferred();
|
|
||||||
return req[_deferred].promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.http = {
|
const response = fromInnerResponse(r, "immutable");
|
||||||
HttpConn,
|
|
||||||
upgradeWebSocket,
|
const socket = webidl.createBranded(WebSocket);
|
||||||
upgradeHttp,
|
setEventTargetData(socket);
|
||||||
_ws,
|
socket[_server] = true;
|
||||||
};
|
response[_ws] = socket;
|
||||||
})(this);
|
socket[_idleTimeoutDuration] = options.idleTimeout ?? 120;
|
||||||
|
socket[_idleTimeoutTimeout] = null;
|
||||||
|
|
||||||
|
return { response, socket };
|
||||||
|
}
|
||||||
|
|
||||||
|
function upgradeHttp(req) {
|
||||||
|
if (req[_flash]) {
|
||||||
|
throw new TypeError(
|
||||||
|
"Flash requests can not be upgraded with `upgradeHttp`. Use `upgradeHttpRaw` instead.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
req[_deferred] = new Deferred();
|
||||||
|
return req[_deferred].promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { _ws, HttpConn, upgradeHttp, upgradeWebSocket };
|
||||||
|
|
|
@ -80,7 +80,7 @@ mod reader_stream;
|
||||||
pub fn init() -> Extension {
|
pub fn init() -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_web", "deno_net", "deno_fetch", "deno_websocket"])
|
.dependencies(vec!["deno_web", "deno_net", "deno_fetch", "deno_websocket"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/http",
|
prefix "internal:ext/http",
|
||||||
"01_http.js",
|
"01_http.js",
|
||||||
))
|
))
|
||||||
|
|
|
@ -1,423 +1,421 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const { BadResourcePrototype, InterruptedPrototype, ops } = core;
|
||||||
const { BadResourcePrototype, InterruptedPrototype, ops } = core;
|
import {
|
||||||
const {
|
readableStreamForRidUnrefable,
|
||||||
readableStreamForRidUnrefable,
|
readableStreamForRidUnrefableRef,
|
||||||
readableStreamForRidUnrefableRef,
|
readableStreamForRidUnrefableUnref,
|
||||||
readableStreamForRidUnrefableUnref,
|
writableStreamForRid,
|
||||||
writableStreamForRid,
|
} from "internal:ext/web/06_streams.js";
|
||||||
} = window.__bootstrap.streams;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
Error,
|
Error,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
PromiseResolve,
|
PromiseResolve,
|
||||||
SymbolAsyncIterator,
|
SymbolAsyncIterator,
|
||||||
SymbolFor,
|
SymbolFor,
|
||||||
TypedArrayPrototypeSubarray,
|
TypedArrayPrototypeSubarray,
|
||||||
TypeError,
|
TypeError,
|
||||||
Uint8Array,
|
Uint8Array,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
|
|
||||||
const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
|
const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
|
||||||
|
|
||||||
async function write(rid, data) {
|
async function write(rid, data) {
|
||||||
return await core.write(rid, data);
|
return await core.write(rid, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown(rid) {
|
||||||
|
return core.shutdown(rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDns(query, recordType, options) {
|
||||||
|
return core.opAsync("op_dns_resolve", { query, recordType, options });
|
||||||
|
}
|
||||||
|
|
||||||
|
class Conn {
|
||||||
|
#rid = 0;
|
||||||
|
#remoteAddr = null;
|
||||||
|
#localAddr = null;
|
||||||
|
#unref = false;
|
||||||
|
#pendingReadPromiseIds = [];
|
||||||
|
|
||||||
|
#readable;
|
||||||
|
#writable;
|
||||||
|
|
||||||
|
constructor(rid, remoteAddr, localAddr) {
|
||||||
|
this.#rid = rid;
|
||||||
|
this.#remoteAddr = remoteAddr;
|
||||||
|
this.#localAddr = localAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
function shutdown(rid) {
|
get rid() {
|
||||||
return core.shutdown(rid);
|
return this.#rid;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveDns(query, recordType, options) {
|
get remoteAddr() {
|
||||||
return core.opAsync("op_dns_resolve", { query, recordType, options });
|
return this.#remoteAddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Conn {
|
get localAddr() {
|
||||||
#rid = 0;
|
return this.#localAddr;
|
||||||
#remoteAddr = null;
|
}
|
||||||
#localAddr = null;
|
|
||||||
#unref = false;
|
|
||||||
#pendingReadPromiseIds = [];
|
|
||||||
|
|
||||||
#readable;
|
write(p) {
|
||||||
#writable;
|
return write(this.rid, p);
|
||||||
|
}
|
||||||
|
|
||||||
constructor(rid, remoteAddr, localAddr) {
|
async read(buffer) {
|
||||||
this.#rid = rid;
|
if (buffer.length === 0) {
|
||||||
this.#remoteAddr = remoteAddr;
|
return 0;
|
||||||
this.#localAddr = localAddr;
|
|
||||||
}
|
}
|
||||||
|
const promise = core.read(this.rid, buffer);
|
||||||
get rid() {
|
const promiseId = promise[promiseIdSymbol];
|
||||||
return this.#rid;
|
if (this.#unref) core.unrefOp(promiseId);
|
||||||
|
this.#pendingReadPromiseIds.push(promiseId);
|
||||||
|
let nread;
|
||||||
|
try {
|
||||||
|
nread = await promise;
|
||||||
|
} catch (e) {
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
this.#pendingReadPromiseIds = this.#pendingReadPromiseIds.filter((id) =>
|
||||||
|
id !== promiseId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
return nread === 0 ? null : nread;
|
||||||
|
}
|
||||||
|
|
||||||
get remoteAddr() {
|
close() {
|
||||||
return this.#remoteAddr;
|
core.close(this.rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
get localAddr() {
|
closeWrite() {
|
||||||
return this.#localAddr;
|
return shutdown(this.rid);
|
||||||
}
|
}
|
||||||
|
|
||||||
write(p) {
|
get readable() {
|
||||||
return write(this.rid, p);
|
if (this.#readable === undefined) {
|
||||||
}
|
this.#readable = readableStreamForRidUnrefable(this.rid);
|
||||||
|
if (this.#unref) {
|
||||||
async read(buffer) {
|
|
||||||
if (buffer.length === 0) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const promise = core.read(this.rid, buffer);
|
|
||||||
const promiseId = promise[promiseIdSymbol];
|
|
||||||
if (this.#unref) core.unrefOp(promiseId);
|
|
||||||
this.#pendingReadPromiseIds.push(promiseId);
|
|
||||||
let nread;
|
|
||||||
try {
|
|
||||||
nread = await promise;
|
|
||||||
} catch (e) {
|
|
||||||
throw e;
|
|
||||||
} finally {
|
|
||||||
this.#pendingReadPromiseIds = this.#pendingReadPromiseIds.filter((id) =>
|
|
||||||
id !== promiseId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return nread === 0 ? null : nread;
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
core.close(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
closeWrite() {
|
|
||||||
return shutdown(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
get readable() {
|
|
||||||
if (this.#readable === undefined) {
|
|
||||||
this.#readable = readableStreamForRidUnrefable(this.rid);
|
|
||||||
if (this.#unref) {
|
|
||||||
readableStreamForRidUnrefableUnref(this.#readable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this.#readable;
|
|
||||||
}
|
|
||||||
|
|
||||||
get writable() {
|
|
||||||
if (this.#writable === undefined) {
|
|
||||||
this.#writable = writableStreamForRid(this.rid);
|
|
||||||
}
|
|
||||||
return this.#writable;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref() {
|
|
||||||
this.#unref = false;
|
|
||||||
if (this.#readable) {
|
|
||||||
readableStreamForRidUnrefableRef(this.#readable);
|
|
||||||
}
|
|
||||||
this.#pendingReadPromiseIds.forEach((id) => core.refOp(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
unref() {
|
|
||||||
this.#unref = true;
|
|
||||||
if (this.#readable) {
|
|
||||||
readableStreamForRidUnrefableUnref(this.#readable);
|
readableStreamForRidUnrefableUnref(this.#readable);
|
||||||
}
|
}
|
||||||
this.#pendingReadPromiseIds.forEach((id) => core.unrefOp(id));
|
}
|
||||||
|
return this.#readable;
|
||||||
|
}
|
||||||
|
|
||||||
|
get writable() {
|
||||||
|
if (this.#writable === undefined) {
|
||||||
|
this.#writable = writableStreamForRid(this.rid);
|
||||||
|
}
|
||||||
|
return this.#writable;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref() {
|
||||||
|
this.#unref = false;
|
||||||
|
if (this.#readable) {
|
||||||
|
readableStreamForRidUnrefableRef(this.#readable);
|
||||||
|
}
|
||||||
|
this.#pendingReadPromiseIds.forEach((id) => core.refOp(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
unref() {
|
||||||
|
this.#unref = true;
|
||||||
|
if (this.#readable) {
|
||||||
|
readableStreamForRidUnrefableUnref(this.#readable);
|
||||||
|
}
|
||||||
|
this.#pendingReadPromiseIds.forEach((id) => core.unrefOp(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TcpConn extends Conn {
|
||||||
|
setNoDelay(noDelay = true) {
|
||||||
|
return ops.op_set_nodelay(this.rid, noDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
setKeepAlive(keepAlive = true) {
|
||||||
|
return ops.op_set_keepalive(this.rid, keepAlive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UnixConn extends Conn {}
|
||||||
|
|
||||||
|
class Listener {
|
||||||
|
#rid = 0;
|
||||||
|
#addr = null;
|
||||||
|
#unref = false;
|
||||||
|
#promiseId = null;
|
||||||
|
|
||||||
|
constructor(rid, addr) {
|
||||||
|
this.#rid = rid;
|
||||||
|
this.#addr = addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
get rid() {
|
||||||
|
return this.#rid;
|
||||||
|
}
|
||||||
|
|
||||||
|
get addr() {
|
||||||
|
return this.#addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
async accept() {
|
||||||
|
let promise;
|
||||||
|
switch (this.addr.transport) {
|
||||||
|
case "tcp":
|
||||||
|
promise = core.opAsync("op_net_accept_tcp", this.rid);
|
||||||
|
break;
|
||||||
|
case "unix":
|
||||||
|
promise = core.opAsync("op_net_accept_unix", this.rid);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported transport: ${this.addr.transport}`);
|
||||||
|
}
|
||||||
|
this.#promiseId = promise[promiseIdSymbol];
|
||||||
|
if (this.#unref) core.unrefOp(this.#promiseId);
|
||||||
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await promise;
|
||||||
|
this.#promiseId = null;
|
||||||
|
if (this.addr.transport == "tcp") {
|
||||||
|
localAddr.transport = "tcp";
|
||||||
|
remoteAddr.transport = "tcp";
|
||||||
|
return new TcpConn(rid, remoteAddr, localAddr);
|
||||||
|
} else if (this.addr.transport == "unix") {
|
||||||
|
return new UnixConn(
|
||||||
|
rid,
|
||||||
|
{ transport: "unix", path: remoteAddr },
|
||||||
|
{ transport: "unix", path: localAddr },
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new Error("unreachable");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TcpConn extends Conn {
|
async next() {
|
||||||
setNoDelay(noDelay = true) {
|
let conn;
|
||||||
return ops.op_set_nodelay(this.rid, noDelay);
|
try {
|
||||||
|
conn = await this.accept();
|
||||||
|
} catch (error) {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) ||
|
||||||
|
ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error)
|
||||||
|
) {
|
||||||
|
return { value: undefined, done: true };
|
||||||
|
}
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
return { value: conn, done: false };
|
||||||
|
}
|
||||||
|
|
||||||
setKeepAlive(keepAlive = true) {
|
return(value) {
|
||||||
return ops.op_set_keepalive(this.rid, keepAlive);
|
this.close();
|
||||||
|
return PromiseResolve({ value, done: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
core.close(this.rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
[SymbolAsyncIterator]() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ref() {
|
||||||
|
this.#unref = false;
|
||||||
|
if (typeof this.#promiseId === "number") {
|
||||||
|
core.refOp(this.#promiseId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnixConn extends Conn {}
|
unref() {
|
||||||
|
this.#unref = true;
|
||||||
class Listener {
|
if (typeof this.#promiseId === "number") {
|
||||||
#rid = 0;
|
core.unrefOp(this.#promiseId);
|
||||||
#addr = null;
|
|
||||||
#unref = false;
|
|
||||||
#promiseId = null;
|
|
||||||
|
|
||||||
constructor(rid, addr) {
|
|
||||||
this.#rid = rid;
|
|
||||||
this.#addr = addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
get rid() {
|
|
||||||
return this.#rid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get addr() {
|
|
||||||
return this.#addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
async accept() {
|
|
||||||
let promise;
|
|
||||||
switch (this.addr.transport) {
|
|
||||||
case "tcp":
|
|
||||||
promise = core.opAsync("op_net_accept_tcp", this.rid);
|
|
||||||
break;
|
|
||||||
case "unix":
|
|
||||||
promise = core.opAsync("op_net_accept_unix", this.rid);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported transport: ${this.addr.transport}`);
|
|
||||||
}
|
|
||||||
this.#promiseId = promise[promiseIdSymbol];
|
|
||||||
if (this.#unref) core.unrefOp(this.#promiseId);
|
|
||||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await promise;
|
|
||||||
this.#promiseId = null;
|
|
||||||
if (this.addr.transport == "tcp") {
|
|
||||||
localAddr.transport = "tcp";
|
|
||||||
remoteAddr.transport = "tcp";
|
|
||||||
return new TcpConn(rid, remoteAddr, localAddr);
|
|
||||||
} else if (this.addr.transport == "unix") {
|
|
||||||
return new UnixConn(
|
|
||||||
rid,
|
|
||||||
{ transport: "unix", path: remoteAddr },
|
|
||||||
{ transport: "unix", path: localAddr },
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw new Error("unreachable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async next() {
|
|
||||||
let conn;
|
|
||||||
try {
|
|
||||||
conn = await this.accept();
|
|
||||||
} catch (error) {
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error) ||
|
|
||||||
ObjectPrototypeIsPrototypeOf(InterruptedPrototype, error)
|
|
||||||
) {
|
|
||||||
return { value: undefined, done: true };
|
|
||||||
}
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
return { value: conn, done: false };
|
|
||||||
}
|
|
||||||
|
|
||||||
return(value) {
|
|
||||||
this.close();
|
|
||||||
return PromiseResolve({ value, done: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
core.close(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SymbolAsyncIterator]() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ref() {
|
|
||||||
this.#unref = false;
|
|
||||||
if (typeof this.#promiseId === "number") {
|
|
||||||
core.refOp(this.#promiseId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unref() {
|
|
||||||
this.#unref = true;
|
|
||||||
if (typeof this.#promiseId === "number") {
|
|
||||||
core.unrefOp(this.#promiseId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Datagram {
|
class Datagram {
|
||||||
#rid = 0;
|
#rid = 0;
|
||||||
#addr = null;
|
#addr = null;
|
||||||
|
|
||||||
constructor(rid, addr, bufSize = 1024) {
|
constructor(rid, addr, bufSize = 1024) {
|
||||||
this.#rid = rid;
|
this.#rid = rid;
|
||||||
this.#addr = addr;
|
this.#addr = addr;
|
||||||
this.bufSize = bufSize;
|
this.bufSize = bufSize;
|
||||||
}
|
|
||||||
|
|
||||||
get rid() {
|
|
||||||
return this.#rid;
|
|
||||||
}
|
|
||||||
|
|
||||||
get addr() {
|
|
||||||
return this.#addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
async receive(p) {
|
|
||||||
const buf = p || new Uint8Array(this.bufSize);
|
|
||||||
let nread;
|
|
||||||
let remoteAddr;
|
|
||||||
switch (this.addr.transport) {
|
|
||||||
case "udp": {
|
|
||||||
({ 0: nread, 1: remoteAddr } = await core.opAsync(
|
|
||||||
"op_net_recv_udp",
|
|
||||||
this.rid,
|
|
||||||
buf,
|
|
||||||
));
|
|
||||||
remoteAddr.transport = "udp";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "unixpacket": {
|
|
||||||
let path;
|
|
||||||
({ 0: nread, 1: path } = await core.opAsync(
|
|
||||||
"op_net_recv_unixpacket",
|
|
||||||
this.rid,
|
|
||||||
buf,
|
|
||||||
));
|
|
||||||
remoteAddr = { transport: "unixpacket", path };
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported transport: ${this.addr.transport}`);
|
|
||||||
}
|
|
||||||
const sub = TypedArrayPrototypeSubarray(buf, 0, nread);
|
|
||||||
return [sub, remoteAddr];
|
|
||||||
}
|
|
||||||
|
|
||||||
async send(p, opts) {
|
|
||||||
switch (this.addr.transport) {
|
|
||||||
case "udp":
|
|
||||||
return await core.opAsync(
|
|
||||||
"op_net_send_udp",
|
|
||||||
this.rid,
|
|
||||||
{ hostname: opts.hostname ?? "127.0.0.1", port: opts.port },
|
|
||||||
p,
|
|
||||||
);
|
|
||||||
case "unixpacket":
|
|
||||||
return await core.opAsync(
|
|
||||||
"op_net_send_unixpacket",
|
|
||||||
this.rid,
|
|
||||||
opts.path,
|
|
||||||
p,
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
throw new Error(`Unsupported transport: ${this.addr.transport}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
core.close(this.rid);
|
|
||||||
}
|
|
||||||
|
|
||||||
async *[SymbolAsyncIterator]() {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
yield await this.receive();
|
|
||||||
} catch (err) {
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, err) ||
|
|
||||||
ObjectPrototypeIsPrototypeOf(InterruptedPrototype, err)
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function listen(args) {
|
get rid() {
|
||||||
switch (args.transport ?? "tcp") {
|
return this.#rid;
|
||||||
case "tcp": {
|
}
|
||||||
const { 0: rid, 1: addr } = ops.op_net_listen_tcp({
|
|
||||||
hostname: args.hostname ?? "0.0.0.0",
|
get addr() {
|
||||||
port: args.port,
|
return this.#addr;
|
||||||
}, args.reusePort);
|
}
|
||||||
addr.transport = "tcp";
|
|
||||||
return new Listener(rid, addr);
|
async receive(p) {
|
||||||
|
const buf = p || new Uint8Array(this.bufSize);
|
||||||
|
let nread;
|
||||||
|
let remoteAddr;
|
||||||
|
switch (this.addr.transport) {
|
||||||
|
case "udp": {
|
||||||
|
({ 0: nread, 1: remoteAddr } = await core.opAsync(
|
||||||
|
"op_net_recv_udp",
|
||||||
|
this.rid,
|
||||||
|
buf,
|
||||||
|
));
|
||||||
|
remoteAddr.transport = "udp";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case "unix": {
|
case "unixpacket": {
|
||||||
const { 0: rid, 1: path } = ops.op_net_listen_unix(args.path);
|
let path;
|
||||||
const addr = {
|
({ 0: nread, 1: path } = await core.opAsync(
|
||||||
transport: "unix",
|
"op_net_recv_unixpacket",
|
||||||
path,
|
this.rid,
|
||||||
};
|
buf,
|
||||||
return new Listener(rid, addr);
|
));
|
||||||
|
remoteAddr = { transport: "unixpacket", path };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new TypeError(`Unsupported transport: '${transport}'`);
|
throw new Error(`Unsupported transport: ${this.addr.transport}`);
|
||||||
|
}
|
||||||
|
const sub = TypedArrayPrototypeSubarray(buf, 0, nread);
|
||||||
|
return [sub, remoteAddr];
|
||||||
|
}
|
||||||
|
|
||||||
|
async send(p, opts) {
|
||||||
|
switch (this.addr.transport) {
|
||||||
|
case "udp":
|
||||||
|
return await core.opAsync(
|
||||||
|
"op_net_send_udp",
|
||||||
|
this.rid,
|
||||||
|
{ hostname: opts.hostname ?? "127.0.0.1", port: opts.port },
|
||||||
|
p,
|
||||||
|
);
|
||||||
|
case "unixpacket":
|
||||||
|
return await core.opAsync(
|
||||||
|
"op_net_send_unixpacket",
|
||||||
|
this.rid,
|
||||||
|
opts.path,
|
||||||
|
p,
|
||||||
|
);
|
||||||
|
default:
|
||||||
|
throw new Error(`Unsupported transport: ${this.addr.transport}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createListenDatagram(udpOpFn, unixOpFn) {
|
close() {
|
||||||
return function listenDatagram(args) {
|
core.close(this.rid);
|
||||||
switch (args.transport) {
|
|
||||||
case "udp": {
|
|
||||||
const { 0: rid, 1: addr } = udpOpFn(
|
|
||||||
{
|
|
||||||
hostname: args.hostname ?? "127.0.0.1",
|
|
||||||
port: args.port,
|
|
||||||
},
|
|
||||||
args.reuseAddress ?? false,
|
|
||||||
);
|
|
||||||
addr.transport = "udp";
|
|
||||||
return new Datagram(rid, addr);
|
|
||||||
}
|
|
||||||
case "unixpacket": {
|
|
||||||
const { 0: rid, 1: path } = unixOpFn(args.path);
|
|
||||||
const addr = {
|
|
||||||
transport: "unixpacket",
|
|
||||||
path,
|
|
||||||
};
|
|
||||||
return new Datagram(rid, addr);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new TypeError(`Unsupported transport: '${transport}'`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function connect(args) {
|
async *[SymbolAsyncIterator]() {
|
||||||
switch (args.transport ?? "tcp") {
|
while (true) {
|
||||||
case "tcp": {
|
try {
|
||||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
yield await this.receive();
|
||||||
"op_net_connect_tcp",
|
} catch (err) {
|
||||||
|
if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(BadResourcePrototype, err) ||
|
||||||
|
ObjectPrototypeIsPrototypeOf(InterruptedPrototype, err)
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function listen(args) {
|
||||||
|
switch (args.transport ?? "tcp") {
|
||||||
|
case "tcp": {
|
||||||
|
const { 0: rid, 1: addr } = ops.op_net_listen_tcp({
|
||||||
|
hostname: args.hostname ?? "0.0.0.0",
|
||||||
|
port: args.port,
|
||||||
|
}, args.reusePort);
|
||||||
|
addr.transport = "tcp";
|
||||||
|
return new Listener(rid, addr);
|
||||||
|
}
|
||||||
|
case "unix": {
|
||||||
|
const { 0: rid, 1: path } = ops.op_net_listen_unix(args.path);
|
||||||
|
const addr = {
|
||||||
|
transport: "unix",
|
||||||
|
path,
|
||||||
|
};
|
||||||
|
return new Listener(rid, addr);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createListenDatagram(udpOpFn, unixOpFn) {
|
||||||
|
return function listenDatagram(args) {
|
||||||
|
switch (args.transport) {
|
||||||
|
case "udp": {
|
||||||
|
const { 0: rid, 1: addr } = udpOpFn(
|
||||||
{
|
{
|
||||||
hostname: args.hostname ?? "127.0.0.1",
|
hostname: args.hostname ?? "127.0.0.1",
|
||||||
port: args.port,
|
port: args.port,
|
||||||
},
|
},
|
||||||
|
args.reuseAddress ?? false,
|
||||||
);
|
);
|
||||||
localAddr.transport = "tcp";
|
addr.transport = "udp";
|
||||||
remoteAddr.transport = "tcp";
|
return new Datagram(rid, addr);
|
||||||
return new TcpConn(rid, remoteAddr, localAddr);
|
|
||||||
}
|
}
|
||||||
case "unix": {
|
case "unixpacket": {
|
||||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
const { 0: rid, 1: path } = unixOpFn(args.path);
|
||||||
"op_net_connect_unix",
|
const addr = {
|
||||||
args.path,
|
transport: "unixpacket",
|
||||||
);
|
path,
|
||||||
return new UnixConn(
|
};
|
||||||
rid,
|
return new Datagram(rid, addr);
|
||||||
{ transport: "unix", path: remoteAddr },
|
|
||||||
{ transport: "unix", path: localAddr },
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new TypeError(`Unsupported transport: '${transport}'`);
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.net = {
|
|
||||||
connect,
|
|
||||||
Conn,
|
|
||||||
TcpConn,
|
|
||||||
UnixConn,
|
|
||||||
listen,
|
|
||||||
createListenDatagram,
|
|
||||||
Listener,
|
|
||||||
shutdown,
|
|
||||||
Datagram,
|
|
||||||
resolveDns,
|
|
||||||
};
|
};
|
||||||
})(this);
|
}
|
||||||
|
|
||||||
|
async function connect(args) {
|
||||||
|
switch (args.transport ?? "tcp") {
|
||||||
|
case "tcp": {
|
||||||
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
||||||
|
"op_net_connect_tcp",
|
||||||
|
{
|
||||||
|
hostname: args.hostname ?? "127.0.0.1",
|
||||||
|
port: args.port,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
localAddr.transport = "tcp";
|
||||||
|
remoteAddr.transport = "tcp";
|
||||||
|
return new TcpConn(rid, remoteAddr, localAddr);
|
||||||
|
}
|
||||||
|
case "unix": {
|
||||||
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
||||||
|
"op_net_connect_unix",
|
||||||
|
args.path,
|
||||||
|
);
|
||||||
|
return new UnixConn(
|
||||||
|
rid,
|
||||||
|
{ transport: "unix", path: remoteAddr },
|
||||||
|
{ transport: "unix", path: localAddr },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
Conn,
|
||||||
|
connect,
|
||||||
|
createListenDatagram,
|
||||||
|
Datagram,
|
||||||
|
listen,
|
||||||
|
Listener,
|
||||||
|
resolveDns,
|
||||||
|
shutdown,
|
||||||
|
TcpConn,
|
||||||
|
UnixConn,
|
||||||
|
};
|
||||||
|
|
|
@ -1,106 +1,98 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
import { Conn, Listener } from "internal:ext/net/01_net.js";
|
||||||
const { Listener, Conn } = window.__bootstrap.net;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const { TypeError } = window.__bootstrap.primordials;
|
const { TypeError } = primordials;
|
||||||
|
|
||||||
function opStartTls(args) {
|
function opStartTls(args) {
|
||||||
return core.opAsync("op_tls_start", args);
|
return core.opAsync("op_tls_start", args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function opTlsHandshake(rid) {
|
||||||
|
return core.opAsync("op_tls_handshake", rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
class TlsConn extends Conn {
|
||||||
|
handshake() {
|
||||||
|
return opTlsHandshake(this.rid);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function opTlsHandshake(rid) {
|
async function connectTls({
|
||||||
return core.opAsync("op_tls_handshake", rid);
|
port,
|
||||||
|
hostname = "127.0.0.1",
|
||||||
|
transport = "tcp",
|
||||||
|
certFile = undefined,
|
||||||
|
caCerts = [],
|
||||||
|
certChain = undefined,
|
||||||
|
privateKey = undefined,
|
||||||
|
alpnProtocols = undefined,
|
||||||
|
}) {
|
||||||
|
if (transport !== "tcp") {
|
||||||
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
||||||
}
|
}
|
||||||
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
||||||
|
"op_net_connect_tls",
|
||||||
|
{ hostname, port },
|
||||||
|
{ certFile, caCerts, certChain, privateKey, alpnProtocols },
|
||||||
|
);
|
||||||
|
localAddr.transport = "tcp";
|
||||||
|
remoteAddr.transport = "tcp";
|
||||||
|
return new TlsConn(rid, remoteAddr, localAddr);
|
||||||
|
}
|
||||||
|
|
||||||
class TlsConn extends Conn {
|
class TlsListener extends Listener {
|
||||||
handshake() {
|
async accept() {
|
||||||
return opTlsHandshake(this.rid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function connectTls({
|
|
||||||
port,
|
|
||||||
hostname = "127.0.0.1",
|
|
||||||
transport = "tcp",
|
|
||||||
certFile = undefined,
|
|
||||||
caCerts = [],
|
|
||||||
certChain = undefined,
|
|
||||||
privateKey = undefined,
|
|
||||||
alpnProtocols = undefined,
|
|
||||||
}) {
|
|
||||||
if (transport !== "tcp") {
|
|
||||||
throw new TypeError(`Unsupported transport: '${transport}'`);
|
|
||||||
}
|
|
||||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
||||||
"op_net_connect_tls",
|
"op_net_accept_tls",
|
||||||
{ hostname, port },
|
this.rid,
|
||||||
{ certFile, caCerts, certChain, privateKey, alpnProtocols },
|
|
||||||
);
|
);
|
||||||
localAddr.transport = "tcp";
|
localAddr.transport = "tcp";
|
||||||
remoteAddr.transport = "tcp";
|
remoteAddr.transport = "tcp";
|
||||||
return new TlsConn(rid, remoteAddr, localAddr);
|
return new TlsConn(rid, remoteAddr, localAddr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TlsListener extends Listener {
|
function listenTls({
|
||||||
async accept() {
|
port,
|
||||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
cert,
|
||||||
"op_net_accept_tls",
|
certFile,
|
||||||
this.rid,
|
key,
|
||||||
);
|
keyFile,
|
||||||
localAddr.transport = "tcp";
|
hostname = "0.0.0.0",
|
||||||
remoteAddr.transport = "tcp";
|
transport = "tcp",
|
||||||
return new TlsConn(rid, remoteAddr, localAddr);
|
alpnProtocols = undefined,
|
||||||
}
|
reusePort = false,
|
||||||
|
}) {
|
||||||
|
if (transport !== "tcp") {
|
||||||
|
throw new TypeError(`Unsupported transport: '${transport}'`);
|
||||||
}
|
}
|
||||||
|
const { 0: rid, 1: localAddr } = ops.op_net_listen_tls(
|
||||||
|
{ hostname, port },
|
||||||
|
{ cert, certFile, key, keyFile, alpnProtocols, reusePort },
|
||||||
|
);
|
||||||
|
return new TlsListener(rid, localAddr);
|
||||||
|
}
|
||||||
|
|
||||||
function listenTls({
|
async function startTls(
|
||||||
port,
|
conn,
|
||||||
cert,
|
{
|
||||||
certFile,
|
hostname = "127.0.0.1",
|
||||||
key,
|
certFile = undefined,
|
||||||
keyFile,
|
caCerts = [],
|
||||||
hostname = "0.0.0.0",
|
|
||||||
transport = "tcp",
|
|
||||||
alpnProtocols = undefined,
|
alpnProtocols = undefined,
|
||||||
reusePort = false,
|
} = {},
|
||||||
}) {
|
) {
|
||||||
if (transport !== "tcp") {
|
const { 0: rid, 1: localAddr, 2: remoteAddr } = await opStartTls({
|
||||||
throw new TypeError(`Unsupported transport: '${transport}'`);
|
rid: conn.rid,
|
||||||
}
|
hostname,
|
||||||
const { 0: rid, 1: localAddr } = ops.op_net_listen_tls(
|
certFile,
|
||||||
{ hostname, port },
|
caCerts,
|
||||||
{ cert, certFile, key, keyFile, alpnProtocols, reusePort },
|
alpnProtocols,
|
||||||
);
|
});
|
||||||
return new TlsListener(rid, localAddr);
|
return new TlsConn(rid, remoteAddr, localAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function startTls(
|
export { connectTls, listenTls, startTls, TlsConn, TlsListener };
|
||||||
conn,
|
|
||||||
{
|
|
||||||
hostname = "127.0.0.1",
|
|
||||||
certFile = undefined,
|
|
||||||
caCerts = [],
|
|
||||||
alpnProtocols = undefined,
|
|
||||||
} = {},
|
|
||||||
) {
|
|
||||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await opStartTls({
|
|
||||||
rid: conn.rid,
|
|
||||||
hostname,
|
|
||||||
certFile,
|
|
||||||
caCerts,
|
|
||||||
alpnProtocols,
|
|
||||||
});
|
|
||||||
return new TlsConn(rid, remoteAddr, localAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.tls = {
|
|
||||||
startTls,
|
|
||||||
listenTls,
|
|
||||||
connectTls,
|
|
||||||
TlsConn,
|
|
||||||
TlsListener,
|
|
||||||
};
|
|
||||||
})(this);
|
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub fn init<P: NetPermissions + 'static>(
|
||||||
ops.extend(ops_tls::init::<P>());
|
ops.extend(ops_tls::init::<P>());
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_web"])
|
.dependencies(vec!["deno_web"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/net",
|
prefix "internal:ext/net",
|
||||||
"01_net.js",
|
"01_net.js",
|
||||||
"02_tls.js",
|
"02_tls.js",
|
||||||
|
|
|
@ -2,127 +2,122 @@
|
||||||
|
|
||||||
// deno-lint-ignore-file
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
"use strict";
|
const internals = globalThis.__bootstrap.internals;
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypePush,
|
||||||
|
ArrayPrototypeFilter,
|
||||||
|
ObjectEntries,
|
||||||
|
ObjectCreate,
|
||||||
|
ObjectDefineProperty,
|
||||||
|
Proxy,
|
||||||
|
ReflectDefineProperty,
|
||||||
|
ReflectGetOwnPropertyDescriptor,
|
||||||
|
ReflectOwnKeys,
|
||||||
|
Set,
|
||||||
|
SetPrototypeHas,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
function assert(cond) {
|
||||||
const {
|
if (!cond) {
|
||||||
ArrayPrototypePush,
|
throw Error("assert");
|
||||||
ArrayPrototypeFilter,
|
|
||||||
ObjectEntries,
|
|
||||||
ObjectCreate,
|
|
||||||
ObjectDefineProperty,
|
|
||||||
Proxy,
|
|
||||||
ReflectDefineProperty,
|
|
||||||
ReflectGetOwnPropertyDescriptor,
|
|
||||||
ReflectOwnKeys,
|
|
||||||
Set,
|
|
||||||
SetPrototypeHas,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
function assert(cond) {
|
|
||||||
if (!cond) {
|
|
||||||
throw Error("assert");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let initialized = false;
|
let initialized = false;
|
||||||
const nodeGlobals = {};
|
const nodeGlobals = {};
|
||||||
const nodeGlobalThis = new Proxy(globalThis, {
|
const nodeGlobalThis = new Proxy(globalThis, {
|
||||||
get(_target, prop, _receiver) {
|
get(_target, prop, _receiver) {
|
||||||
if (prop in nodeGlobals) {
|
if (prop in nodeGlobals) {
|
||||||
return nodeGlobals[prop];
|
return nodeGlobals[prop];
|
||||||
} else {
|
} else {
|
||||||
return globalThis[prop];
|
return globalThis[prop];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
set(_target, prop, value) {
|
set(_target, prop, value) {
|
||||||
if (prop in nodeGlobals) {
|
if (prop in nodeGlobals) {
|
||||||
nodeGlobals[prop] = value;
|
nodeGlobals[prop] = value;
|
||||||
} else {
|
} else {
|
||||||
globalThis[prop] = value;
|
globalThis[prop] = value;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
deleteProperty(_target, prop) {
|
deleteProperty(_target, prop) {
|
||||||
let success = false;
|
let success = false;
|
||||||
if (prop in nodeGlobals) {
|
if (prop in nodeGlobals) {
|
||||||
delete nodeGlobals[prop];
|
delete nodeGlobals[prop];
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
if (prop in globalThis) {
|
if (prop in globalThis) {
|
||||||
delete globalThis[prop];
|
delete globalThis[prop];
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
},
|
},
|
||||||
ownKeys(_target) {
|
ownKeys(_target) {
|
||||||
const globalThisKeys = ReflectOwnKeys(globalThis);
|
const globalThisKeys = ReflectOwnKeys(globalThis);
|
||||||
const nodeGlobalsKeys = ReflectOwnKeys(nodeGlobals);
|
const nodeGlobalsKeys = ReflectOwnKeys(nodeGlobals);
|
||||||
const nodeGlobalsKeySet = new Set(nodeGlobalsKeys);
|
const nodeGlobalsKeySet = new Set(nodeGlobalsKeys);
|
||||||
return [
|
return [
|
||||||
...ArrayPrototypeFilter(
|
...ArrayPrototypeFilter(
|
||||||
globalThisKeys,
|
globalThisKeys,
|
||||||
(k) => !SetPrototypeHas(nodeGlobalsKeySet, k),
|
(k) => !SetPrototypeHas(nodeGlobalsKeySet, k),
|
||||||
),
|
),
|
||||||
...nodeGlobalsKeys,
|
...nodeGlobalsKeys,
|
||||||
];
|
];
|
||||||
},
|
},
|
||||||
defineProperty(_target, prop, desc) {
|
defineProperty(_target, prop, desc) {
|
||||||
if (prop in nodeGlobals) {
|
if (prop in nodeGlobals) {
|
||||||
return ReflectDefineProperty(nodeGlobals, prop, desc);
|
return ReflectDefineProperty(nodeGlobals, prop, desc);
|
||||||
} else {
|
} else {
|
||||||
return ReflectDefineProperty(globalThis, prop, desc);
|
return ReflectDefineProperty(globalThis, prop, desc);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getOwnPropertyDescriptor(_target, prop) {
|
getOwnPropertyDescriptor(_target, prop) {
|
||||||
if (prop in nodeGlobals) {
|
if (prop in nodeGlobals) {
|
||||||
return ReflectGetOwnPropertyDescriptor(nodeGlobals, prop);
|
return ReflectGetOwnPropertyDescriptor(nodeGlobals, prop);
|
||||||
} else {
|
} else {
|
||||||
return ReflectGetOwnPropertyDescriptor(globalThis, prop);
|
return ReflectGetOwnPropertyDescriptor(globalThis, prop);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
has(_target, prop) {
|
has(_target, prop) {
|
||||||
return prop in nodeGlobals || prop in globalThis;
|
return prop in nodeGlobals || prop in globalThis;
|
||||||
},
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const nativeModuleExports = ObjectCreate(null);
|
||||||
|
const builtinModules = [];
|
||||||
|
|
||||||
|
function initialize(nodeModules, nodeGlobalThisName) {
|
||||||
|
assert(!initialized);
|
||||||
|
initialized = true;
|
||||||
|
for (const [name, exports] of ObjectEntries(nodeModules)) {
|
||||||
|
nativeModuleExports[name] = exports;
|
||||||
|
ArrayPrototypePush(builtinModules, name);
|
||||||
|
}
|
||||||
|
nodeGlobals.Buffer = nativeModuleExports["buffer"].Buffer;
|
||||||
|
nodeGlobals.clearImmediate = nativeModuleExports["timers"].clearImmediate;
|
||||||
|
nodeGlobals.clearInterval = nativeModuleExports["timers"].clearInterval;
|
||||||
|
nodeGlobals.clearTimeout = nativeModuleExports["timers"].clearTimeout;
|
||||||
|
nodeGlobals.console = nativeModuleExports["console"];
|
||||||
|
nodeGlobals.global = nodeGlobalThis;
|
||||||
|
nodeGlobals.process = nativeModuleExports["process"];
|
||||||
|
nodeGlobals.setImmediate = nativeModuleExports["timers"].setImmediate;
|
||||||
|
nodeGlobals.setInterval = nativeModuleExports["timers"].setInterval;
|
||||||
|
nodeGlobals.setTimeout = nativeModuleExports["timers"].setTimeout;
|
||||||
|
|
||||||
|
// add a hidden global for the esm code to use in order to reliably
|
||||||
|
// get node's globalThis
|
||||||
|
ObjectDefineProperty(globalThis, nodeGlobalThisName, {
|
||||||
|
enumerable: false,
|
||||||
|
writable: false,
|
||||||
|
value: nodeGlobalThis,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const nativeModuleExports = ObjectCreate(null);
|
internals.node = {
|
||||||
const builtinModules = [];
|
globalThis: nodeGlobalThis,
|
||||||
|
initialize,
|
||||||
function initialize(nodeModules, nodeGlobalThisName) {
|
nativeModuleExports,
|
||||||
assert(!initialized);
|
builtinModules,
|
||||||
initialized = true;
|
};
|
||||||
for (const [name, exports] of ObjectEntries(nodeModules)) {
|
|
||||||
nativeModuleExports[name] = exports;
|
|
||||||
ArrayPrototypePush(builtinModules, name);
|
|
||||||
}
|
|
||||||
nodeGlobals.Buffer = nativeModuleExports["buffer"].Buffer;
|
|
||||||
nodeGlobals.clearImmediate = nativeModuleExports["timers"].clearImmediate;
|
|
||||||
nodeGlobals.clearInterval = nativeModuleExports["timers"].clearInterval;
|
|
||||||
nodeGlobals.clearTimeout = nativeModuleExports["timers"].clearTimeout;
|
|
||||||
nodeGlobals.console = nativeModuleExports["console"];
|
|
||||||
nodeGlobals.global = nodeGlobalThis;
|
|
||||||
nodeGlobals.process = nativeModuleExports["process"];
|
|
||||||
nodeGlobals.setImmediate = nativeModuleExports["timers"].setImmediate;
|
|
||||||
nodeGlobals.setInterval = nativeModuleExports["timers"].setInterval;
|
|
||||||
nodeGlobals.setTimeout = nativeModuleExports["timers"].setTimeout;
|
|
||||||
|
|
||||||
// add a hidden global for the esm code to use in order to reliably
|
|
||||||
// get node's globalThis
|
|
||||||
ObjectDefineProperty(globalThis, nodeGlobalThisName, {
|
|
||||||
enumerable: false,
|
|
||||||
writable: false,
|
|
||||||
value: nodeGlobalThis,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.internals = {
|
|
||||||
...window.__bootstrap.internals ?? {},
|
|
||||||
node: {
|
|
||||||
globalThis: nodeGlobalThis,
|
|
||||||
initialize,
|
|
||||||
nativeModuleExports,
|
|
||||||
builtinModules,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
})(globalThis);
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -85,7 +85,7 @@ pub fn init<P: NodePermissions + 'static>(
|
||||||
maybe_npm_resolver: Option<Rc<dyn RequireNpmResolver>>,
|
maybe_npm_resolver: Option<Rc<dyn RequireNpmResolver>>,
|
||||||
) -> Extension {
|
) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/node",
|
prefix "internal:ext/node",
|
||||||
"01_node.js",
|
"01_node.js",
|
||||||
"02_require.js",
|
"02_require.js",
|
||||||
|
|
1475
ext/url/00_url.js
1475
ext/url/00_url.js
File diff suppressed because it is too large
Load diff
|
@ -7,268 +7,263 @@
|
||||||
/// <reference path="./internal.d.ts" />
|
/// <reference path="./internal.d.ts" />
|
||||||
/// <reference path="./lib.deno_url.d.ts" />
|
/// <reference path="./lib.deno_url.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeMap,
|
||||||
|
ObjectKeys,
|
||||||
|
ObjectFromEntries,
|
||||||
|
RegExp,
|
||||||
|
RegExpPrototypeExec,
|
||||||
|
RegExpPrototypeTest,
|
||||||
|
Symbol,
|
||||||
|
SymbolFor,
|
||||||
|
TypeError,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
const _components = Symbol("components");
|
||||||
const core = window.Deno.core;
|
|
||||||
const ops = core.ops;
|
|
||||||
const webidl = window.__bootstrap.webidl;
|
|
||||||
const {
|
|
||||||
ArrayPrototypeMap,
|
|
||||||
ObjectKeys,
|
|
||||||
ObjectFromEntries,
|
|
||||||
RegExp,
|
|
||||||
RegExpPrototypeExec,
|
|
||||||
RegExpPrototypeTest,
|
|
||||||
Symbol,
|
|
||||||
SymbolFor,
|
|
||||||
TypeError,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
const _components = Symbol("components");
|
/**
|
||||||
|
* @typedef Components
|
||||||
|
* @property {Component} protocol
|
||||||
|
* @property {Component} username
|
||||||
|
* @property {Component} password
|
||||||
|
* @property {Component} hostname
|
||||||
|
* @property {Component} port
|
||||||
|
* @property {Component} pathname
|
||||||
|
* @property {Component} search
|
||||||
|
* @property {Component} hash
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef Component
|
||||||
|
* @property {string} patternString
|
||||||
|
* @property {RegExp} regexp
|
||||||
|
* @property {string[]} groupNameList
|
||||||
|
*/
|
||||||
|
|
||||||
|
class URLPattern {
|
||||||
|
/** @type {Components} */
|
||||||
|
[_components];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef Components
|
* @param {URLPatternInput} input
|
||||||
* @property {Component} protocol
|
* @param {string} [baseURL]
|
||||||
* @property {Component} username
|
|
||||||
* @property {Component} password
|
|
||||||
* @property {Component} hostname
|
|
||||||
* @property {Component} port
|
|
||||||
* @property {Component} pathname
|
|
||||||
* @property {Component} search
|
|
||||||
* @property {Component} hash
|
|
||||||
*/
|
*/
|
||||||
|
constructor(input, baseURL = undefined) {
|
||||||
/**
|
this[webidl.brand] = webidl.brand;
|
||||||
* @typedef Component
|
const prefix = "Failed to construct 'URLPattern'";
|
||||||
* @property {string} patternString
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
* @property {RegExp} regexp
|
input = webidl.converters.URLPatternInput(input, {
|
||||||
* @property {string[]} groupNameList
|
prefix,
|
||||||
*/
|
context: "Argument 1",
|
||||||
|
});
|
||||||
class URLPattern {
|
if (baseURL !== undefined) {
|
||||||
/** @type {Components} */
|
baseURL = webidl.converters.USVString(baseURL, {
|
||||||
[_components];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {URLPatternInput} input
|
|
||||||
* @param {string} [baseURL]
|
|
||||||
*/
|
|
||||||
constructor(input, baseURL = undefined) {
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
const prefix = "Failed to construct 'URLPattern'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
input = webidl.converters.URLPatternInput(input, {
|
|
||||||
prefix,
|
prefix,
|
||||||
context: "Argument 1",
|
context: "Argument 2",
|
||||||
});
|
});
|
||||||
if (baseURL !== undefined) {
|
|
||||||
baseURL = webidl.converters.USVString(baseURL, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const components = ops.op_urlpattern_parse(input, baseURL);
|
|
||||||
|
|
||||||
const keys = ObjectKeys(components);
|
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
|
||||||
const key = keys[i];
|
|
||||||
try {
|
|
||||||
components[key].regexp = new RegExp(
|
|
||||||
components[key].regexpString,
|
|
||||||
"u",
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
throw new TypeError(`${prefix}: ${key} is invalid; ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this[_components] = components;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get protocol() {
|
const components = ops.op_urlpattern_parse(input, baseURL);
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
return this[_components].protocol.patternString;
|
|
||||||
}
|
|
||||||
|
|
||||||
get username() {
|
const keys = ObjectKeys(components);
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
return this[_components].username.patternString;
|
const key = keys[i];
|
||||||
}
|
try {
|
||||||
|
components[key].regexp = new RegExp(
|
||||||
get password() {
|
components[key].regexpString,
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
"u",
|
||||||
return this[_components].password.patternString;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hostname() {
|
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
return this[_components].hostname.patternString;
|
|
||||||
}
|
|
||||||
|
|
||||||
get port() {
|
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
return this[_components].port.patternString;
|
|
||||||
}
|
|
||||||
|
|
||||||
get pathname() {
|
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
return this[_components].pathname.patternString;
|
|
||||||
}
|
|
||||||
|
|
||||||
get search() {
|
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
return this[_components].search.patternString;
|
|
||||||
}
|
|
||||||
|
|
||||||
get hash() {
|
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
return this[_components].hash.patternString;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {URLPatternInput} input
|
|
||||||
* @param {string} [baseURL]
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
test(input, baseURL = undefined) {
|
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
const prefix = "Failed to execute 'test' on 'URLPattern'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
input = webidl.converters.URLPatternInput(input, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
if (baseURL !== undefined) {
|
|
||||||
baseURL = webidl.converters.USVString(baseURL, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = ops.op_urlpattern_process_match_input(
|
|
||||||
input,
|
|
||||||
baseURL,
|
|
||||||
);
|
|
||||||
if (res === null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const values = res[0];
|
|
||||||
|
|
||||||
const keys = ObjectKeys(values);
|
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
|
||||||
const key = keys[i];
|
|
||||||
if (!RegExpPrototypeTest(this[_components][key].regexp, values[key])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {URLPatternInput} input
|
|
||||||
* @param {string} [baseURL]
|
|
||||||
* @returns {URLPatternResult | null}
|
|
||||||
*/
|
|
||||||
exec(input, baseURL = undefined) {
|
|
||||||
webidl.assertBranded(this, URLPatternPrototype);
|
|
||||||
const prefix = "Failed to execute 'exec' on 'URLPattern'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
input = webidl.converters.URLPatternInput(input, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
if (baseURL !== undefined) {
|
|
||||||
baseURL = webidl.converters.USVString(baseURL, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = ops.op_urlpattern_process_match_input(
|
|
||||||
input,
|
|
||||||
baseURL,
|
|
||||||
);
|
|
||||||
if (res === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { 0: values, 1: inputs } = res;
|
|
||||||
if (inputs[1] === null) {
|
|
||||||
inputs.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {URLPatternResult} */
|
|
||||||
const result = { inputs };
|
|
||||||
|
|
||||||
const keys = ObjectKeys(values);
|
|
||||||
for (let i = 0; i < keys.length; ++i) {
|
|
||||||
const key = keys[i];
|
|
||||||
/** @type {Component} */
|
|
||||||
const component = this[_components][key];
|
|
||||||
const input = values[key];
|
|
||||||
const match = RegExpPrototypeExec(component.regexp, input);
|
|
||||||
if (match === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const groupEntries = ArrayPrototypeMap(
|
|
||||||
component.groupNameList,
|
|
||||||
(name, i) => [name, match[i + 1] ?? ""],
|
|
||||||
);
|
);
|
||||||
const groups = ObjectFromEntries(groupEntries);
|
} catch (e) {
|
||||||
result[key] = {
|
throw new TypeError(`${prefix}: ${key} is invalid; ${e.message}`);
|
||||||
input,
|
|
||||||
groups,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[SymbolFor("Deno.customInspect")](inspect) {
|
this[_components] = components;
|
||||||
return `URLPattern ${
|
|
||||||
inspect({
|
|
||||||
protocol: this.protocol,
|
|
||||||
username: this.username,
|
|
||||||
password: this.password,
|
|
||||||
hostname: this.hostname,
|
|
||||||
port: this.port,
|
|
||||||
pathname: this.pathname,
|
|
||||||
search: this.search,
|
|
||||||
hash: this.hash,
|
|
||||||
})
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(URLPattern);
|
get protocol() {
|
||||||
const URLPatternPrototype = URLPattern.prototype;
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
return this[_components].protocol.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
webidl.converters.URLPatternInit = webidl
|
get username() {
|
||||||
.createDictionaryConverter("URLPatternInit", [
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
{ key: "protocol", converter: webidl.converters.USVString },
|
return this[_components].username.patternString;
|
||||||
{ key: "username", converter: webidl.converters.USVString },
|
}
|
||||||
{ key: "password", converter: webidl.converters.USVString },
|
|
||||||
{ key: "hostname", converter: webidl.converters.USVString },
|
|
||||||
{ key: "port", converter: webidl.converters.USVString },
|
|
||||||
{ key: "pathname", converter: webidl.converters.USVString },
|
|
||||||
{ key: "search", converter: webidl.converters.USVString },
|
|
||||||
{ key: "hash", converter: webidl.converters.USVString },
|
|
||||||
{ key: "baseURL", converter: webidl.converters.USVString },
|
|
||||||
]);
|
|
||||||
|
|
||||||
webidl.converters["URLPatternInput"] = (V, opts) => {
|
get password() {
|
||||||
// Union for (URLPatternInit or USVString)
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
if (typeof V == "object") {
|
return this[_components].password.patternString;
|
||||||
return webidl.converters.URLPatternInit(V, opts);
|
}
|
||||||
|
|
||||||
|
get hostname() {
|
||||||
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
return this[_components].hostname.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get port() {
|
||||||
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
return this[_components].port.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get pathname() {
|
||||||
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
return this[_components].pathname.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get search() {
|
||||||
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
return this[_components].search.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
get hash() {
|
||||||
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
return this[_components].hash.patternString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {URLPatternInput} input
|
||||||
|
* @param {string} [baseURL]
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
test(input, baseURL = undefined) {
|
||||||
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
const prefix = "Failed to execute 'test' on 'URLPattern'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
input = webidl.converters.URLPatternInput(input, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
if (baseURL !== undefined) {
|
||||||
|
baseURL = webidl.converters.USVString(baseURL, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return webidl.converters.USVString(V, opts);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.__bootstrap.urlPattern = {
|
const res = ops.op_urlpattern_process_match_input(
|
||||||
URLPattern,
|
input,
|
||||||
};
|
baseURL,
|
||||||
})(globalThis);
|
);
|
||||||
|
if (res === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = res[0];
|
||||||
|
|
||||||
|
const keys = ObjectKeys(values);
|
||||||
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
|
const key = keys[i];
|
||||||
|
if (!RegExpPrototypeTest(this[_components][key].regexp, values[key])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {URLPatternInput} input
|
||||||
|
* @param {string} [baseURL]
|
||||||
|
* @returns {URLPatternResult | null}
|
||||||
|
*/
|
||||||
|
exec(input, baseURL = undefined) {
|
||||||
|
webidl.assertBranded(this, URLPatternPrototype);
|
||||||
|
const prefix = "Failed to execute 'exec' on 'URLPattern'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
input = webidl.converters.URLPatternInput(input, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
if (baseURL !== undefined) {
|
||||||
|
baseURL = webidl.converters.USVString(baseURL, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = ops.op_urlpattern_process_match_input(
|
||||||
|
input,
|
||||||
|
baseURL,
|
||||||
|
);
|
||||||
|
if (res === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { 0: values, 1: inputs } = res;
|
||||||
|
if (inputs[1] === null) {
|
||||||
|
inputs.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {URLPatternResult} */
|
||||||
|
const result = { inputs };
|
||||||
|
|
||||||
|
const keys = ObjectKeys(values);
|
||||||
|
for (let i = 0; i < keys.length; ++i) {
|
||||||
|
const key = keys[i];
|
||||||
|
/** @type {Component} */
|
||||||
|
const component = this[_components][key];
|
||||||
|
const input = values[key];
|
||||||
|
const match = RegExpPrototypeExec(component.regexp, input);
|
||||||
|
if (match === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const groupEntries = ArrayPrototypeMap(
|
||||||
|
component.groupNameList,
|
||||||
|
(name, i) => [name, match[i + 1] ?? ""],
|
||||||
|
);
|
||||||
|
const groups = ObjectFromEntries(groupEntries);
|
||||||
|
result[key] = {
|
||||||
|
input,
|
||||||
|
groups,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
[SymbolFor("Deno.customInspect")](inspect) {
|
||||||
|
return `URLPattern ${
|
||||||
|
inspect({
|
||||||
|
protocol: this.protocol,
|
||||||
|
username: this.username,
|
||||||
|
password: this.password,
|
||||||
|
hostname: this.hostname,
|
||||||
|
port: this.port,
|
||||||
|
pathname: this.pathname,
|
||||||
|
search: this.search,
|
||||||
|
hash: this.hash,
|
||||||
|
})
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(URLPattern);
|
||||||
|
const URLPatternPrototype = URLPattern.prototype;
|
||||||
|
|
||||||
|
webidl.converters.URLPatternInit = webidl
|
||||||
|
.createDictionaryConverter("URLPatternInit", [
|
||||||
|
{ key: "protocol", converter: webidl.converters.USVString },
|
||||||
|
{ key: "username", converter: webidl.converters.USVString },
|
||||||
|
{ key: "password", converter: webidl.converters.USVString },
|
||||||
|
{ key: "hostname", converter: webidl.converters.USVString },
|
||||||
|
{ key: "port", converter: webidl.converters.USVString },
|
||||||
|
{ key: "pathname", converter: webidl.converters.USVString },
|
||||||
|
{ key: "search", converter: webidl.converters.USVString },
|
||||||
|
{ key: "hash", converter: webidl.converters.USVString },
|
||||||
|
{ key: "baseURL", converter: webidl.converters.USVString },
|
||||||
|
]);
|
||||||
|
|
||||||
|
webidl.converters["URLPatternInput"] = (V, opts) => {
|
||||||
|
// Union for (URLPatternInit or USVString)
|
||||||
|
if (typeof V == "object") {
|
||||||
|
return webidl.converters.URLPatternInit(V, opts);
|
||||||
|
}
|
||||||
|
return webidl.converters.USVString(V, opts);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { URLPattern };
|
||||||
|
|
|
@ -12,9 +12,11 @@ fn setup() -> Vec<Extension> {
|
||||||
deno_webidl::init(),
|
deno_webidl::init(),
|
||||||
deno_url::init(),
|
deno_url::init(),
|
||||||
Extension::builder("bench_setup")
|
Extension::builder("bench_setup")
|
||||||
.js(vec![(
|
.esm(vec![(
|
||||||
"setup",
|
"internal:setup",
|
||||||
"const { URL } = globalThis.__bootstrap.url;",
|
r#"import { URL } from "internal:ext/url/00_url.js";
|
||||||
|
globalThis.URL = URL;
|
||||||
|
"#,
|
||||||
)])
|
)])
|
||||||
.build(),
|
.build(),
|
||||||
]
|
]
|
||||||
|
|
22
ext/url/internal.d.ts
vendored
22
ext/url/internal.d.ts
vendored
|
@ -1,20 +1,14 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
// deno-lint-ignore-file no-var
|
|
||||||
|
|
||||||
/// <reference no-default-lib="true" />
|
/// <reference no-default-lib="true" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
declare namespace globalThis {
|
declare module "internal:ext/url/00_url.js" {
|
||||||
declare namespace __bootstrap {
|
const URL: typeof URL;
|
||||||
declare var url: {
|
const URLSearchParams: typeof URLSearchParams;
|
||||||
URL: typeof URL;
|
function parseUrlEncoded(bytes: Uint8Array): [string, string][];
|
||||||
URLSearchParams: typeof URLSearchParams;
|
}
|
||||||
parseUrlEncoded(bytes: Uint8Array): [string, string][];
|
|
||||||
};
|
declare module "internal:ext/url/01_urlpattern.js" {
|
||||||
|
const URLPattern: typeof URLPattern;
|
||||||
declare var urlPattern: {
|
|
||||||
URLPattern: typeof URLPattern;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,7 @@ use crate::urlpattern::op_urlpattern_process_match_input;
|
||||||
pub fn init() -> Extension {
|
pub fn init() -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl"])
|
.dependencies(vec!["deno_webidl"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/url",
|
prefix "internal:ext/url",
|
||||||
"00_url.js",
|
"00_url.js",
|
||||||
"01_urlpattern.js",
|
"01_urlpattern.js",
|
||||||
|
|
|
@ -6,334 +6,331 @@
|
||||||
/// <reference path="../web/internal.d.ts" />
|
/// <reference path="../web/internal.d.ts" />
|
||||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeJoin,
|
||||||
|
ArrayPrototypeMap,
|
||||||
|
Error,
|
||||||
|
JSONStringify,
|
||||||
|
NumberPrototypeToString,
|
||||||
|
RegExp,
|
||||||
|
SafeArrayIterator,
|
||||||
|
String,
|
||||||
|
StringPrototypeCharAt,
|
||||||
|
StringPrototypeCharCodeAt,
|
||||||
|
StringPrototypeMatch,
|
||||||
|
StringPrototypePadStart,
|
||||||
|
StringPrototypeReplace,
|
||||||
|
StringPrototypeSlice,
|
||||||
|
StringPrototypeSubstring,
|
||||||
|
StringPrototypeToLowerCase,
|
||||||
|
StringPrototypeToUpperCase,
|
||||||
|
TypeError,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
const ASCII_DIGIT = ["\u0030-\u0039"];
|
||||||
const core = Deno.core;
|
const ASCII_UPPER_ALPHA = ["\u0041-\u005A"];
|
||||||
const ops = core.ops;
|
const ASCII_LOWER_ALPHA = ["\u0061-\u007A"];
|
||||||
const {
|
const ASCII_ALPHA = [
|
||||||
ArrayPrototypeJoin,
|
...new SafeArrayIterator(ASCII_UPPER_ALPHA),
|
||||||
ArrayPrototypeMap,
|
...new SafeArrayIterator(ASCII_LOWER_ALPHA),
|
||||||
Error,
|
];
|
||||||
JSONStringify,
|
const ASCII_ALPHANUMERIC = [
|
||||||
NumberPrototypeToString,
|
...new SafeArrayIterator(ASCII_DIGIT),
|
||||||
RegExp,
|
...new SafeArrayIterator(ASCII_ALPHA),
|
||||||
SafeArrayIterator,
|
];
|
||||||
String,
|
|
||||||
StringPrototypeCharAt,
|
|
||||||
StringPrototypeCharCodeAt,
|
|
||||||
StringPrototypeMatch,
|
|
||||||
StringPrototypePadStart,
|
|
||||||
StringPrototypeReplace,
|
|
||||||
StringPrototypeSlice,
|
|
||||||
StringPrototypeSubstring,
|
|
||||||
StringPrototypeToLowerCase,
|
|
||||||
StringPrototypeToUpperCase,
|
|
||||||
TypeError,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
const ASCII_DIGIT = ["\u0030-\u0039"];
|
const HTTP_TAB_OR_SPACE = ["\u0009", "\u0020"];
|
||||||
const ASCII_UPPER_ALPHA = ["\u0041-\u005A"];
|
const HTTP_WHITESPACE = [
|
||||||
const ASCII_LOWER_ALPHA = ["\u0061-\u007A"];
|
"\u000A",
|
||||||
const ASCII_ALPHA = [
|
"\u000D",
|
||||||
...new SafeArrayIterator(ASCII_UPPER_ALPHA),
|
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
||||||
...new SafeArrayIterator(ASCII_LOWER_ALPHA),
|
];
|
||||||
];
|
|
||||||
const ASCII_ALPHANUMERIC = [
|
|
||||||
...new SafeArrayIterator(ASCII_DIGIT),
|
|
||||||
...new SafeArrayIterator(ASCII_ALPHA),
|
|
||||||
];
|
|
||||||
|
|
||||||
const HTTP_TAB_OR_SPACE = ["\u0009", "\u0020"];
|
const HTTP_TOKEN_CODE_POINT = [
|
||||||
const HTTP_WHITESPACE = [
|
"\u0021",
|
||||||
"\u000A",
|
"\u0023",
|
||||||
"\u000D",
|
"\u0024",
|
||||||
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
"\u0025",
|
||||||
];
|
"\u0026",
|
||||||
|
"\u0027",
|
||||||
|
"\u002A",
|
||||||
|
"\u002B",
|
||||||
|
"\u002D",
|
||||||
|
"\u002E",
|
||||||
|
"\u005E",
|
||||||
|
"\u005F",
|
||||||
|
"\u0060",
|
||||||
|
"\u007C",
|
||||||
|
"\u007E",
|
||||||
|
...new SafeArrayIterator(ASCII_ALPHANUMERIC),
|
||||||
|
];
|
||||||
|
const HTTP_TOKEN_CODE_POINT_RE = new RegExp(
|
||||||
|
`^[${regexMatcher(HTTP_TOKEN_CODE_POINT)}]+$`,
|
||||||
|
);
|
||||||
|
const HTTP_QUOTED_STRING_TOKEN_POINT = [
|
||||||
|
"\u0009",
|
||||||
|
"\u0020-\u007E",
|
||||||
|
"\u0080-\u00FF",
|
||||||
|
];
|
||||||
|
const HTTP_QUOTED_STRING_TOKEN_POINT_RE = new RegExp(
|
||||||
|
`^[${regexMatcher(HTTP_QUOTED_STRING_TOKEN_POINT)}]+$`,
|
||||||
|
);
|
||||||
|
const HTTP_TAB_OR_SPACE_MATCHER = regexMatcher(HTTP_TAB_OR_SPACE);
|
||||||
|
const HTTP_TAB_OR_SPACE_PREFIX_RE = new RegExp(
|
||||||
|
`^[${HTTP_TAB_OR_SPACE_MATCHER}]+`,
|
||||||
|
"g",
|
||||||
|
);
|
||||||
|
const HTTP_TAB_OR_SPACE_SUFFIX_RE = new RegExp(
|
||||||
|
`[${HTTP_TAB_OR_SPACE_MATCHER}]+$`,
|
||||||
|
"g",
|
||||||
|
);
|
||||||
|
const HTTP_WHITESPACE_MATCHER = regexMatcher(HTTP_WHITESPACE);
|
||||||
|
const HTTP_BETWEEN_WHITESPACE = new RegExp(
|
||||||
|
`^[${HTTP_WHITESPACE_MATCHER}]*(.*?)[${HTTP_WHITESPACE_MATCHER}]*$`,
|
||||||
|
);
|
||||||
|
const HTTP_WHITESPACE_PREFIX_RE = new RegExp(
|
||||||
|
`^[${HTTP_WHITESPACE_MATCHER}]+`,
|
||||||
|
"g",
|
||||||
|
);
|
||||||
|
const HTTP_WHITESPACE_SUFFIX_RE = new RegExp(
|
||||||
|
`[${HTTP_WHITESPACE_MATCHER}]+$`,
|
||||||
|
"g",
|
||||||
|
);
|
||||||
|
|
||||||
const HTTP_TOKEN_CODE_POINT = [
|
/**
|
||||||
"\u0021",
|
* Turn a string of chars into a regex safe matcher.
|
||||||
"\u0023",
|
* @param {string[]} chars
|
||||||
"\u0024",
|
* @returns {string}
|
||||||
"\u0025",
|
*/
|
||||||
"\u0026",
|
function regexMatcher(chars) {
|
||||||
"\u0027",
|
const matchers = ArrayPrototypeMap(chars, (char) => {
|
||||||
"\u002A",
|
if (char.length === 1) {
|
||||||
"\u002B",
|
const a = StringPrototypePadStart(
|
||||||
"\u002D",
|
NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16),
|
||||||
"\u002E",
|
4,
|
||||||
"\u005E",
|
"0",
|
||||||
"\u005F",
|
|
||||||
"\u0060",
|
|
||||||
"\u007C",
|
|
||||||
"\u007E",
|
|
||||||
...new SafeArrayIterator(ASCII_ALPHANUMERIC),
|
|
||||||
];
|
|
||||||
const HTTP_TOKEN_CODE_POINT_RE = new RegExp(
|
|
||||||
`^[${regexMatcher(HTTP_TOKEN_CODE_POINT)}]+$`,
|
|
||||||
);
|
|
||||||
const HTTP_QUOTED_STRING_TOKEN_POINT = [
|
|
||||||
"\u0009",
|
|
||||||
"\u0020-\u007E",
|
|
||||||
"\u0080-\u00FF",
|
|
||||||
];
|
|
||||||
const HTTP_QUOTED_STRING_TOKEN_POINT_RE = new RegExp(
|
|
||||||
`^[${regexMatcher(HTTP_QUOTED_STRING_TOKEN_POINT)}]+$`,
|
|
||||||
);
|
|
||||||
const HTTP_TAB_OR_SPACE_MATCHER = regexMatcher(HTTP_TAB_OR_SPACE);
|
|
||||||
const HTTP_TAB_OR_SPACE_PREFIX_RE = new RegExp(
|
|
||||||
`^[${HTTP_TAB_OR_SPACE_MATCHER}]+`,
|
|
||||||
"g",
|
|
||||||
);
|
|
||||||
const HTTP_TAB_OR_SPACE_SUFFIX_RE = new RegExp(
|
|
||||||
`[${HTTP_TAB_OR_SPACE_MATCHER}]+$`,
|
|
||||||
"g",
|
|
||||||
);
|
|
||||||
const HTTP_WHITESPACE_MATCHER = regexMatcher(HTTP_WHITESPACE);
|
|
||||||
const HTTP_BETWEEN_WHITESPACE = new RegExp(
|
|
||||||
`^[${HTTP_WHITESPACE_MATCHER}]*(.*?)[${HTTP_WHITESPACE_MATCHER}]*$`,
|
|
||||||
);
|
|
||||||
const HTTP_WHITESPACE_PREFIX_RE = new RegExp(
|
|
||||||
`^[${HTTP_WHITESPACE_MATCHER}]+`,
|
|
||||||
"g",
|
|
||||||
);
|
|
||||||
const HTTP_WHITESPACE_SUFFIX_RE = new RegExp(
|
|
||||||
`[${HTTP_WHITESPACE_MATCHER}]+$`,
|
|
||||||
"g",
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turn a string of chars into a regex safe matcher.
|
|
||||||
* @param {string[]} chars
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function regexMatcher(chars) {
|
|
||||||
const matchers = ArrayPrototypeMap(chars, (char) => {
|
|
||||||
if (char.length === 1) {
|
|
||||||
const a = StringPrototypePadStart(
|
|
||||||
NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16),
|
|
||||||
4,
|
|
||||||
"0",
|
|
||||||
);
|
|
||||||
return `\\u${a}`;
|
|
||||||
} else if (char.length === 3 && char[1] === "-") {
|
|
||||||
const a = StringPrototypePadStart(
|
|
||||||
NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16),
|
|
||||||
4,
|
|
||||||
"0",
|
|
||||||
);
|
|
||||||
const b = StringPrototypePadStart(
|
|
||||||
NumberPrototypeToString(StringPrototypeCharCodeAt(char, 2), 16),
|
|
||||||
4,
|
|
||||||
"0",
|
|
||||||
);
|
|
||||||
return `\\u${a}-\\u${b}`;
|
|
||||||
} else {
|
|
||||||
throw TypeError("unreachable");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return ArrayPrototypeJoin(matchers, "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
|
|
||||||
* @param {string} input
|
|
||||||
* @param {number} position
|
|
||||||
* @param {(char: string) => boolean} condition
|
|
||||||
* @returns {{result: string, position: number}}
|
|
||||||
*/
|
|
||||||
function collectSequenceOfCodepoints(input, position, condition) {
|
|
||||||
const start = position;
|
|
||||||
for (
|
|
||||||
let c = StringPrototypeCharAt(input, position);
|
|
||||||
position < input.length && condition(c);
|
|
||||||
c = StringPrototypeCharAt(input, ++position)
|
|
||||||
);
|
|
||||||
return { result: StringPrototypeSlice(input, start, position), position };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} s
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function byteUpperCase(s) {
|
|
||||||
return StringPrototypeReplace(
|
|
||||||
String(s),
|
|
||||||
/[a-z]/g,
|
|
||||||
function byteUpperCaseReplace(c) {
|
|
||||||
return StringPrototypeToUpperCase(c);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} s
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function byteLowerCase(s) {
|
|
||||||
// NOTE: correct since all callers convert to ByteString first
|
|
||||||
// TODO(@AaronO): maybe prefer a ByteString_Lower webidl converter
|
|
||||||
return StringPrototypeToLowerCase(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://fetch.spec.whatwg.org/#collect-an-http-quoted-string
|
|
||||||
* @param {string} input
|
|
||||||
* @param {number} position
|
|
||||||
* @param {boolean} extractValue
|
|
||||||
* @returns {{result: string, position: number}}
|
|
||||||
*/
|
|
||||||
function collectHttpQuotedString(input, position, extractValue) {
|
|
||||||
// 1.
|
|
||||||
const positionStart = position;
|
|
||||||
// 2.
|
|
||||||
let value = "";
|
|
||||||
// 3.
|
|
||||||
if (input[position] !== "\u0022") throw new TypeError('must be "');
|
|
||||||
// 4.
|
|
||||||
position++;
|
|
||||||
// 5.
|
|
||||||
while (true) {
|
|
||||||
// 5.1.
|
|
||||||
const res = collectSequenceOfCodepoints(
|
|
||||||
input,
|
|
||||||
position,
|
|
||||||
(c) => c !== "\u0022" && c !== "\u005C",
|
|
||||||
);
|
);
|
||||||
value += res.result;
|
return `\\u${a}`;
|
||||||
position = res.position;
|
} else if (char.length === 3 && char[1] === "-") {
|
||||||
// 5.2.
|
const a = StringPrototypePadStart(
|
||||||
if (position >= input.length) break;
|
NumberPrototypeToString(StringPrototypeCharCodeAt(char, 0), 16),
|
||||||
// 5.3.
|
4,
|
||||||
const quoteOrBackslash = input[position];
|
"0",
|
||||||
// 5.4.
|
);
|
||||||
position++;
|
const b = StringPrototypePadStart(
|
||||||
// 5.5.
|
NumberPrototypeToString(StringPrototypeCharCodeAt(char, 2), 16),
|
||||||
if (quoteOrBackslash === "\u005C") {
|
4,
|
||||||
// 5.5.1.
|
"0",
|
||||||
if (position >= input.length) {
|
);
|
||||||
value += "\u005C";
|
return `\\u${a}-\\u${b}`;
|
||||||
break;
|
} else {
|
||||||
}
|
throw TypeError("unreachable");
|
||||||
// 5.5.2.
|
}
|
||||||
value += input[position];
|
});
|
||||||
// 5.5.3.
|
return ArrayPrototypeJoin(matchers, "");
|
||||||
position++;
|
}
|
||||||
} else { // 5.6.
|
|
||||||
// 5.6.1
|
/**
|
||||||
if (quoteOrBackslash !== "\u0022") throw new TypeError('must be "');
|
* https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
|
||||||
// 5.6.2
|
* @param {string} input
|
||||||
|
* @param {number} position
|
||||||
|
* @param {(char: string) => boolean} condition
|
||||||
|
* @returns {{result: string, position: number}}
|
||||||
|
*/
|
||||||
|
function collectSequenceOfCodepoints(input, position, condition) {
|
||||||
|
const start = position;
|
||||||
|
for (
|
||||||
|
let c = StringPrototypeCharAt(input, position);
|
||||||
|
position < input.length && condition(c);
|
||||||
|
c = StringPrototypeCharAt(input, ++position)
|
||||||
|
);
|
||||||
|
return { result: StringPrototypeSlice(input, start, position), position };
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function byteUpperCase(s) {
|
||||||
|
return StringPrototypeReplace(
|
||||||
|
String(s),
|
||||||
|
/[a-z]/g,
|
||||||
|
function byteUpperCaseReplace(c) {
|
||||||
|
return StringPrototypeToUpperCase(c);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function byteLowerCase(s) {
|
||||||
|
// NOTE: correct since all callers convert to ByteString first
|
||||||
|
// TODO(@AaronO): maybe prefer a ByteString_Lower webidl converter
|
||||||
|
return StringPrototypeToLowerCase(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* https://fetch.spec.whatwg.org/#collect-an-http-quoted-string
|
||||||
|
* @param {string} input
|
||||||
|
* @param {number} position
|
||||||
|
* @param {boolean} extractValue
|
||||||
|
* @returns {{result: string, position: number}}
|
||||||
|
*/
|
||||||
|
function collectHttpQuotedString(input, position, extractValue) {
|
||||||
|
// 1.
|
||||||
|
const positionStart = position;
|
||||||
|
// 2.
|
||||||
|
let value = "";
|
||||||
|
// 3.
|
||||||
|
if (input[position] !== "\u0022") throw new TypeError('must be "');
|
||||||
|
// 4.
|
||||||
|
position++;
|
||||||
|
// 5.
|
||||||
|
while (true) {
|
||||||
|
// 5.1.
|
||||||
|
const res = collectSequenceOfCodepoints(
|
||||||
|
input,
|
||||||
|
position,
|
||||||
|
(c) => c !== "\u0022" && c !== "\u005C",
|
||||||
|
);
|
||||||
|
value += res.result;
|
||||||
|
position = res.position;
|
||||||
|
// 5.2.
|
||||||
|
if (position >= input.length) break;
|
||||||
|
// 5.3.
|
||||||
|
const quoteOrBackslash = input[position];
|
||||||
|
// 5.4.
|
||||||
|
position++;
|
||||||
|
// 5.5.
|
||||||
|
if (quoteOrBackslash === "\u005C") {
|
||||||
|
// 5.5.1.
|
||||||
|
if (position >= input.length) {
|
||||||
|
value += "\u005C";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
// 5.5.2.
|
||||||
// 6.
|
value += input[position];
|
||||||
if (extractValue) return { result: value, position };
|
// 5.5.3.
|
||||||
// 7.
|
position++;
|
||||||
return {
|
} else { // 5.6.
|
||||||
result: StringPrototypeSubstring(input, positionStart, position + 1),
|
// 5.6.1
|
||||||
position,
|
if (quoteOrBackslash !== "\u0022") throw new TypeError('must be "');
|
||||||
};
|
// 5.6.2
|
||||||
}
|
break;
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Uint8Array} data
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function forgivingBase64Encode(data) {
|
|
||||||
return ops.op_base64_encode(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} data
|
|
||||||
* @returns {Uint8Array}
|
|
||||||
*/
|
|
||||||
function forgivingBase64Decode(data) {
|
|
||||||
return ops.op_base64_decode(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} char
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
function isHttpWhitespace(char) {
|
|
||||||
switch (char) {
|
|
||||||
case "\u0009":
|
|
||||||
case "\u000A":
|
|
||||||
case "\u000D":
|
|
||||||
case "\u0020":
|
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 6.
|
||||||
/**
|
if (extractValue) return { result: value, position };
|
||||||
* @param {string} s
|
// 7.
|
||||||
* @returns {string}
|
return {
|
||||||
*/
|
result: StringPrototypeSubstring(input, positionStart, position + 1),
|
||||||
function httpTrim(s) {
|
position,
|
||||||
if (!isHttpWhitespace(s[0]) && !isHttpWhitespace(s[s.length - 1])) {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
return StringPrototypeMatch(s, HTTP_BETWEEN_WHITESPACE)?.[1] ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
class AssertionError extends Error {
|
|
||||||
constructor(msg) {
|
|
||||||
super(msg);
|
|
||||||
this.name = "AssertionError";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {unknown} cond
|
|
||||||
* @param {string=} msg
|
|
||||||
* @returns {asserts cond}
|
|
||||||
*/
|
|
||||||
function assert(cond, msg = "Assertion failed.") {
|
|
||||||
if (!cond) {
|
|
||||||
throw new AssertionError(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {unknown} value
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function serializeJSValueToJSONString(value) {
|
|
||||||
const result = JSONStringify(value);
|
|
||||||
if (result === undefined) {
|
|
||||||
throw new TypeError("Value is not JSON serializable.");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.infra = {
|
|
||||||
collectSequenceOfCodepoints,
|
|
||||||
ASCII_DIGIT,
|
|
||||||
ASCII_UPPER_ALPHA,
|
|
||||||
ASCII_LOWER_ALPHA,
|
|
||||||
ASCII_ALPHA,
|
|
||||||
ASCII_ALPHANUMERIC,
|
|
||||||
HTTP_TAB_OR_SPACE,
|
|
||||||
HTTP_WHITESPACE,
|
|
||||||
HTTP_TOKEN_CODE_POINT,
|
|
||||||
HTTP_TOKEN_CODE_POINT_RE,
|
|
||||||
HTTP_QUOTED_STRING_TOKEN_POINT,
|
|
||||||
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
|
|
||||||
HTTP_TAB_OR_SPACE_PREFIX_RE,
|
|
||||||
HTTP_TAB_OR_SPACE_SUFFIX_RE,
|
|
||||||
HTTP_WHITESPACE_PREFIX_RE,
|
|
||||||
HTTP_WHITESPACE_SUFFIX_RE,
|
|
||||||
httpTrim,
|
|
||||||
regexMatcher,
|
|
||||||
byteUpperCase,
|
|
||||||
byteLowerCase,
|
|
||||||
collectHttpQuotedString,
|
|
||||||
forgivingBase64Encode,
|
|
||||||
forgivingBase64Decode,
|
|
||||||
AssertionError,
|
|
||||||
assert,
|
|
||||||
serializeJSValueToJSONString,
|
|
||||||
};
|
};
|
||||||
})(globalThis);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} data
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function forgivingBase64Encode(data) {
|
||||||
|
return ops.op_base64_encode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} data
|
||||||
|
* @returns {Uint8Array}
|
||||||
|
*/
|
||||||
|
function forgivingBase64Decode(data) {
|
||||||
|
return ops.op_base64_decode(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} char
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isHttpWhitespace(char) {
|
||||||
|
switch (char) {
|
||||||
|
case "\u0009":
|
||||||
|
case "\u000A":
|
||||||
|
case "\u000D":
|
||||||
|
case "\u0020":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} s
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function httpTrim(s) {
|
||||||
|
if (!isHttpWhitespace(s[0]) && !isHttpWhitespace(s[s.length - 1])) {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return StringPrototypeMatch(s, HTTP_BETWEEN_WHITESPACE)?.[1] ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
class AssertionError extends Error {
|
||||||
|
constructor(msg) {
|
||||||
|
super(msg);
|
||||||
|
this.name = "AssertionError";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {unknown} cond
|
||||||
|
* @param {string=} msg
|
||||||
|
* @returns {asserts cond}
|
||||||
|
*/
|
||||||
|
function assert(cond, msg = "Assertion failed.") {
|
||||||
|
if (!cond) {
|
||||||
|
throw new AssertionError(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {unknown} value
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function serializeJSValueToJSONString(value) {
|
||||||
|
const result = JSONStringify(value);
|
||||||
|
if (result === undefined) {
|
||||||
|
throw new TypeError("Value is not JSON serializable.");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
ASCII_ALPHA,
|
||||||
|
ASCII_ALPHANUMERIC,
|
||||||
|
ASCII_DIGIT,
|
||||||
|
ASCII_LOWER_ALPHA,
|
||||||
|
ASCII_UPPER_ALPHA,
|
||||||
|
assert,
|
||||||
|
AssertionError,
|
||||||
|
byteLowerCase,
|
||||||
|
byteUpperCase,
|
||||||
|
collectHttpQuotedString,
|
||||||
|
collectSequenceOfCodepoints,
|
||||||
|
forgivingBase64Decode,
|
||||||
|
forgivingBase64Encode,
|
||||||
|
HTTP_QUOTED_STRING_TOKEN_POINT,
|
||||||
|
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
|
||||||
|
HTTP_TAB_OR_SPACE,
|
||||||
|
HTTP_TAB_OR_SPACE_PREFIX_RE,
|
||||||
|
HTTP_TAB_OR_SPACE_SUFFIX_RE,
|
||||||
|
HTTP_TOKEN_CODE_POINT,
|
||||||
|
HTTP_TOKEN_CODE_POINT_RE,
|
||||||
|
HTTP_WHITESPACE,
|
||||||
|
HTTP_WHITESPACE_PREFIX_RE,
|
||||||
|
HTTP_WHITESPACE_SUFFIX_RE,
|
||||||
|
httpTrim,
|
||||||
|
regexMatcher,
|
||||||
|
serializeJSValueToJSONString,
|
||||||
|
};
|
||||||
|
|
|
@ -7,197 +7,194 @@
|
||||||
/// <reference path="../web/internal.d.ts" />
|
/// <reference path="../web/internal.d.ts" />
|
||||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeSlice,
|
||||||
|
Error,
|
||||||
|
ErrorPrototype,
|
||||||
|
ObjectDefineProperty,
|
||||||
|
ObjectCreate,
|
||||||
|
ObjectEntries,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
ObjectSetPrototypeOf,
|
||||||
|
Symbol,
|
||||||
|
SymbolFor,
|
||||||
|
} = primordials;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import { createFilteredInspectProxy } from "internal:ext/console/02_console.js";
|
||||||
|
|
||||||
((window) => {
|
const _name = Symbol("name");
|
||||||
const {
|
const _message = Symbol("message");
|
||||||
ArrayPrototypeSlice,
|
const _code = Symbol("code");
|
||||||
Error,
|
|
||||||
ErrorPrototype,
|
|
||||||
ObjectDefineProperty,
|
|
||||||
ObjectCreate,
|
|
||||||
ObjectEntries,
|
|
||||||
ObjectPrototypeIsPrototypeOf,
|
|
||||||
ObjectSetPrototypeOf,
|
|
||||||
Symbol,
|
|
||||||
SymbolFor,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
const webidl = window.__bootstrap.webidl;
|
|
||||||
const consoleInternal = window.__bootstrap.console;
|
|
||||||
|
|
||||||
const _name = Symbol("name");
|
// Defined in WebIDL 4.3.
|
||||||
const _message = Symbol("message");
|
// https://webidl.spec.whatwg.org/#idl-DOMException
|
||||||
const _code = Symbol("code");
|
const INDEX_SIZE_ERR = 1;
|
||||||
|
const DOMSTRING_SIZE_ERR = 2;
|
||||||
|
const HIERARCHY_REQUEST_ERR = 3;
|
||||||
|
const WRONG_DOCUMENT_ERR = 4;
|
||||||
|
const INVALID_CHARACTER_ERR = 5;
|
||||||
|
const NO_DATA_ALLOWED_ERR = 6;
|
||||||
|
const NO_MODIFICATION_ALLOWED_ERR = 7;
|
||||||
|
const NOT_FOUND_ERR = 8;
|
||||||
|
const NOT_SUPPORTED_ERR = 9;
|
||||||
|
const INUSE_ATTRIBUTE_ERR = 10;
|
||||||
|
const INVALID_STATE_ERR = 11;
|
||||||
|
const SYNTAX_ERR = 12;
|
||||||
|
const INVALID_MODIFICATION_ERR = 13;
|
||||||
|
const NAMESPACE_ERR = 14;
|
||||||
|
const INVALID_ACCESS_ERR = 15;
|
||||||
|
const VALIDATION_ERR = 16;
|
||||||
|
const TYPE_MISMATCH_ERR = 17;
|
||||||
|
const SECURITY_ERR = 18;
|
||||||
|
const NETWORK_ERR = 19;
|
||||||
|
const ABORT_ERR = 20;
|
||||||
|
const URL_MISMATCH_ERR = 21;
|
||||||
|
const QUOTA_EXCEEDED_ERR = 22;
|
||||||
|
const TIMEOUT_ERR = 23;
|
||||||
|
const INVALID_NODE_TYPE_ERR = 24;
|
||||||
|
const DATA_CLONE_ERR = 25;
|
||||||
|
|
||||||
// Defined in WebIDL 4.3.
|
// Defined in WebIDL 2.8.1.
|
||||||
// https://webidl.spec.whatwg.org/#idl-DOMException
|
// https://webidl.spec.whatwg.org/#dfn-error-names-table
|
||||||
const INDEX_SIZE_ERR = 1;
|
/** @type {Record<string, number>} */
|
||||||
const DOMSTRING_SIZE_ERR = 2;
|
// the prototype should be null, to prevent user code from looking
|
||||||
const HIERARCHY_REQUEST_ERR = 3;
|
// up Object.prototype properties, such as "toString"
|
||||||
const WRONG_DOCUMENT_ERR = 4;
|
const nameToCodeMapping = ObjectCreate(null, {
|
||||||
const INVALID_CHARACTER_ERR = 5;
|
IndexSizeError: { value: INDEX_SIZE_ERR },
|
||||||
const NO_DATA_ALLOWED_ERR = 6;
|
HierarchyRequestError: { value: HIERARCHY_REQUEST_ERR },
|
||||||
const NO_MODIFICATION_ALLOWED_ERR = 7;
|
WrongDocumentError: { value: WRONG_DOCUMENT_ERR },
|
||||||
const NOT_FOUND_ERR = 8;
|
InvalidCharacterError: { value: INVALID_CHARACTER_ERR },
|
||||||
const NOT_SUPPORTED_ERR = 9;
|
NoModificationAllowedError: { value: NO_MODIFICATION_ALLOWED_ERR },
|
||||||
const INUSE_ATTRIBUTE_ERR = 10;
|
NotFoundError: { value: NOT_FOUND_ERR },
|
||||||
const INVALID_STATE_ERR = 11;
|
NotSupportedError: { value: NOT_SUPPORTED_ERR },
|
||||||
const SYNTAX_ERR = 12;
|
InUseAttributeError: { value: INUSE_ATTRIBUTE_ERR },
|
||||||
const INVALID_MODIFICATION_ERR = 13;
|
InvalidStateError: { value: INVALID_STATE_ERR },
|
||||||
const NAMESPACE_ERR = 14;
|
SyntaxError: { value: SYNTAX_ERR },
|
||||||
const INVALID_ACCESS_ERR = 15;
|
InvalidModificationError: { value: INVALID_MODIFICATION_ERR },
|
||||||
const VALIDATION_ERR = 16;
|
NamespaceError: { value: NAMESPACE_ERR },
|
||||||
const TYPE_MISMATCH_ERR = 17;
|
InvalidAccessError: { value: INVALID_ACCESS_ERR },
|
||||||
const SECURITY_ERR = 18;
|
TypeMismatchError: { value: TYPE_MISMATCH_ERR },
|
||||||
const NETWORK_ERR = 19;
|
SecurityError: { value: SECURITY_ERR },
|
||||||
const ABORT_ERR = 20;
|
NetworkError: { value: NETWORK_ERR },
|
||||||
const URL_MISMATCH_ERR = 21;
|
AbortError: { value: ABORT_ERR },
|
||||||
const QUOTA_EXCEEDED_ERR = 22;
|
URLMismatchError: { value: URL_MISMATCH_ERR },
|
||||||
const TIMEOUT_ERR = 23;
|
QuotaExceededError: { value: QUOTA_EXCEEDED_ERR },
|
||||||
const INVALID_NODE_TYPE_ERR = 24;
|
TimeoutError: { value: TIMEOUT_ERR },
|
||||||
const DATA_CLONE_ERR = 25;
|
InvalidNodeTypeError: { value: INVALID_NODE_TYPE_ERR },
|
||||||
|
DataCloneError: { value: DATA_CLONE_ERR },
|
||||||
|
});
|
||||||
|
|
||||||
// Defined in WebIDL 2.8.1.
|
// Defined in WebIDL 4.3.
|
||||||
// https://webidl.spec.whatwg.org/#dfn-error-names-table
|
// https://webidl.spec.whatwg.org/#idl-DOMException
|
||||||
/** @type {Record<string, number>} */
|
class DOMException {
|
||||||
// the prototype should be null, to prevent user code from looking
|
[_message];
|
||||||
// up Object.prototype properties, such as "toString"
|
[_name];
|
||||||
const nameToCodeMapping = ObjectCreate(null, {
|
[_code];
|
||||||
IndexSizeError: { value: INDEX_SIZE_ERR },
|
|
||||||
HierarchyRequestError: { value: HIERARCHY_REQUEST_ERR },
|
|
||||||
WrongDocumentError: { value: WRONG_DOCUMENT_ERR },
|
|
||||||
InvalidCharacterError: { value: INVALID_CHARACTER_ERR },
|
|
||||||
NoModificationAllowedError: { value: NO_MODIFICATION_ALLOWED_ERR },
|
|
||||||
NotFoundError: { value: NOT_FOUND_ERR },
|
|
||||||
NotSupportedError: { value: NOT_SUPPORTED_ERR },
|
|
||||||
InUseAttributeError: { value: INUSE_ATTRIBUTE_ERR },
|
|
||||||
InvalidStateError: { value: INVALID_STATE_ERR },
|
|
||||||
SyntaxError: { value: SYNTAX_ERR },
|
|
||||||
InvalidModificationError: { value: INVALID_MODIFICATION_ERR },
|
|
||||||
NamespaceError: { value: NAMESPACE_ERR },
|
|
||||||
InvalidAccessError: { value: INVALID_ACCESS_ERR },
|
|
||||||
TypeMismatchError: { value: TYPE_MISMATCH_ERR },
|
|
||||||
SecurityError: { value: SECURITY_ERR },
|
|
||||||
NetworkError: { value: NETWORK_ERR },
|
|
||||||
AbortError: { value: ABORT_ERR },
|
|
||||||
URLMismatchError: { value: URL_MISMATCH_ERR },
|
|
||||||
QuotaExceededError: { value: QUOTA_EXCEEDED_ERR },
|
|
||||||
TimeoutError: { value: TIMEOUT_ERR },
|
|
||||||
InvalidNodeTypeError: { value: INVALID_NODE_TYPE_ERR },
|
|
||||||
DataCloneError: { value: DATA_CLONE_ERR },
|
|
||||||
});
|
|
||||||
|
|
||||||
// Defined in WebIDL 4.3.
|
// https://webidl.spec.whatwg.org/#dom-domexception-domexception
|
||||||
// https://webidl.spec.whatwg.org/#idl-DOMException
|
constructor(message = "", name = "Error") {
|
||||||
class DOMException {
|
message = webidl.converters.DOMString(message, {
|
||||||
[_message];
|
prefix: "Failed to construct 'DOMException'",
|
||||||
[_name];
|
context: "Argument 1",
|
||||||
[_code];
|
});
|
||||||
|
name = webidl.converters.DOMString(name, {
|
||||||
|
prefix: "Failed to construct 'DOMException'",
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
const code = nameToCodeMapping[name] ?? 0;
|
||||||
|
|
||||||
// https://webidl.spec.whatwg.org/#dom-domexception-domexception
|
this[_message] = message;
|
||||||
constructor(message = "", name = "Error") {
|
this[_name] = name;
|
||||||
message = webidl.converters.DOMString(message, {
|
this[_code] = code;
|
||||||
prefix: "Failed to construct 'DOMException'",
|
this[webidl.brand] = webidl.brand;
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
name = webidl.converters.DOMString(name, {
|
|
||||||
prefix: "Failed to construct 'DOMException'",
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
const code = nameToCodeMapping[name] ?? 0;
|
|
||||||
|
|
||||||
this[_message] = message;
|
const error = new Error(message);
|
||||||
this[_name] = name;
|
error.name = "DOMException";
|
||||||
this[_code] = code;
|
ObjectDefineProperty(this, "stack", {
|
||||||
this[webidl.brand] = webidl.brand;
|
value: error.stack,
|
||||||
|
writable: true,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
|
||||||
const error = new Error(message);
|
// `DOMException` isn't a native error, so `Error.prepareStackTrace()` is
|
||||||
error.name = "DOMException";
|
// not called when accessing `.stack`, meaning our structured stack trace
|
||||||
ObjectDefineProperty(this, "stack", {
|
// hack doesn't apply. This patches it in.
|
||||||
value: error.stack,
|
ObjectDefineProperty(this, "__callSiteEvals", {
|
||||||
writable: true,
|
value: ArrayPrototypeSlice(error.__callSiteEvals, 1),
|
||||||
configurable: true,
|
configurable: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
// `DOMException` isn't a native error, so `Error.prepareStackTrace()` is
|
|
||||||
// not called when accessing `.stack`, meaning our structured stack trace
|
|
||||||
// hack doesn't apply. This patches it in.
|
|
||||||
ObjectDefineProperty(this, "__callSiteEvals", {
|
|
||||||
value: ArrayPrototypeSlice(error.__callSiteEvals, 1),
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
get message() {
|
|
||||||
webidl.assertBranded(this, DOMExceptionPrototype);
|
|
||||||
return this[_message];
|
|
||||||
}
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
webidl.assertBranded(this, DOMExceptionPrototype);
|
|
||||||
return this[_name];
|
|
||||||
}
|
|
||||||
|
|
||||||
get code() {
|
|
||||||
webidl.assertBranded(this, DOMExceptionPrototype);
|
|
||||||
return this[_code];
|
|
||||||
}
|
|
||||||
|
|
||||||
[SymbolFor("Deno.customInspect")](inspect) {
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(DOMExceptionPrototype, this)) {
|
|
||||||
return `DOMException: ${this[_message]}`;
|
|
||||||
} else {
|
|
||||||
return inspect(consoleInternal.createFilteredInspectProxy({
|
|
||||||
object: this,
|
|
||||||
evaluate: false,
|
|
||||||
keys: [
|
|
||||||
"message",
|
|
||||||
"name",
|
|
||||||
"code",
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype);
|
get message() {
|
||||||
|
webidl.assertBranded(this, DOMExceptionPrototype);
|
||||||
webidl.configurePrototype(DOMException);
|
return this[_message];
|
||||||
const DOMExceptionPrototype = DOMException.prototype;
|
|
||||||
|
|
||||||
const entries = ObjectEntries({
|
|
||||||
INDEX_SIZE_ERR,
|
|
||||||
DOMSTRING_SIZE_ERR,
|
|
||||||
HIERARCHY_REQUEST_ERR,
|
|
||||||
WRONG_DOCUMENT_ERR,
|
|
||||||
INVALID_CHARACTER_ERR,
|
|
||||||
NO_DATA_ALLOWED_ERR,
|
|
||||||
NO_MODIFICATION_ALLOWED_ERR,
|
|
||||||
NOT_FOUND_ERR,
|
|
||||||
NOT_SUPPORTED_ERR,
|
|
||||||
INUSE_ATTRIBUTE_ERR,
|
|
||||||
INVALID_STATE_ERR,
|
|
||||||
SYNTAX_ERR,
|
|
||||||
INVALID_MODIFICATION_ERR,
|
|
||||||
NAMESPACE_ERR,
|
|
||||||
INVALID_ACCESS_ERR,
|
|
||||||
VALIDATION_ERR,
|
|
||||||
TYPE_MISMATCH_ERR,
|
|
||||||
SECURITY_ERR,
|
|
||||||
NETWORK_ERR,
|
|
||||||
ABORT_ERR,
|
|
||||||
URL_MISMATCH_ERR,
|
|
||||||
QUOTA_EXCEEDED_ERR,
|
|
||||||
TIMEOUT_ERR,
|
|
||||||
INVALID_NODE_TYPE_ERR,
|
|
||||||
DATA_CLONE_ERR,
|
|
||||||
});
|
|
||||||
for (let i = 0; i < entries.length; ++i) {
|
|
||||||
const { 0: key, 1: value } = entries[i];
|
|
||||||
const desc = { value, enumerable: true };
|
|
||||||
ObjectDefineProperty(DOMException, key, desc);
|
|
||||||
ObjectDefineProperty(DOMException.prototype, key, desc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.domException = { DOMException };
|
get name() {
|
||||||
})(this);
|
webidl.assertBranded(this, DOMExceptionPrototype);
|
||||||
|
return this[_name];
|
||||||
|
}
|
||||||
|
|
||||||
|
get code() {
|
||||||
|
webidl.assertBranded(this, DOMExceptionPrototype);
|
||||||
|
return this[_code];
|
||||||
|
}
|
||||||
|
|
||||||
|
[SymbolFor("Deno.customInspect")](inspect) {
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(DOMExceptionPrototype, this)) {
|
||||||
|
return `DOMException: ${this[_message]}`;
|
||||||
|
} else {
|
||||||
|
return inspect(createFilteredInspectProxy({
|
||||||
|
object: this,
|
||||||
|
evaluate: false,
|
||||||
|
keys: [
|
||||||
|
"message",
|
||||||
|
"name",
|
||||||
|
"code",
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectSetPrototypeOf(DOMException.prototype, ErrorPrototype);
|
||||||
|
|
||||||
|
webidl.configurePrototype(DOMException);
|
||||||
|
const DOMExceptionPrototype = DOMException.prototype;
|
||||||
|
|
||||||
|
const entries = ObjectEntries({
|
||||||
|
INDEX_SIZE_ERR,
|
||||||
|
DOMSTRING_SIZE_ERR,
|
||||||
|
HIERARCHY_REQUEST_ERR,
|
||||||
|
WRONG_DOCUMENT_ERR,
|
||||||
|
INVALID_CHARACTER_ERR,
|
||||||
|
NO_DATA_ALLOWED_ERR,
|
||||||
|
NO_MODIFICATION_ALLOWED_ERR,
|
||||||
|
NOT_FOUND_ERR,
|
||||||
|
NOT_SUPPORTED_ERR,
|
||||||
|
INUSE_ATTRIBUTE_ERR,
|
||||||
|
INVALID_STATE_ERR,
|
||||||
|
SYNTAX_ERR,
|
||||||
|
INVALID_MODIFICATION_ERR,
|
||||||
|
NAMESPACE_ERR,
|
||||||
|
INVALID_ACCESS_ERR,
|
||||||
|
VALIDATION_ERR,
|
||||||
|
TYPE_MISMATCH_ERR,
|
||||||
|
SECURITY_ERR,
|
||||||
|
NETWORK_ERR,
|
||||||
|
ABORT_ERR,
|
||||||
|
URL_MISMATCH_ERR,
|
||||||
|
QUOTA_EXCEEDED_ERR,
|
||||||
|
TIMEOUT_ERR,
|
||||||
|
INVALID_NODE_TYPE_ERR,
|
||||||
|
DATA_CLONE_ERR,
|
||||||
|
});
|
||||||
|
for (let i = 0; i < entries.length; ++i) {
|
||||||
|
const { 0: key, 1: value } = entries[i];
|
||||||
|
const desc = { value, enumerable: true };
|
||||||
|
ObjectDefineProperty(DOMException, key, desc);
|
||||||
|
ObjectDefineProperty(DOMException.prototype, key, desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DOMException;
|
||||||
|
|
|
@ -6,255 +6,247 @@
|
||||||
/// <reference path="../web/internal.d.ts" />
|
/// <reference path="../web/internal.d.ts" />
|
||||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeIncludes,
|
||||||
|
Map,
|
||||||
|
MapPrototypeGet,
|
||||||
|
MapPrototypeHas,
|
||||||
|
MapPrototypeSet,
|
||||||
|
RegExpPrototypeTest,
|
||||||
|
SafeMapIterator,
|
||||||
|
StringPrototypeReplaceAll,
|
||||||
|
StringPrototypeToLowerCase,
|
||||||
|
} = primordials;
|
||||||
|
import {
|
||||||
|
collectHttpQuotedString,
|
||||||
|
collectSequenceOfCodepoints,
|
||||||
|
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
|
||||||
|
HTTP_TOKEN_CODE_POINT_RE,
|
||||||
|
HTTP_WHITESPACE,
|
||||||
|
HTTP_WHITESPACE_PREFIX_RE,
|
||||||
|
HTTP_WHITESPACE_SUFFIX_RE,
|
||||||
|
} from "internal:ext/web/00_infra.js";
|
||||||
|
|
||||||
((window) => {
|
/**
|
||||||
const {
|
* @typedef MimeType
|
||||||
ArrayPrototypeIncludes,
|
* @property {string} type
|
||||||
Map,
|
* @property {string} subtype
|
||||||
MapPrototypeGet,
|
* @property {Map<string,string>} parameters
|
||||||
MapPrototypeHas,
|
*/
|
||||||
MapPrototypeSet,
|
|
||||||
RegExpPrototypeTest,
|
|
||||||
SafeMapIterator,
|
|
||||||
StringPrototypeReplaceAll,
|
|
||||||
StringPrototypeToLowerCase,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
const {
|
|
||||||
collectSequenceOfCodepoints,
|
|
||||||
HTTP_WHITESPACE,
|
|
||||||
HTTP_WHITESPACE_PREFIX_RE,
|
|
||||||
HTTP_WHITESPACE_SUFFIX_RE,
|
|
||||||
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
|
|
||||||
HTTP_TOKEN_CODE_POINT_RE,
|
|
||||||
collectHttpQuotedString,
|
|
||||||
} = window.__bootstrap.infra;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef MimeType
|
* @param {string} input
|
||||||
* @property {string} type
|
* @returns {MimeType | null}
|
||||||
* @property {string} subtype
|
*/
|
||||||
* @property {Map<string,string>} parameters
|
function parseMimeType(input) {
|
||||||
*/
|
// 1.
|
||||||
|
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_PREFIX_RE, "");
|
||||||
|
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_SUFFIX_RE, "");
|
||||||
|
|
||||||
/**
|
// 2.
|
||||||
* @param {string} input
|
let position = 0;
|
||||||
* @returns {MimeType | null}
|
const endOfInput = input.length;
|
||||||
*/
|
|
||||||
function parseMimeType(input) {
|
|
||||||
// 1.
|
|
||||||
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_PREFIX_RE, "");
|
|
||||||
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_SUFFIX_RE, "");
|
|
||||||
|
|
||||||
// 2.
|
// 3.
|
||||||
let position = 0;
|
const res1 = collectSequenceOfCodepoints(
|
||||||
const endOfInput = input.length;
|
input,
|
||||||
|
position,
|
||||||
|
(c) => c != "\u002F",
|
||||||
|
);
|
||||||
|
const type = res1.result;
|
||||||
|
position = res1.position;
|
||||||
|
|
||||||
// 3.
|
// 4.
|
||||||
|
if (type === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, type)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5.
|
||||||
|
if (position >= endOfInput) return null;
|
||||||
|
|
||||||
|
// 6.
|
||||||
|
position++;
|
||||||
|
|
||||||
|
// 7.
|
||||||
|
const res2 = collectSequenceOfCodepoints(
|
||||||
|
input,
|
||||||
|
position,
|
||||||
|
(c) => c != "\u003B",
|
||||||
|
);
|
||||||
|
let subtype = res2.result;
|
||||||
|
position = res2.position;
|
||||||
|
|
||||||
|
// 8.
|
||||||
|
subtype = StringPrototypeReplaceAll(subtype, HTTP_WHITESPACE_SUFFIX_RE, "");
|
||||||
|
|
||||||
|
// 9.
|
||||||
|
if (
|
||||||
|
subtype === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, subtype)
|
||||||
|
) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10.
|
||||||
|
const mimeType = {
|
||||||
|
type: StringPrototypeToLowerCase(type),
|
||||||
|
subtype: StringPrototypeToLowerCase(subtype),
|
||||||
|
/** @type {Map<string, string>} */
|
||||||
|
parameters: new Map(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 11.
|
||||||
|
while (position < endOfInput) {
|
||||||
|
// 11.1.
|
||||||
|
position++;
|
||||||
|
|
||||||
|
// 11.2.
|
||||||
const res1 = collectSequenceOfCodepoints(
|
const res1 = collectSequenceOfCodepoints(
|
||||||
input,
|
input,
|
||||||
position,
|
position,
|
||||||
(c) => c != "\u002F",
|
(c) => ArrayPrototypeIncludes(HTTP_WHITESPACE, c),
|
||||||
);
|
);
|
||||||
const type = res1.result;
|
|
||||||
position = res1.position;
|
position = res1.position;
|
||||||
|
|
||||||
// 4.
|
// 11.3.
|
||||||
if (type === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, type)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5.
|
|
||||||
if (position >= endOfInput) return null;
|
|
||||||
|
|
||||||
// 6.
|
|
||||||
position++;
|
|
||||||
|
|
||||||
// 7.
|
|
||||||
const res2 = collectSequenceOfCodepoints(
|
const res2 = collectSequenceOfCodepoints(
|
||||||
input,
|
input,
|
||||||
position,
|
position,
|
||||||
(c) => c != "\u003B",
|
(c) => c !== "\u003B" && c !== "\u003D",
|
||||||
);
|
);
|
||||||
let subtype = res2.result;
|
let parameterName = res2.result;
|
||||||
position = res2.position;
|
position = res2.position;
|
||||||
|
|
||||||
// 8.
|
// 11.4.
|
||||||
subtype = StringPrototypeReplaceAll(subtype, HTTP_WHITESPACE_SUFFIX_RE, "");
|
parameterName = StringPrototypeToLowerCase(parameterName);
|
||||||
|
|
||||||
// 9.
|
// 11.5.
|
||||||
if (
|
if (position < endOfInput) {
|
||||||
subtype === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, subtype)
|
if (input[position] == "\u003B") continue;
|
||||||
) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10.
|
|
||||||
const mimeType = {
|
|
||||||
type: StringPrototypeToLowerCase(type),
|
|
||||||
subtype: StringPrototypeToLowerCase(subtype),
|
|
||||||
/** @type {Map<string, string>} */
|
|
||||||
parameters: new Map(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// 11.
|
|
||||||
while (position < endOfInput) {
|
|
||||||
// 11.1.
|
|
||||||
position++;
|
position++;
|
||||||
|
}
|
||||||
|
|
||||||
// 11.2.
|
// 11.6.
|
||||||
const res1 = collectSequenceOfCodepoints(
|
if (position >= endOfInput) break;
|
||||||
|
|
||||||
|
// 11.7.
|
||||||
|
let parameterValue = null;
|
||||||
|
|
||||||
|
// 11.8.
|
||||||
|
if (input[position] === "\u0022") {
|
||||||
|
// 11.8.1.
|
||||||
|
const res = collectHttpQuotedString(input, position, true);
|
||||||
|
parameterValue = res.result;
|
||||||
|
position = res.position;
|
||||||
|
|
||||||
|
// 11.8.2.
|
||||||
|
position++;
|
||||||
|
} else { // 11.9.
|
||||||
|
// 11.9.1.
|
||||||
|
const res = collectSequenceOfCodepoints(
|
||||||
input,
|
input,
|
||||||
position,
|
position,
|
||||||
(c) => ArrayPrototypeIncludes(HTTP_WHITESPACE, c),
|
(c) => c !== "\u003B",
|
||||||
);
|
);
|
||||||
position = res1.position;
|
parameterValue = res.result;
|
||||||
|
position = res.position;
|
||||||
|
|
||||||
// 11.3.
|
// 11.9.2.
|
||||||
const res2 = collectSequenceOfCodepoints(
|
parameterValue = StringPrototypeReplaceAll(
|
||||||
input,
|
parameterValue,
|
||||||
position,
|
HTTP_WHITESPACE_SUFFIX_RE,
|
||||||
(c) => c !== "\u003B" && c !== "\u003D",
|
"",
|
||||||
);
|
);
|
||||||
let parameterName = res2.result;
|
|
||||||
position = res2.position;
|
|
||||||
|
|
||||||
// 11.4.
|
// 11.9.3.
|
||||||
parameterName = StringPrototypeToLowerCase(parameterName);
|
if (parameterValue === "") continue;
|
||||||
|
}
|
||||||
|
|
||||||
// 11.5.
|
// 11.10.
|
||||||
if (position < endOfInput) {
|
if (
|
||||||
if (input[position] == "\u003B") continue;
|
parameterName !== "" &&
|
||||||
position++;
|
RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, parameterName) &&
|
||||||
|
RegExpPrototypeTest(
|
||||||
|
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
|
||||||
|
parameterValue,
|
||||||
|
) &&
|
||||||
|
!MapPrototypeHas(mimeType.parameters, parameterName)
|
||||||
|
) {
|
||||||
|
MapPrototypeSet(mimeType.parameters, parameterName, parameterValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12.
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MimeType} mimeType
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function essence(mimeType) {
|
||||||
|
return `${mimeType.type}/${mimeType.subtype}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {MimeType} mimeType
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
function serializeMimeType(mimeType) {
|
||||||
|
let serialization = essence(mimeType);
|
||||||
|
for (const param of new SafeMapIterator(mimeType.parameters)) {
|
||||||
|
serialization += `;${param[0]}=`;
|
||||||
|
let value = param[1];
|
||||||
|
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, value)) {
|
||||||
|
value = StringPrototypeReplaceAll(value, "\\", "\\\\");
|
||||||
|
value = StringPrototypeReplaceAll(value, '"', '\\"');
|
||||||
|
value = `"${value}"`;
|
||||||
|
}
|
||||||
|
serialization += value;
|
||||||
|
}
|
||||||
|
return serialization;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of the Fetch spec's "extract a MIME type" algorithm
|
||||||
|
* (https://fetch.spec.whatwg.org/#concept-header-extract-mime-type).
|
||||||
|
* @param {string[] | null} headerValues The result of getting, decoding and
|
||||||
|
* splitting the "Content-Type" header.
|
||||||
|
* @returns {MimeType | null}
|
||||||
|
*/
|
||||||
|
function extractMimeType(headerValues) {
|
||||||
|
if (headerValues === null) return null;
|
||||||
|
|
||||||
|
let charset = null;
|
||||||
|
let essence_ = null;
|
||||||
|
let mimeType = null;
|
||||||
|
for (let i = 0; i < headerValues.length; ++i) {
|
||||||
|
const value = headerValues[i];
|
||||||
|
const temporaryMimeType = parseMimeType(value);
|
||||||
|
if (
|
||||||
|
temporaryMimeType === null ||
|
||||||
|
essence(temporaryMimeType) == "*/*"
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
mimeType = temporaryMimeType;
|
||||||
|
if (essence(mimeType) !== essence_) {
|
||||||
|
charset = null;
|
||||||
|
const newCharset = MapPrototypeGet(mimeType.parameters, "charset");
|
||||||
|
if (newCharset !== undefined) {
|
||||||
|
charset = newCharset;
|
||||||
}
|
}
|
||||||
|
essence_ = essence(mimeType);
|
||||||
// 11.6.
|
} else {
|
||||||
if (position >= endOfInput) break;
|
|
||||||
|
|
||||||
// 11.7.
|
|
||||||
let parameterValue = null;
|
|
||||||
|
|
||||||
// 11.8.
|
|
||||||
if (input[position] === "\u0022") {
|
|
||||||
// 11.8.1.
|
|
||||||
const res = collectHttpQuotedString(input, position, true);
|
|
||||||
parameterValue = res.result;
|
|
||||||
position = res.position;
|
|
||||||
|
|
||||||
// 11.8.2.
|
|
||||||
position++;
|
|
||||||
} else { // 11.9.
|
|
||||||
// 11.9.1.
|
|
||||||
const res = collectSequenceOfCodepoints(
|
|
||||||
input,
|
|
||||||
position,
|
|
||||||
(c) => c !== "\u003B",
|
|
||||||
);
|
|
||||||
parameterValue = res.result;
|
|
||||||
position = res.position;
|
|
||||||
|
|
||||||
// 11.9.2.
|
|
||||||
parameterValue = StringPrototypeReplaceAll(
|
|
||||||
parameterValue,
|
|
||||||
HTTP_WHITESPACE_SUFFIX_RE,
|
|
||||||
"",
|
|
||||||
);
|
|
||||||
|
|
||||||
// 11.9.3.
|
|
||||||
if (parameterValue === "") continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 11.10.
|
|
||||||
if (
|
if (
|
||||||
parameterName !== "" &&
|
!MapPrototypeHas(mimeType.parameters, "charset") &&
|
||||||
RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, parameterName) &&
|
charset !== null
|
||||||
RegExpPrototypeTest(
|
|
||||||
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
|
|
||||||
parameterValue,
|
|
||||||
) &&
|
|
||||||
!MapPrototypeHas(mimeType.parameters, parameterName)
|
|
||||||
) {
|
) {
|
||||||
MapPrototypeSet(mimeType.parameters, parameterName, parameterValue);
|
MapPrototypeSet(mimeType.parameters, "charset", charset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 12.
|
|
||||||
return mimeType;
|
|
||||||
}
|
}
|
||||||
|
return mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
export { essence, extractMimeType, parseMimeType, serializeMimeType };
|
||||||
* @param {MimeType} mimeType
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function essence(mimeType) {
|
|
||||||
return `${mimeType.type}/${mimeType.subtype}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {MimeType} mimeType
|
|
||||||
* @returns {string}
|
|
||||||
*/
|
|
||||||
function serializeMimeType(mimeType) {
|
|
||||||
let serialization = essence(mimeType);
|
|
||||||
for (const param of new SafeMapIterator(mimeType.parameters)) {
|
|
||||||
serialization += `;${param[0]}=`;
|
|
||||||
let value = param[1];
|
|
||||||
if (!RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, value)) {
|
|
||||||
value = StringPrototypeReplaceAll(value, "\\", "\\\\");
|
|
||||||
value = StringPrototypeReplaceAll(value, '"', '\\"');
|
|
||||||
value = `"${value}"`;
|
|
||||||
}
|
|
||||||
serialization += value;
|
|
||||||
}
|
|
||||||
return serialization;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Part of the Fetch spec's "extract a MIME type" algorithm
|
|
||||||
* (https://fetch.spec.whatwg.org/#concept-header-extract-mime-type).
|
|
||||||
* @param {string[] | null} headerValues The result of getting, decoding and
|
|
||||||
* splitting the "Content-Type" header.
|
|
||||||
* @returns {MimeType | null}
|
|
||||||
*/
|
|
||||||
function extractMimeType(headerValues) {
|
|
||||||
if (headerValues === null) return null;
|
|
||||||
|
|
||||||
let charset = null;
|
|
||||||
let essence_ = null;
|
|
||||||
let mimeType = null;
|
|
||||||
for (let i = 0; i < headerValues.length; ++i) {
|
|
||||||
const value = headerValues[i];
|
|
||||||
const temporaryMimeType = parseMimeType(value);
|
|
||||||
if (
|
|
||||||
temporaryMimeType === null ||
|
|
||||||
essence(temporaryMimeType) == "*/*"
|
|
||||||
) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
mimeType = temporaryMimeType;
|
|
||||||
if (essence(mimeType) !== essence_) {
|
|
||||||
charset = null;
|
|
||||||
const newCharset = MapPrototypeGet(mimeType.parameters, "charset");
|
|
||||||
if (newCharset !== undefined) {
|
|
||||||
charset = newCharset;
|
|
||||||
}
|
|
||||||
essence_ = essence(mimeType);
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
!MapPrototypeHas(mimeType.parameters, "charset") &&
|
|
||||||
charset !== null
|
|
||||||
) {
|
|
||||||
MapPrototypeSet(mimeType.parameters, "charset", charset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return mimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.mimesniff = {
|
|
||||||
parseMimeType,
|
|
||||||
essence,
|
|
||||||
serializeMimeType,
|
|
||||||
extractMimeType,
|
|
||||||
};
|
|
||||||
})(this);
|
|
||||||
|
|
2743
ext/web/02_event.js
2743
ext/web/02_event.js
File diff suppressed because it is too large
Load diff
|
@ -6,138 +6,135 @@
|
||||||
/// <reference path="../web/internal.d.ts" />
|
/// <reference path="../web/internal.d.ts" />
|
||||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayBuffer,
|
||||||
|
ArrayBufferPrototype,
|
||||||
|
ArrayBufferPrototypeGetByteLength,
|
||||||
|
ArrayBufferPrototypeSlice,
|
||||||
|
ArrayBufferIsView,
|
||||||
|
DataView,
|
||||||
|
DataViewPrototypeGetBuffer,
|
||||||
|
DataViewPrototypeGetByteLength,
|
||||||
|
DataViewPrototypeGetByteOffset,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
TypedArrayPrototypeGetBuffer,
|
||||||
|
TypedArrayPrototypeGetByteOffset,
|
||||||
|
TypedArrayPrototypeGetLength,
|
||||||
|
TypedArrayPrototypeGetSymbolToStringTag,
|
||||||
|
TypeErrorPrototype,
|
||||||
|
WeakMap,
|
||||||
|
WeakMapPrototypeSet,
|
||||||
|
Int8Array,
|
||||||
|
Int16Array,
|
||||||
|
Int32Array,
|
||||||
|
BigInt64Array,
|
||||||
|
Uint8Array,
|
||||||
|
Uint8ClampedArray,
|
||||||
|
Uint16Array,
|
||||||
|
Uint32Array,
|
||||||
|
BigUint64Array,
|
||||||
|
Float32Array,
|
||||||
|
Float64Array,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
const objectCloneMemo = new WeakMap();
|
||||||
const core = window.Deno.core;
|
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
|
||||||
const {
|
|
||||||
ArrayBuffer,
|
|
||||||
ArrayBufferPrototype,
|
|
||||||
ArrayBufferPrototypeGetByteLength,
|
|
||||||
ArrayBufferPrototypeSlice,
|
|
||||||
ArrayBufferIsView,
|
|
||||||
DataView,
|
|
||||||
DataViewPrototypeGetBuffer,
|
|
||||||
DataViewPrototypeGetByteLength,
|
|
||||||
DataViewPrototypeGetByteOffset,
|
|
||||||
ObjectPrototypeIsPrototypeOf,
|
|
||||||
TypedArrayPrototypeGetBuffer,
|
|
||||||
TypedArrayPrototypeGetByteOffset,
|
|
||||||
TypedArrayPrototypeGetLength,
|
|
||||||
TypedArrayPrototypeGetSymbolToStringTag,
|
|
||||||
TypeErrorPrototype,
|
|
||||||
WeakMap,
|
|
||||||
WeakMapPrototypeSet,
|
|
||||||
Int8Array,
|
|
||||||
Int16Array,
|
|
||||||
Int32Array,
|
|
||||||
BigInt64Array,
|
|
||||||
Uint8Array,
|
|
||||||
Uint8ClampedArray,
|
|
||||||
Uint16Array,
|
|
||||||
Uint32Array,
|
|
||||||
BigUint64Array,
|
|
||||||
Float32Array,
|
|
||||||
Float64Array,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
const objectCloneMemo = new WeakMap();
|
function cloneArrayBuffer(
|
||||||
|
srcBuffer,
|
||||||
function cloneArrayBuffer(
|
srcByteOffset,
|
||||||
|
srcLength,
|
||||||
|
_cloneConstructor,
|
||||||
|
) {
|
||||||
|
// this function fudges the return type but SharedArrayBuffer is disabled for a while anyway
|
||||||
|
return ArrayBufferPrototypeSlice(
|
||||||
srcBuffer,
|
srcBuffer,
|
||||||
srcByteOffset,
|
srcByteOffset,
|
||||||
srcLength,
|
srcByteOffset + srcLength,
|
||||||
_cloneConstructor,
|
);
|
||||||
) {
|
}
|
||||||
// this function fudges the return type but SharedArrayBuffer is disabled for a while anyway
|
|
||||||
return ArrayBufferPrototypeSlice(
|
// TODO(petamoriken): Resizable ArrayBuffer support in the future
|
||||||
srcBuffer,
|
/** Clone a value in a similar way to structured cloning. It is similar to a
|
||||||
srcByteOffset,
|
* StructureDeserialize(StructuredSerialize(...)). */
|
||||||
srcByteOffset + srcLength,
|
function structuredClone(value) {
|
||||||
|
// Performance optimization for buffers, otherwise
|
||||||
|
// `serialize/deserialize` will allocate new buffer.
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, value)) {
|
||||||
|
const cloned = cloneArrayBuffer(
|
||||||
|
value,
|
||||||
|
0,
|
||||||
|
ArrayBufferPrototypeGetByteLength(value),
|
||||||
|
ArrayBuffer,
|
||||||
|
);
|
||||||
|
WeakMapPrototypeSet(objectCloneMemo, value, cloned);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ArrayBufferIsView(value)) {
|
||||||
|
const tag = TypedArrayPrototypeGetSymbolToStringTag(value);
|
||||||
|
// DataView
|
||||||
|
if (tag === undefined) {
|
||||||
|
return new DataView(
|
||||||
|
structuredClone(DataViewPrototypeGetBuffer(value)),
|
||||||
|
DataViewPrototypeGetByteOffset(value),
|
||||||
|
DataViewPrototypeGetByteLength(value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// TypedArray
|
||||||
|
let Constructor;
|
||||||
|
switch (tag) {
|
||||||
|
case "Int8Array":
|
||||||
|
Constructor = Int8Array;
|
||||||
|
break;
|
||||||
|
case "Int16Array":
|
||||||
|
Constructor = Int16Array;
|
||||||
|
break;
|
||||||
|
case "Int32Array":
|
||||||
|
Constructor = Int32Array;
|
||||||
|
break;
|
||||||
|
case "BigInt64Array":
|
||||||
|
Constructor = BigInt64Array;
|
||||||
|
break;
|
||||||
|
case "Uint8Array":
|
||||||
|
Constructor = Uint8Array;
|
||||||
|
break;
|
||||||
|
case "Uint8ClampedArray":
|
||||||
|
Constructor = Uint8ClampedArray;
|
||||||
|
break;
|
||||||
|
case "Uint16Array":
|
||||||
|
Constructor = Uint16Array;
|
||||||
|
break;
|
||||||
|
case "Uint32Array":
|
||||||
|
Constructor = Uint32Array;
|
||||||
|
break;
|
||||||
|
case "BigUint64Array":
|
||||||
|
Constructor = BigUint64Array;
|
||||||
|
break;
|
||||||
|
case "Float32Array":
|
||||||
|
Constructor = Float32Array;
|
||||||
|
break;
|
||||||
|
case "Float64Array":
|
||||||
|
Constructor = Float64Array;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return new Constructor(
|
||||||
|
structuredClone(TypedArrayPrototypeGetBuffer(value)),
|
||||||
|
TypedArrayPrototypeGetByteOffset(value),
|
||||||
|
TypedArrayPrototypeGetLength(value),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(petamoriken): Resizable ArrayBuffer support in the future
|
try {
|
||||||
/** Clone a value in a similar way to structured cloning. It is similar to a
|
return core.deserialize(core.serialize(value));
|
||||||
* StructureDeserialize(StructuredSerialize(...)). */
|
} catch (e) {
|
||||||
function structuredClone(value) {
|
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
||||||
// Performance optimization for buffers, otherwise
|
throw new DOMException(e.message, "DataCloneError");
|
||||||
// `serialize/deserialize` will allocate new buffer.
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, value)) {
|
|
||||||
const cloned = cloneArrayBuffer(
|
|
||||||
value,
|
|
||||||
0,
|
|
||||||
ArrayBufferPrototypeGetByteLength(value),
|
|
||||||
ArrayBuffer,
|
|
||||||
);
|
|
||||||
WeakMapPrototypeSet(objectCloneMemo, value, cloned);
|
|
||||||
return cloned;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ArrayBufferIsView(value)) {
|
|
||||||
const tag = TypedArrayPrototypeGetSymbolToStringTag(value);
|
|
||||||
// DataView
|
|
||||||
if (tag === undefined) {
|
|
||||||
return new DataView(
|
|
||||||
structuredClone(DataViewPrototypeGetBuffer(value)),
|
|
||||||
DataViewPrototypeGetByteOffset(value),
|
|
||||||
DataViewPrototypeGetByteLength(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// TypedArray
|
|
||||||
let Constructor;
|
|
||||||
switch (tag) {
|
|
||||||
case "Int8Array":
|
|
||||||
Constructor = Int8Array;
|
|
||||||
break;
|
|
||||||
case "Int16Array":
|
|
||||||
Constructor = Int16Array;
|
|
||||||
break;
|
|
||||||
case "Int32Array":
|
|
||||||
Constructor = Int32Array;
|
|
||||||
break;
|
|
||||||
case "BigInt64Array":
|
|
||||||
Constructor = BigInt64Array;
|
|
||||||
break;
|
|
||||||
case "Uint8Array":
|
|
||||||
Constructor = Uint8Array;
|
|
||||||
break;
|
|
||||||
case "Uint8ClampedArray":
|
|
||||||
Constructor = Uint8ClampedArray;
|
|
||||||
break;
|
|
||||||
case "Uint16Array":
|
|
||||||
Constructor = Uint16Array;
|
|
||||||
break;
|
|
||||||
case "Uint32Array":
|
|
||||||
Constructor = Uint32Array;
|
|
||||||
break;
|
|
||||||
case "BigUint64Array":
|
|
||||||
Constructor = BigUint64Array;
|
|
||||||
break;
|
|
||||||
case "Float32Array":
|
|
||||||
Constructor = Float32Array;
|
|
||||||
break;
|
|
||||||
case "Float64Array":
|
|
||||||
Constructor = Float64Array;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return new Constructor(
|
|
||||||
structuredClone(TypedArrayPrototypeGetBuffer(value)),
|
|
||||||
TypedArrayPrototypeGetByteOffset(value),
|
|
||||||
TypedArrayPrototypeGetLength(value),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return core.deserialize(core.serialize(value));
|
|
||||||
} catch (e) {
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
|
||||||
throw new DOMException(e.message, "DataCloneError");
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.__bootstrap.structuredClone = structuredClone;
|
export { structuredClone };
|
||||||
})(globalThis);
|
|
||||||
|
|
|
@ -1,375 +1,372 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
ArrayPrototypePush,
|
ArrayPrototypePush,
|
||||||
ArrayPrototypeShift,
|
ArrayPrototypeShift,
|
||||||
FunctionPrototypeCall,
|
FunctionPrototypeCall,
|
||||||
Map,
|
Map,
|
||||||
MapPrototypeDelete,
|
MapPrototypeDelete,
|
||||||
MapPrototypeGet,
|
MapPrototypeGet,
|
||||||
MapPrototypeHas,
|
MapPrototypeHas,
|
||||||
MapPrototypeSet,
|
MapPrototypeSet,
|
||||||
Uint8Array,
|
Uint8Array,
|
||||||
Uint32Array,
|
Uint32Array,
|
||||||
// deno-lint-ignore camelcase
|
// deno-lint-ignore camelcase
|
||||||
NumberPOSITIVE_INFINITY,
|
NumberPOSITIVE_INFINITY,
|
||||||
PromisePrototypeThen,
|
PromisePrototypeThen,
|
||||||
SafeArrayIterator,
|
SafeArrayIterator,
|
||||||
SymbolFor,
|
SymbolFor,
|
||||||
TypeError,
|
TypeError,
|
||||||
indirectEval,
|
indirectEval,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
const { webidl } = window.__bootstrap;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const { reportException } = window.__bootstrap.event;
|
import { reportException } from "internal:ext/web/02_event.js";
|
||||||
const { assert } = window.__bootstrap.infra;
|
import { assert } from "internal:ext/web/00_infra.js";
|
||||||
|
|
||||||
const hrU8 = new Uint8Array(8);
|
const hrU8 = new Uint8Array(8);
|
||||||
const hr = new Uint32Array(hrU8.buffer);
|
const hr = new Uint32Array(hrU8.buffer);
|
||||||
function opNow() {
|
function opNow() {
|
||||||
ops.op_now(hrU8);
|
ops.op_now(hrU8);
|
||||||
return (hr[0] * 1000 + hr[1] / 1e6);
|
return (hr[0] * 1000 + hr[1] / 1e6);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The task queue corresponding to the timer task source.
|
||||||
|
*
|
||||||
|
* @type { {action: () => void, nestingLevel: number}[] }
|
||||||
|
*/
|
||||||
|
const timerTasks = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current task's timer nesting level, or zero if we're not currently
|
||||||
|
* running a timer task (since the minimum nesting level is 1).
|
||||||
|
*
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
|
let timerNestingLevel = 0;
|
||||||
|
|
||||||
|
function handleTimerMacrotask() {
|
||||||
|
if (timerTasks.length === 0) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
const task = ArrayPrototypeShift(timerTasks);
|
||||||
|
|
||||||
/**
|
timerNestingLevel = task.nestingLevel;
|
||||||
* The task queue corresponding to the timer task source.
|
|
||||||
*
|
|
||||||
* @type { {action: () => void, nestingLevel: number}[] }
|
|
||||||
*/
|
|
||||||
const timerTasks = [];
|
|
||||||
|
|
||||||
/**
|
try {
|
||||||
* The current task's timer nesting level, or zero if we're not currently
|
task.action();
|
||||||
* running a timer task (since the minimum nesting level is 1).
|
} finally {
|
||||||
*
|
timerNestingLevel = 0;
|
||||||
* @type {number}
|
}
|
||||||
*/
|
return timerTasks.length === 0;
|
||||||
let timerNestingLevel = 0;
|
}
|
||||||
|
|
||||||
function handleTimerMacrotask() {
|
// ---------------------------------------------------------------------------
|
||||||
if (timerTasks.length === 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const task = ArrayPrototypeShift(timerTasks);
|
/**
|
||||||
|
* The keys in this map correspond to the key ID's in the spec's map of active
|
||||||
|
* timers. The values are the timeout's cancel rid.
|
||||||
|
*
|
||||||
|
* @type {Map<number, { cancelRid: number, isRef: boolean, promiseId: number }>}
|
||||||
|
*/
|
||||||
|
const activeTimers = new Map();
|
||||||
|
|
||||||
timerNestingLevel = task.nestingLevel;
|
let nextId = 1;
|
||||||
|
|
||||||
try {
|
/**
|
||||||
task.action();
|
* @param {Function | string} callback
|
||||||
} finally {
|
* @param {number} timeout
|
||||||
timerNestingLevel = 0;
|
* @param {Array<any>} args
|
||||||
}
|
* @param {boolean} repeat
|
||||||
return timerTasks.length === 0;
|
* @param {number | undefined} prevId
|
||||||
|
* @returns {number} The timer ID
|
||||||
|
*/
|
||||||
|
function initializeTimer(
|
||||||
|
callback,
|
||||||
|
timeout,
|
||||||
|
args,
|
||||||
|
repeat,
|
||||||
|
prevId,
|
||||||
|
) {
|
||||||
|
// 2. If previousId was given, let id be previousId; otherwise, let
|
||||||
|
// previousId be an implementation-defined integer than is greater than zero
|
||||||
|
// and does not already exist in global's map of active timers.
|
||||||
|
let id;
|
||||||
|
let timerInfo;
|
||||||
|
if (prevId !== undefined) {
|
||||||
|
// `prevId` is only passed for follow-up calls on intervals
|
||||||
|
assert(repeat);
|
||||||
|
id = prevId;
|
||||||
|
timerInfo = MapPrototypeGet(activeTimers, id);
|
||||||
|
} else {
|
||||||
|
// TODO(@andreubotella): Deal with overflow.
|
||||||
|
// https://github.com/whatwg/html/issues/7358
|
||||||
|
id = nextId++;
|
||||||
|
const cancelRid = ops.op_timer_handle();
|
||||||
|
timerInfo = { cancelRid, isRef: true, promiseId: -1 };
|
||||||
|
|
||||||
|
// Step 4 in "run steps after a timeout".
|
||||||
|
MapPrototypeSet(activeTimers, id, timerInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// 3. If the surrounding agent's event loop's currently running task is a
|
||||||
|
// task that was created by this algorithm, then let nesting level be the
|
||||||
|
// task's timer nesting level. Otherwise, let nesting level be zero.
|
||||||
|
// 4. If timeout is less than 0, then set timeout to 0.
|
||||||
|
// 5. If nesting level is greater than 5, and timeout is less than 4, then
|
||||||
|
// set timeout to 4.
|
||||||
|
//
|
||||||
|
// The nesting level of 5 and minimum of 4 ms are spec-mandated magic
|
||||||
|
// constants.
|
||||||
|
if (timeout < 0) timeout = 0;
|
||||||
|
if (timerNestingLevel > 5 && timeout < 4) timeout = 4;
|
||||||
|
|
||||||
/**
|
// 9. Let task be a task that runs the following steps:
|
||||||
* The keys in this map correspond to the key ID's in the spec's map of active
|
const task = {
|
||||||
* timers. The values are the timeout's cancel rid.
|
action: () => {
|
||||||
*
|
// 1. If id does not exist in global's map of active timers, then abort
|
||||||
* @type {Map<number, { cancelRid: number, isRef: boolean, promiseId: number }>}
|
// these steps.
|
||||||
*/
|
//
|
||||||
const activeTimers = new Map();
|
// This is relevant if the timer has been canceled after the sleep op
|
||||||
|
// resolves but before this task runs.
|
||||||
|
if (!MapPrototypeHas(activeTimers, id)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let nextId = 1;
|
// 2.
|
||||||
|
// 3.
|
||||||
|
if (typeof callback === "function") {
|
||||||
|
try {
|
||||||
|
FunctionPrototypeCall(
|
||||||
|
callback,
|
||||||
|
globalThis,
|
||||||
|
...new SafeArrayIterator(args),
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
reportException(error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
indirectEval(callback);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
if (repeat) {
|
||||||
* @param {Function | string} callback
|
if (MapPrototypeHas(activeTimers, id)) {
|
||||||
* @param {number} timeout
|
// 4. If id does not exist in global's map of active timers, then
|
||||||
* @param {Array<any>} args
|
// abort these steps.
|
||||||
* @param {boolean} repeat
|
// NOTE: If might have been removed via the author code in handler
|
||||||
* @param {number | undefined} prevId
|
// calling clearTimeout() or clearInterval().
|
||||||
* @returns {number} The timer ID
|
// 5. If repeat is true, then perform the timer initialization steps
|
||||||
*/
|
// again, given global, handler, timeout, arguments, true, and id.
|
||||||
function initializeTimer(
|
initializeTimer(callback, timeout, args, true, id);
|
||||||
callback,
|
}
|
||||||
|
} else {
|
||||||
|
// 6. Otherwise, remove global's map of active timers[id].
|
||||||
|
core.tryClose(timerInfo.cancelRid);
|
||||||
|
MapPrototypeDelete(activeTimers, id);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 10. Increment nesting level by one.
|
||||||
|
// 11. Set task's timer nesting level to nesting level.
|
||||||
|
nestingLevel: timerNestingLevel + 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// 12. Let completionStep be an algorithm step which queues a global task on
|
||||||
|
// the timer task source given global to run task.
|
||||||
|
// 13. Run steps after a timeout given global, "setTimeout/setInterval",
|
||||||
|
// timeout, completionStep, and id.
|
||||||
|
runAfterTimeout(
|
||||||
|
() => ArrayPrototypePush(timerTasks, task),
|
||||||
timeout,
|
timeout,
|
||||||
args,
|
timerInfo,
|
||||||
repeat,
|
);
|
||||||
prevId,
|
|
||||||
) {
|
|
||||||
// 2. If previousId was given, let id be previousId; otherwise, let
|
|
||||||
// previousId be an implementation-defined integer than is greater than zero
|
|
||||||
// and does not already exist in global's map of active timers.
|
|
||||||
let id;
|
|
||||||
let timerInfo;
|
|
||||||
if (prevId !== undefined) {
|
|
||||||
// `prevId` is only passed for follow-up calls on intervals
|
|
||||||
assert(repeat);
|
|
||||||
id = prevId;
|
|
||||||
timerInfo = MapPrototypeGet(activeTimers, id);
|
|
||||||
} else {
|
|
||||||
// TODO(@andreubotella): Deal with overflow.
|
|
||||||
// https://github.com/whatwg/html/issues/7358
|
|
||||||
id = nextId++;
|
|
||||||
const cancelRid = ops.op_timer_handle();
|
|
||||||
timerInfo = { cancelRid, isRef: true, promiseId: -1 };
|
|
||||||
|
|
||||||
// Step 4 in "run steps after a timeout".
|
return id;
|
||||||
MapPrototypeSet(activeTimers, id, timerInfo);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// 3. If the surrounding agent's event loop's currently running task is a
|
// ---------------------------------------------------------------------------
|
||||||
// task that was created by this algorithm, then let nesting level be the
|
|
||||||
// task's timer nesting level. Otherwise, let nesting level be zero.
|
|
||||||
// 4. If timeout is less than 0, then set timeout to 0.
|
|
||||||
// 5. If nesting level is greater than 5, and timeout is less than 4, then
|
|
||||||
// set timeout to 4.
|
|
||||||
//
|
|
||||||
// The nesting level of 5 and minimum of 4 ms are spec-mandated magic
|
|
||||||
// constants.
|
|
||||||
if (timeout < 0) timeout = 0;
|
|
||||||
if (timerNestingLevel > 5 && timeout < 4) timeout = 4;
|
|
||||||
|
|
||||||
// 9. Let task be a task that runs the following steps:
|
/**
|
||||||
const task = {
|
* @typedef ScheduledTimer
|
||||||
action: () => {
|
* @property {number} millis
|
||||||
// 1. If id does not exist in global's map of active timers, then abort
|
* @property {() => void} cb
|
||||||
// these steps.
|
* @property {boolean} resolved
|
||||||
//
|
* @property {ScheduledTimer | null} prev
|
||||||
// This is relevant if the timer has been canceled after the sleep op
|
* @property {ScheduledTimer | null} next
|
||||||
// resolves but before this task runs.
|
*/
|
||||||
if (!MapPrototypeHas(activeTimers, id)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2.
|
/**
|
||||||
// 3.
|
* A doubly linked list of timers.
|
||||||
if (typeof callback === "function") {
|
* @type { { head: ScheduledTimer | null, tail: ScheduledTimer | null } }
|
||||||
try {
|
*/
|
||||||
FunctionPrototypeCall(
|
const scheduledTimers = { head: null, tail: null };
|
||||||
callback,
|
|
||||||
globalThis,
|
|
||||||
...new SafeArrayIterator(args),
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
reportException(error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
indirectEval(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (repeat) {
|
/**
|
||||||
if (MapPrototypeHas(activeTimers, id)) {
|
* @param {() => void} cb Will be run after the timeout, if it hasn't been
|
||||||
// 4. If id does not exist in global's map of active timers, then
|
* cancelled.
|
||||||
// abort these steps.
|
* @param {number} millis
|
||||||
// NOTE: If might have been removed via the author code in handler
|
* @param {{ cancelRid: number, isRef: boolean, promiseId: number }} timerInfo
|
||||||
// calling clearTimeout() or clearInterval().
|
*/
|
||||||
// 5. If repeat is true, then perform the timer initialization steps
|
function runAfterTimeout(cb, millis, timerInfo) {
|
||||||
// again, given global, handler, timeout, arguments, true, and id.
|
const cancelRid = timerInfo.cancelRid;
|
||||||
initializeTimer(callback, timeout, args, true, id);
|
const sleepPromise = core.opAsync("op_sleep", millis, cancelRid);
|
||||||
}
|
timerInfo.promiseId = sleepPromise[SymbolFor("Deno.core.internalPromiseId")];
|
||||||
} else {
|
if (!timerInfo.isRef) {
|
||||||
// 6. Otherwise, remove global's map of active timers[id].
|
|
||||||
core.tryClose(timerInfo.cancelRid);
|
|
||||||
MapPrototypeDelete(activeTimers, id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 10. Increment nesting level by one.
|
|
||||||
// 11. Set task's timer nesting level to nesting level.
|
|
||||||
nestingLevel: timerNestingLevel + 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 12. Let completionStep be an algorithm step which queues a global task on
|
|
||||||
// the timer task source given global to run task.
|
|
||||||
// 13. Run steps after a timeout given global, "setTimeout/setInterval",
|
|
||||||
// timeout, completionStep, and id.
|
|
||||||
runAfterTimeout(
|
|
||||||
() => ArrayPrototypePush(timerTasks, task),
|
|
||||||
timeout,
|
|
||||||
timerInfo,
|
|
||||||
);
|
|
||||||
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @typedef ScheduledTimer
|
|
||||||
* @property {number} millis
|
|
||||||
* @property {() => void} cb
|
|
||||||
* @property {boolean} resolved
|
|
||||||
* @property {ScheduledTimer | null} prev
|
|
||||||
* @property {ScheduledTimer | null} next
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A doubly linked list of timers.
|
|
||||||
* @type { { head: ScheduledTimer | null, tail: ScheduledTimer | null } }
|
|
||||||
*/
|
|
||||||
const scheduledTimers = { head: null, tail: null };
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {() => void} cb Will be run after the timeout, if it hasn't been
|
|
||||||
* cancelled.
|
|
||||||
* @param {number} millis
|
|
||||||
* @param {{ cancelRid: number, isRef: boolean, promiseId: number }} timerInfo
|
|
||||||
*/
|
|
||||||
function runAfterTimeout(cb, millis, timerInfo) {
|
|
||||||
const cancelRid = timerInfo.cancelRid;
|
|
||||||
const sleepPromise = core.opAsync("op_sleep", millis, cancelRid);
|
|
||||||
timerInfo.promiseId =
|
|
||||||
sleepPromise[SymbolFor("Deno.core.internalPromiseId")];
|
|
||||||
if (!timerInfo.isRef) {
|
|
||||||
core.unrefOp(timerInfo.promiseId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @type {ScheduledTimer} */
|
|
||||||
const timerObject = {
|
|
||||||
millis,
|
|
||||||
cb,
|
|
||||||
resolved: false,
|
|
||||||
prev: scheduledTimers.tail,
|
|
||||||
next: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add timerObject to the end of the list.
|
|
||||||
if (scheduledTimers.tail === null) {
|
|
||||||
assert(scheduledTimers.head === null);
|
|
||||||
scheduledTimers.head = scheduledTimers.tail = timerObject;
|
|
||||||
} else {
|
|
||||||
scheduledTimers.tail.next = timerObject;
|
|
||||||
scheduledTimers.tail = timerObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1.
|
|
||||||
PromisePrototypeThen(
|
|
||||||
sleepPromise,
|
|
||||||
(cancelled) => {
|
|
||||||
if (!cancelled) {
|
|
||||||
// The timer was cancelled.
|
|
||||||
removeFromScheduledTimers(timerObject);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 2. Wait until any invocations of this algorithm that had the same
|
|
||||||
// global and orderingIdentifier, that started before this one, and
|
|
||||||
// whose milliseconds is equal to or less than this one's, have
|
|
||||||
// completed.
|
|
||||||
// 4. Perform completionSteps.
|
|
||||||
|
|
||||||
// IMPORTANT: Since the sleep ops aren't guaranteed to resolve in the
|
|
||||||
// right order, whenever one resolves, we run through the scheduled
|
|
||||||
// timers list (which is in the order in which they were scheduled), and
|
|
||||||
// we call the callback for every timer which both:
|
|
||||||
// a) has resolved, and
|
|
||||||
// b) its timeout is lower than the lowest unresolved timeout found so
|
|
||||||
// far in the list.
|
|
||||||
|
|
||||||
timerObject.resolved = true;
|
|
||||||
|
|
||||||
let lowestUnresolvedTimeout = NumberPOSITIVE_INFINITY;
|
|
||||||
|
|
||||||
let currentEntry = scheduledTimers.head;
|
|
||||||
while (currentEntry !== null) {
|
|
||||||
if (currentEntry.millis < lowestUnresolvedTimeout) {
|
|
||||||
if (currentEntry.resolved) {
|
|
||||||
currentEntry.cb();
|
|
||||||
removeFromScheduledTimers(currentEntry);
|
|
||||||
} else {
|
|
||||||
lowestUnresolvedTimeout = currentEntry.millis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentEntry = currentEntry.next;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {ScheduledTimer} timerObj */
|
|
||||||
function removeFromScheduledTimers(timerObj) {
|
|
||||||
if (timerObj.prev !== null) {
|
|
||||||
timerObj.prev.next = timerObj.next;
|
|
||||||
} else {
|
|
||||||
assert(scheduledTimers.head === timerObj);
|
|
||||||
scheduledTimers.head = timerObj.next;
|
|
||||||
}
|
|
||||||
if (timerObj.next !== null) {
|
|
||||||
timerObj.next.prev = timerObj.prev;
|
|
||||||
} else {
|
|
||||||
assert(scheduledTimers.tail === timerObj);
|
|
||||||
scheduledTimers.tail = timerObj.prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
|
||||||
|
|
||||||
function checkThis(thisArg) {
|
|
||||||
if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) {
|
|
||||||
throw new TypeError("Illegal invocation");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTimeout(callback, timeout = 0, ...args) {
|
|
||||||
checkThis(this);
|
|
||||||
if (typeof callback !== "function") {
|
|
||||||
callback = webidl.converters.DOMString(callback);
|
|
||||||
}
|
|
||||||
timeout = webidl.converters.long(timeout);
|
|
||||||
|
|
||||||
return initializeTimer(callback, timeout, args, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setInterval(callback, timeout = 0, ...args) {
|
|
||||||
checkThis(this);
|
|
||||||
if (typeof callback !== "function") {
|
|
||||||
callback = webidl.converters.DOMString(callback);
|
|
||||||
}
|
|
||||||
timeout = webidl.converters.long(timeout);
|
|
||||||
|
|
||||||
return initializeTimer(callback, timeout, args, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearTimeout(id = 0) {
|
|
||||||
checkThis(this);
|
|
||||||
id = webidl.converters.long(id);
|
|
||||||
const timerInfo = MapPrototypeGet(activeTimers, id);
|
|
||||||
if (timerInfo !== undefined) {
|
|
||||||
core.tryClose(timerInfo.cancelRid);
|
|
||||||
MapPrototypeDelete(activeTimers, id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearInterval(id = 0) {
|
|
||||||
checkThis(this);
|
|
||||||
clearTimeout(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function refTimer(id) {
|
|
||||||
const timerInfo = MapPrototypeGet(activeTimers, id);
|
|
||||||
if (timerInfo === undefined || timerInfo.isRef) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
timerInfo.isRef = true;
|
|
||||||
core.refOp(timerInfo.promiseId);
|
|
||||||
}
|
|
||||||
|
|
||||||
function unrefTimer(id) {
|
|
||||||
const timerInfo = MapPrototypeGet(activeTimers, id);
|
|
||||||
if (timerInfo === undefined || !timerInfo.isRef) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
timerInfo.isRef = false;
|
|
||||||
core.unrefOp(timerInfo.promiseId);
|
core.unrefOp(timerInfo.promiseId);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.timers = {
|
/** @type {ScheduledTimer} */
|
||||||
setTimeout,
|
const timerObject = {
|
||||||
setInterval,
|
millis,
|
||||||
clearTimeout,
|
cb,
|
||||||
clearInterval,
|
resolved: false,
|
||||||
handleTimerMacrotask,
|
prev: scheduledTimers.tail,
|
||||||
opNow,
|
next: null,
|
||||||
refTimer,
|
|
||||||
unrefTimer,
|
|
||||||
};
|
};
|
||||||
})(this);
|
|
||||||
|
// Add timerObject to the end of the list.
|
||||||
|
if (scheduledTimers.tail === null) {
|
||||||
|
assert(scheduledTimers.head === null);
|
||||||
|
scheduledTimers.head = scheduledTimers.tail = timerObject;
|
||||||
|
} else {
|
||||||
|
scheduledTimers.tail.next = timerObject;
|
||||||
|
scheduledTimers.tail = timerObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1.
|
||||||
|
PromisePrototypeThen(
|
||||||
|
sleepPromise,
|
||||||
|
(cancelled) => {
|
||||||
|
if (!cancelled) {
|
||||||
|
// The timer was cancelled.
|
||||||
|
removeFromScheduledTimers(timerObject);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 2. Wait until any invocations of this algorithm that had the same
|
||||||
|
// global and orderingIdentifier, that started before this one, and
|
||||||
|
// whose milliseconds is equal to or less than this one's, have
|
||||||
|
// completed.
|
||||||
|
// 4. Perform completionSteps.
|
||||||
|
|
||||||
|
// IMPORTANT: Since the sleep ops aren't guaranteed to resolve in the
|
||||||
|
// right order, whenever one resolves, we run through the scheduled
|
||||||
|
// timers list (which is in the order in which they were scheduled), and
|
||||||
|
// we call the callback for every timer which both:
|
||||||
|
// a) has resolved, and
|
||||||
|
// b) its timeout is lower than the lowest unresolved timeout found so
|
||||||
|
// far in the list.
|
||||||
|
|
||||||
|
timerObject.resolved = true;
|
||||||
|
|
||||||
|
let lowestUnresolvedTimeout = NumberPOSITIVE_INFINITY;
|
||||||
|
|
||||||
|
let currentEntry = scheduledTimers.head;
|
||||||
|
while (currentEntry !== null) {
|
||||||
|
if (currentEntry.millis < lowestUnresolvedTimeout) {
|
||||||
|
if (currentEntry.resolved) {
|
||||||
|
currentEntry.cb();
|
||||||
|
removeFromScheduledTimers(currentEntry);
|
||||||
|
} else {
|
||||||
|
lowestUnresolvedTimeout = currentEntry.millis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentEntry = currentEntry.next;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {ScheduledTimer} timerObj */
|
||||||
|
function removeFromScheduledTimers(timerObj) {
|
||||||
|
if (timerObj.prev !== null) {
|
||||||
|
timerObj.prev.next = timerObj.next;
|
||||||
|
} else {
|
||||||
|
assert(scheduledTimers.head === timerObj);
|
||||||
|
scheduledTimers.head = timerObj.next;
|
||||||
|
}
|
||||||
|
if (timerObj.next !== null) {
|
||||||
|
timerObj.next.prev = timerObj.prev;
|
||||||
|
} else {
|
||||||
|
assert(scheduledTimers.tail === timerObj);
|
||||||
|
scheduledTimers.tail = timerObj.prev;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
function checkThis(thisArg) {
|
||||||
|
if (thisArg !== null && thisArg !== undefined && thisArg !== globalThis) {
|
||||||
|
throw new TypeError("Illegal invocation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTimeout(callback, timeout = 0, ...args) {
|
||||||
|
checkThis(this);
|
||||||
|
if (typeof callback !== "function") {
|
||||||
|
callback = webidl.converters.DOMString(callback);
|
||||||
|
}
|
||||||
|
timeout = webidl.converters.long(timeout);
|
||||||
|
|
||||||
|
return initializeTimer(callback, timeout, args, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInterval(callback, timeout = 0, ...args) {
|
||||||
|
checkThis(this);
|
||||||
|
if (typeof callback !== "function") {
|
||||||
|
callback = webidl.converters.DOMString(callback);
|
||||||
|
}
|
||||||
|
timeout = webidl.converters.long(timeout);
|
||||||
|
|
||||||
|
return initializeTimer(callback, timeout, args, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearTimeout(id = 0) {
|
||||||
|
checkThis(this);
|
||||||
|
id = webidl.converters.long(id);
|
||||||
|
const timerInfo = MapPrototypeGet(activeTimers, id);
|
||||||
|
if (timerInfo !== undefined) {
|
||||||
|
core.tryClose(timerInfo.cancelRid);
|
||||||
|
MapPrototypeDelete(activeTimers, id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearInterval(id = 0) {
|
||||||
|
checkThis(this);
|
||||||
|
clearTimeout(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function refTimer(id) {
|
||||||
|
const timerInfo = MapPrototypeGet(activeTimers, id);
|
||||||
|
if (timerInfo === undefined || timerInfo.isRef) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timerInfo.isRef = true;
|
||||||
|
core.refOp(timerInfo.promiseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unrefTimer(id) {
|
||||||
|
const timerInfo = MapPrototypeGet(activeTimers, id);
|
||||||
|
if (timerInfo === undefined || !timerInfo.isRef) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
timerInfo.isRef = false;
|
||||||
|
core.unrefOp(timerInfo.promiseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
clearInterval,
|
||||||
|
clearTimeout,
|
||||||
|
handleTimerMacrotask,
|
||||||
|
opNow,
|
||||||
|
refTimer,
|
||||||
|
setInterval,
|
||||||
|
setTimeout,
|
||||||
|
unrefTimer,
|
||||||
|
};
|
||||||
|
|
|
@ -1,200 +1,205 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// @ts-check
|
// @ts-check
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
((window) => {
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = window.__bootstrap.webidl;
|
import {
|
||||||
const { Event, setIsTrusted, defineEventHandler } = window.__bootstrap.event;
|
defineEventHandler,
|
||||||
const { EventTarget, listenerCount } = window.__bootstrap.eventTarget;
|
Event,
|
||||||
const {
|
EventTarget,
|
||||||
SafeArrayIterator,
|
listenerCount,
|
||||||
SafeSetIterator,
|
setIsTrusted,
|
||||||
Set,
|
} from "internal:ext/web/02_event.js";
|
||||||
SetPrototypeAdd,
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
SetPrototypeDelete,
|
const {
|
||||||
Symbol,
|
SafeArrayIterator,
|
||||||
TypeError,
|
SafeSetIterator,
|
||||||
} = window.__bootstrap.primordials;
|
Set,
|
||||||
const { setTimeout, refTimer, unrefTimer } = window.__bootstrap.timers;
|
SetPrototypeAdd,
|
||||||
|
SetPrototypeDelete,
|
||||||
|
Symbol,
|
||||||
|
TypeError,
|
||||||
|
} = primordials;
|
||||||
|
import {
|
||||||
|
refTimer,
|
||||||
|
setTimeout,
|
||||||
|
unrefTimer,
|
||||||
|
} from "internal:ext/web/02_timers.js";
|
||||||
|
|
||||||
const add = Symbol("[[add]]");
|
const add = Symbol("[[add]]");
|
||||||
const signalAbort = Symbol("[[signalAbort]]");
|
const signalAbort = Symbol("[[signalAbort]]");
|
||||||
const remove = Symbol("[[remove]]");
|
const remove = Symbol("[[remove]]");
|
||||||
const abortReason = Symbol("[[abortReason]]");
|
const abortReason = Symbol("[[abortReason]]");
|
||||||
const abortAlgos = Symbol("[[abortAlgos]]");
|
const abortAlgos = Symbol("[[abortAlgos]]");
|
||||||
const signal = Symbol("[[signal]]");
|
const signal = Symbol("[[signal]]");
|
||||||
const timerId = Symbol("[[timerId]]");
|
const timerId = Symbol("[[timerId]]");
|
||||||
|
|
||||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||||
|
|
||||||
class AbortSignal extends EventTarget {
|
class AbortSignal extends EventTarget {
|
||||||
static abort(reason = undefined) {
|
static abort(reason = undefined) {
|
||||||
if (reason !== undefined) {
|
if (reason !== undefined) {
|
||||||
reason = webidl.converters.any(reason);
|
reason = webidl.converters.any(reason);
|
||||||
}
|
|
||||||
const signal = new AbortSignal(illegalConstructorKey);
|
|
||||||
signal[signalAbort](reason);
|
|
||||||
return signal;
|
|
||||||
}
|
|
||||||
|
|
||||||
static timeout(millis) {
|
|
||||||
const prefix = "Failed to call 'AbortSignal.timeout'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
millis = webidl.converters["unsigned long long"](millis, {
|
|
||||||
enforceRange: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const signal = new AbortSignal(illegalConstructorKey);
|
|
||||||
signal[timerId] = setTimeout(
|
|
||||||
() => {
|
|
||||||
signal[timerId] = null;
|
|
||||||
signal[signalAbort](
|
|
||||||
new DOMException("Signal timed out.", "TimeoutError"),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
millis,
|
|
||||||
);
|
|
||||||
unrefTimer(signal[timerId]);
|
|
||||||
return signal;
|
|
||||||
}
|
|
||||||
|
|
||||||
[add](algorithm) {
|
|
||||||
if (this.aborted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this[abortAlgos] === null) {
|
|
||||||
this[abortAlgos] = new Set();
|
|
||||||
}
|
|
||||||
SetPrototypeAdd(this[abortAlgos], algorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
[signalAbort](
|
|
||||||
reason = new DOMException("The signal has been aborted", "AbortError"),
|
|
||||||
) {
|
|
||||||
if (this.aborted) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this[abortReason] = reason;
|
|
||||||
if (this[abortAlgos] !== null) {
|
|
||||||
for (const algorithm of new SafeSetIterator(this[abortAlgos])) {
|
|
||||||
algorithm();
|
|
||||||
}
|
|
||||||
this[abortAlgos] = null;
|
|
||||||
}
|
|
||||||
const event = new Event("abort");
|
|
||||||
setIsTrusted(event, true);
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
[remove](algorithm) {
|
|
||||||
this[abortAlgos] && SetPrototypeDelete(this[abortAlgos], algorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(key = null) {
|
|
||||||
if (key != illegalConstructorKey) {
|
|
||||||
throw new TypeError("Illegal constructor.");
|
|
||||||
}
|
|
||||||
super();
|
|
||||||
this[abortReason] = undefined;
|
|
||||||
this[abortAlgos] = null;
|
|
||||||
this[timerId] = null;
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
get aborted() {
|
|
||||||
webidl.assertBranded(this, AbortSignalPrototype);
|
|
||||||
return this[abortReason] !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
get reason() {
|
|
||||||
webidl.assertBranded(this, AbortSignalPrototype);
|
|
||||||
return this[abortReason];
|
|
||||||
}
|
|
||||||
|
|
||||||
throwIfAborted() {
|
|
||||||
webidl.assertBranded(this, AbortSignalPrototype);
|
|
||||||
if (this[abortReason] !== undefined) {
|
|
||||||
throw this[abortReason];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// `addEventListener` and `removeEventListener` have to be overriden in
|
|
||||||
// order to have the timer block the event loop while there are listeners.
|
|
||||||
// `[add]` and `[remove]` don't ref and unref the timer because they can
|
|
||||||
// only be used by Deno internals, which use it to essentially cancel async
|
|
||||||
// ops which would block the event loop.
|
|
||||||
addEventListener(...args) {
|
|
||||||
super.addEventListener(...new SafeArrayIterator(args));
|
|
||||||
if (this[timerId] !== null && listenerCount(this, "abort") > 0) {
|
|
||||||
refTimer(this[timerId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeEventListener(...args) {
|
|
||||||
super.removeEventListener(...new SafeArrayIterator(args));
|
|
||||||
if (this[timerId] !== null && listenerCount(this, "abort") === 0) {
|
|
||||||
unrefTimer(this[timerId]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineEventHandler(AbortSignal.prototype, "abort");
|
|
||||||
|
|
||||||
webidl.configurePrototype(AbortSignal);
|
|
||||||
const AbortSignalPrototype = AbortSignal.prototype;
|
|
||||||
|
|
||||||
class AbortController {
|
|
||||||
[signal] = new AbortSignal(illegalConstructorKey);
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
get signal() {
|
|
||||||
webidl.assertBranded(this, AbortControllerPrototype);
|
|
||||||
return this[signal];
|
|
||||||
}
|
|
||||||
|
|
||||||
abort(reason) {
|
|
||||||
webidl.assertBranded(this, AbortControllerPrototype);
|
|
||||||
this[signal][signalAbort](reason);
|
|
||||||
}
|
}
|
||||||
|
const signal = new AbortSignal(illegalConstructorKey);
|
||||||
|
signal[signalAbort](reason);
|
||||||
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(AbortController);
|
static timeout(millis) {
|
||||||
const AbortControllerPrototype = AbortController.prototype;
|
const prefix = "Failed to call 'AbortSignal.timeout'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
millis = webidl.converters["unsigned long long"](millis, {
|
||||||
|
enforceRange: true,
|
||||||
|
});
|
||||||
|
|
||||||
webidl.converters["AbortSignal"] = webidl.createInterfaceConverter(
|
const signal = new AbortSignal(illegalConstructorKey);
|
||||||
"AbortSignal",
|
signal[timerId] = setTimeout(
|
||||||
AbortSignal.prototype,
|
() => {
|
||||||
);
|
signal[timerId] = null;
|
||||||
|
signal[signalAbort](
|
||||||
function newSignal() {
|
new DOMException("Signal timed out.", "TimeoutError"),
|
||||||
return new AbortSignal(illegalConstructorKey);
|
);
|
||||||
|
},
|
||||||
|
millis,
|
||||||
|
);
|
||||||
|
unrefTimer(signal[timerId]);
|
||||||
|
return signal;
|
||||||
}
|
}
|
||||||
|
|
||||||
function follow(followingSignal, parentSignal) {
|
[add](algorithm) {
|
||||||
if (followingSignal.aborted) {
|
if (this.aborted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (parentSignal.aborted) {
|
if (this[abortAlgos] === null) {
|
||||||
followingSignal[signalAbort](parentSignal.reason);
|
this[abortAlgos] = new Set();
|
||||||
} else {
|
}
|
||||||
parentSignal[add](() =>
|
SetPrototypeAdd(this[abortAlgos], algorithm);
|
||||||
followingSignal[signalAbort](parentSignal.reason)
|
}
|
||||||
);
|
|
||||||
|
[signalAbort](
|
||||||
|
reason = new DOMException("The signal has been aborted", "AbortError"),
|
||||||
|
) {
|
||||||
|
if (this.aborted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this[abortReason] = reason;
|
||||||
|
if (this[abortAlgos] !== null) {
|
||||||
|
for (const algorithm of new SafeSetIterator(this[abortAlgos])) {
|
||||||
|
algorithm();
|
||||||
|
}
|
||||||
|
this[abortAlgos] = null;
|
||||||
|
}
|
||||||
|
const event = new Event("abort");
|
||||||
|
setIsTrusted(event, true);
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
[remove](algorithm) {
|
||||||
|
this[abortAlgos] && SetPrototypeDelete(this[abortAlgos], algorithm);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(key = null) {
|
||||||
|
if (key != illegalConstructorKey) {
|
||||||
|
throw new TypeError("Illegal constructor.");
|
||||||
|
}
|
||||||
|
super();
|
||||||
|
this[abortReason] = undefined;
|
||||||
|
this[abortAlgos] = null;
|
||||||
|
this[timerId] = null;
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
}
|
||||||
|
|
||||||
|
get aborted() {
|
||||||
|
webidl.assertBranded(this, AbortSignalPrototype);
|
||||||
|
return this[abortReason] !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
get reason() {
|
||||||
|
webidl.assertBranded(this, AbortSignalPrototype);
|
||||||
|
return this[abortReason];
|
||||||
|
}
|
||||||
|
|
||||||
|
throwIfAborted() {
|
||||||
|
webidl.assertBranded(this, AbortSignalPrototype);
|
||||||
|
if (this[abortReason] !== undefined) {
|
||||||
|
throw this[abortReason];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.abortSignal = {
|
// `addEventListener` and `removeEventListener` have to be overriden in
|
||||||
AbortSignal,
|
// order to have the timer block the event loop while there are listeners.
|
||||||
AbortController,
|
// `[add]` and `[remove]` don't ref and unref the timer because they can
|
||||||
AbortSignalPrototype,
|
// only be used by Deno internals, which use it to essentially cancel async
|
||||||
add,
|
// ops which would block the event loop.
|
||||||
signalAbort,
|
addEventListener(...args) {
|
||||||
remove,
|
super.addEventListener(...new SafeArrayIterator(args));
|
||||||
follow,
|
if (this[timerId] !== null && listenerCount(this, "abort") > 0) {
|
||||||
newSignal,
|
refTimer(this[timerId]);
|
||||||
};
|
}
|
||||||
})(this);
|
}
|
||||||
|
|
||||||
|
removeEventListener(...args) {
|
||||||
|
super.removeEventListener(...new SafeArrayIterator(args));
|
||||||
|
if (this[timerId] !== null && listenerCount(this, "abort") === 0) {
|
||||||
|
unrefTimer(this[timerId]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineEventHandler(AbortSignal.prototype, "abort");
|
||||||
|
|
||||||
|
webidl.configurePrototype(AbortSignal);
|
||||||
|
const AbortSignalPrototype = AbortSignal.prototype;
|
||||||
|
|
||||||
|
class AbortController {
|
||||||
|
[signal] = new AbortSignal(illegalConstructorKey);
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
}
|
||||||
|
|
||||||
|
get signal() {
|
||||||
|
webidl.assertBranded(this, AbortControllerPrototype);
|
||||||
|
return this[signal];
|
||||||
|
}
|
||||||
|
|
||||||
|
abort(reason) {
|
||||||
|
webidl.assertBranded(this, AbortControllerPrototype);
|
||||||
|
this[signal][signalAbort](reason);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(AbortController);
|
||||||
|
const AbortControllerPrototype = AbortController.prototype;
|
||||||
|
|
||||||
|
webidl.converters["AbortSignal"] = webidl.createInterfaceConverter(
|
||||||
|
"AbortSignal",
|
||||||
|
AbortSignal.prototype,
|
||||||
|
);
|
||||||
|
|
||||||
|
function newSignal() {
|
||||||
|
return new AbortSignal(illegalConstructorKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function follow(followingSignal, parentSignal) {
|
||||||
|
if (followingSignal.aborted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (parentSignal.aborted) {
|
||||||
|
followingSignal[signalAbort](parentSignal.reason);
|
||||||
|
} else {
|
||||||
|
parentSignal[add](() => followingSignal[signalAbort](parentSignal.reason));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
AbortController,
|
||||||
|
AbortSignal,
|
||||||
|
AbortSignalPrototype,
|
||||||
|
add,
|
||||||
|
follow,
|
||||||
|
newSignal,
|
||||||
|
remove,
|
||||||
|
signalAbort,
|
||||||
|
};
|
||||||
|
|
|
@ -1,79 +1,83 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// @ts-check
|
// @ts-check
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
((window) => {
|
import { EventTarget } from "internal:ext/web/02_event.js";
|
||||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
Symbol,
|
Symbol,
|
||||||
SymbolToStringTag,
|
SymbolToStringTag,
|
||||||
TypeError,
|
TypeError,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
|
|
||||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||||
|
|
||||||
class Window extends EventTarget {
|
class Window extends EventTarget {
|
||||||
constructor(key = null) {
|
constructor(key = null) {
|
||||||
if (key !== illegalConstructorKey) {
|
if (key !== illegalConstructorKey) {
|
||||||
throw new TypeError("Illegal constructor.");
|
throw new TypeError("Illegal constructor.");
|
||||||
}
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
get [SymbolToStringTag]() {
|
|
||||||
return "Window";
|
|
||||||
}
|
}
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
class WorkerGlobalScope extends EventTarget {
|
get [SymbolToStringTag]() {
|
||||||
constructor(key = null) {
|
return "Window";
|
||||||
if (key != illegalConstructorKey) {
|
}
|
||||||
throw new TypeError("Illegal constructor.");
|
}
|
||||||
}
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
get [SymbolToStringTag]() {
|
class WorkerGlobalScope extends EventTarget {
|
||||||
return "WorkerGlobalScope";
|
constructor(key = null) {
|
||||||
|
if (key != illegalConstructorKey) {
|
||||||
|
throw new TypeError("Illegal constructor.");
|
||||||
}
|
}
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
get [SymbolToStringTag]() {
|
||||||
constructor(key = null) {
|
return "WorkerGlobalScope";
|
||||||
if (key != illegalConstructorKey) {
|
}
|
||||||
throw new TypeError("Illegal constructor.");
|
}
|
||||||
}
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
get [SymbolToStringTag]() {
|
class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
||||||
return "DedicatedWorkerGlobalScope";
|
constructor(key = null) {
|
||||||
|
if (key != illegalConstructorKey) {
|
||||||
|
throw new TypeError("Illegal constructor.");
|
||||||
}
|
}
|
||||||
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.globalInterfaces = {
|
get [SymbolToStringTag]() {
|
||||||
DedicatedWorkerGlobalScope,
|
return "DedicatedWorkerGlobalScope";
|
||||||
Window,
|
}
|
||||||
WorkerGlobalScope,
|
}
|
||||||
dedicatedWorkerGlobalScopeConstructorDescriptor: {
|
|
||||||
configurable: true,
|
const dedicatedWorkerGlobalScopeConstructorDescriptor = {
|
||||||
enumerable: false,
|
configurable: true,
|
||||||
value: DedicatedWorkerGlobalScope,
|
enumerable: false,
|
||||||
writable: true,
|
value: DedicatedWorkerGlobalScope,
|
||||||
},
|
writable: true,
|
||||||
windowConstructorDescriptor: {
|
};
|
||||||
configurable: true,
|
|
||||||
enumerable: false,
|
const windowConstructorDescriptor = {
|
||||||
value: Window,
|
configurable: true,
|
||||||
writable: true,
|
enumerable: false,
|
||||||
},
|
value: Window,
|
||||||
workerGlobalScopeConstructorDescriptor: {
|
writable: true,
|
||||||
configurable: true,
|
};
|
||||||
enumerable: false,
|
|
||||||
value: WorkerGlobalScope,
|
const workerGlobalScopeConstructorDescriptor = {
|
||||||
writable: true,
|
configurable: true,
|
||||||
},
|
enumerable: false,
|
||||||
};
|
value: WorkerGlobalScope,
|
||||||
})(this);
|
writable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
DedicatedWorkerGlobalScope,
|
||||||
|
dedicatedWorkerGlobalScopeConstructorDescriptor,
|
||||||
|
Window,
|
||||||
|
windowConstructorDescriptor,
|
||||||
|
WorkerGlobalScope,
|
||||||
|
workerGlobalScopeConstructorDescriptor,
|
||||||
|
};
|
||||||
|
|
|
@ -6,68 +6,62 @@
|
||||||
/// <reference path="../web/internal.d.ts" />
|
/// <reference path="../web/internal.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
TypeErrorPrototype,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
/**
|
||||||
const core = Deno.core;
|
* @param {string} data
|
||||||
const ops = core.ops;
|
* @returns {string}
|
||||||
const webidl = window.__bootstrap.webidl;
|
*/
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
function atob(data) {
|
||||||
const {
|
const prefix = "Failed to execute 'atob'";
|
||||||
ObjectPrototypeIsPrototypeOf,
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
TypeErrorPrototype,
|
data = webidl.converters.DOMString(data, {
|
||||||
} = window.__bootstrap.primordials;
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
/**
|
});
|
||||||
* @param {string} data
|
try {
|
||||||
* @returns {string}
|
return ops.op_base64_atob(data);
|
||||||
*/
|
} catch (e) {
|
||||||
function atob(data) {
|
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
||||||
const prefix = "Failed to execute 'atob'";
|
throw new DOMException(
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
"Failed to decode base64: invalid character",
|
||||||
data = webidl.converters.DOMString(data, {
|
"InvalidCharacterError",
|
||||||
prefix,
|
);
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
return ops.op_base64_atob(data);
|
|
||||||
} catch (e) {
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
|
||||||
throw new DOMException(
|
|
||||||
"Failed to decode base64: invalid character",
|
|
||||||
"InvalidCharacterError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} data
|
* @param {string} data
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function btoa(data) {
|
function btoa(data) {
|
||||||
const prefix = "Failed to execute 'btoa'";
|
const prefix = "Failed to execute 'btoa'";
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
data = webidl.converters.DOMString(data, {
|
data = webidl.converters.DOMString(data, {
|
||||||
prefix,
|
prefix,
|
||||||
context: "Argument 1",
|
context: "Argument 1",
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
return ops.op_base64_btoa(data);
|
return ops.op_base64_btoa(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
||||||
throw new DOMException(
|
throw new DOMException(
|
||||||
"The string to be encoded contains characters outside of the Latin1 range.",
|
"The string to be encoded contains characters outside of the Latin1 range.",
|
||||||
"InvalidCharacterError",
|
"InvalidCharacterError",
|
||||||
);
|
);
|
||||||
}
|
|
||||||
throw e;
|
|
||||||
}
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
window.__bootstrap.base64 = {
|
export { atob, btoa };
|
||||||
atob,
|
|
||||||
btoa,
|
|
||||||
};
|
|
||||||
})(globalThis);
|
|
||||||
|
|
11627
ext/web/06_streams.js
11627
ext/web/06_streams.js
File diff suppressed because it is too large
Load diff
|
@ -9,437 +9,434 @@
|
||||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
PromiseReject,
|
||||||
|
PromiseResolve,
|
||||||
|
// TODO(lucacasonato): add SharedArrayBuffer to primordials
|
||||||
|
// SharedArrayBufferPrototype
|
||||||
|
StringPrototypeCharCodeAt,
|
||||||
|
StringPrototypeSlice,
|
||||||
|
TypedArrayPrototypeSubarray,
|
||||||
|
Uint8Array,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
ArrayBufferIsView,
|
||||||
|
Uint32Array,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
class TextDecoder {
|
||||||
const core = Deno.core;
|
/** @type {string} */
|
||||||
const ops = core.ops;
|
#encoding;
|
||||||
const webidl = window.__bootstrap.webidl;
|
/** @type {boolean} */
|
||||||
const {
|
#fatal;
|
||||||
PromiseReject,
|
/** @type {boolean} */
|
||||||
PromiseResolve,
|
#ignoreBOM;
|
||||||
// TODO(lucacasonato): add SharedArrayBuffer to primordials
|
/** @type {boolean} */
|
||||||
// SharedArrayBufferPrototype
|
#utf8SinglePass;
|
||||||
StringPrototypeCharCodeAt,
|
|
||||||
StringPrototypeSlice,
|
|
||||||
TypedArrayPrototypeSubarray,
|
|
||||||
Uint8Array,
|
|
||||||
ObjectPrototypeIsPrototypeOf,
|
|
||||||
ArrayBufferIsView,
|
|
||||||
Uint32Array,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
class TextDecoder {
|
/** @type {number | null} */
|
||||||
/** @type {string} */
|
#rid = null;
|
||||||
#encoding;
|
|
||||||
/** @type {boolean} */
|
|
||||||
#fatal;
|
|
||||||
/** @type {boolean} */
|
|
||||||
#ignoreBOM;
|
|
||||||
/** @type {boolean} */
|
|
||||||
#utf8SinglePass;
|
|
||||||
|
|
||||||
/** @type {number | null} */
|
/**
|
||||||
#rid = null;
|
* @param {string} label
|
||||||
|
* @param {TextDecoderOptions} options
|
||||||
/**
|
*/
|
||||||
* @param {string} label
|
constructor(label = "utf-8", options = {}) {
|
||||||
* @param {TextDecoderOptions} options
|
const prefix = "Failed to construct 'TextDecoder'";
|
||||||
*/
|
label = webidl.converters.DOMString(label, {
|
||||||
constructor(label = "utf-8", options = {}) {
|
prefix,
|
||||||
const prefix = "Failed to construct 'TextDecoder'";
|
context: "Argument 1",
|
||||||
label = webidl.converters.DOMString(label, {
|
});
|
||||||
prefix,
|
options = webidl.converters.TextDecoderOptions(options, {
|
||||||
context: "Argument 1",
|
prefix,
|
||||||
});
|
context: "Argument 2",
|
||||||
options = webidl.converters.TextDecoderOptions(options, {
|
});
|
||||||
prefix,
|
const encoding = ops.op_encoding_normalize_label(label);
|
||||||
context: "Argument 2",
|
this.#encoding = encoding;
|
||||||
});
|
this.#fatal = options.fatal;
|
||||||
const encoding = ops.op_encoding_normalize_label(label);
|
this.#ignoreBOM = options.ignoreBOM;
|
||||||
this.#encoding = encoding;
|
this.#utf8SinglePass = encoding === "utf-8" && !options.fatal;
|
||||||
this.#fatal = options.fatal;
|
this[webidl.brand] = webidl.brand;
|
||||||
this.#ignoreBOM = options.ignoreBOM;
|
|
||||||
this.#utf8SinglePass = encoding === "utf-8" && !options.fatal;
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {string} */
|
|
||||||
get encoding() {
|
|
||||||
webidl.assertBranded(this, TextDecoderPrototype);
|
|
||||||
return this.#encoding;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {boolean} */
|
|
||||||
get fatal() {
|
|
||||||
webidl.assertBranded(this, TextDecoderPrototype);
|
|
||||||
return this.#fatal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {boolean} */
|
|
||||||
get ignoreBOM() {
|
|
||||||
webidl.assertBranded(this, TextDecoderPrototype);
|
|
||||||
return this.#ignoreBOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {BufferSource} [input]
|
|
||||||
* @param {TextDecodeOptions} options
|
|
||||||
*/
|
|
||||||
decode(input = new Uint8Array(), options = undefined) {
|
|
||||||
webidl.assertBranded(this, TextDecoderPrototype);
|
|
||||||
const prefix = "Failed to execute 'decode' on 'TextDecoder'";
|
|
||||||
if (input !== undefined) {
|
|
||||||
input = webidl.converters.BufferSource(input, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
allowShared: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
let stream = false;
|
|
||||||
if (options !== undefined) {
|
|
||||||
options = webidl.converters.TextDecodeOptions(options, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
stream = options.stream;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Note from spec: implementations are strongly encouraged to use an implementation strategy that avoids this copy.
|
|
||||||
// When doing so they will have to make sure that changes to input do not affect future calls to decode().
|
|
||||||
if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(
|
|
||||||
// deno-lint-ignore prefer-primordials
|
|
||||||
SharedArrayBuffer.prototype,
|
|
||||||
input || input.buffer,
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
// We clone the data into a non-shared ArrayBuffer so we can pass it
|
|
||||||
// to Rust.
|
|
||||||
// `input` is now a Uint8Array, and calling the TypedArray constructor
|
|
||||||
// with a TypedArray argument copies the data.
|
|
||||||
if (ArrayBufferIsView(input)) {
|
|
||||||
input = new Uint8Array(
|
|
||||||
input.buffer,
|
|
||||||
input.byteOffset,
|
|
||||||
input.byteLength,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
input = new Uint8Array(input);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fast path for single pass encoding.
|
|
||||||
if (!stream && this.#rid === null) {
|
|
||||||
// Fast path for utf8 single pass encoding.
|
|
||||||
if (this.#utf8SinglePass) {
|
|
||||||
return ops.op_encoding_decode_utf8(input, this.#ignoreBOM);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ops.op_encoding_decode_single(
|
|
||||||
input,
|
|
||||||
this.#encoding,
|
|
||||||
this.#fatal,
|
|
||||||
this.#ignoreBOM,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.#rid === null) {
|
|
||||||
this.#rid = ops.op_encoding_new_decoder(
|
|
||||||
this.#encoding,
|
|
||||||
this.#fatal,
|
|
||||||
this.#ignoreBOM,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return ops.op_encoding_decode(input, this.#rid, stream);
|
|
||||||
} finally {
|
|
||||||
if (!stream && this.#rid !== null) {
|
|
||||||
core.close(this.#rid);
|
|
||||||
this.#rid = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(TextDecoder);
|
/** @returns {string} */
|
||||||
const TextDecoderPrototype = TextDecoder.prototype;
|
get encoding() {
|
||||||
|
webidl.assertBranded(this, TextDecoderPrototype);
|
||||||
|
return this.#encoding;
|
||||||
|
}
|
||||||
|
|
||||||
class TextEncoder {
|
/** @returns {boolean} */
|
||||||
constructor() {
|
get fatal() {
|
||||||
this[webidl.brand] = webidl.brand;
|
webidl.assertBranded(this, TextDecoderPrototype);
|
||||||
}
|
return this.#fatal;
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {string} */
|
/** @returns {boolean} */
|
||||||
get encoding() {
|
get ignoreBOM() {
|
||||||
webidl.assertBranded(this, TextEncoderPrototype);
|
webidl.assertBranded(this, TextDecoderPrototype);
|
||||||
return "utf-8";
|
return this.#ignoreBOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} input
|
* @param {BufferSource} [input]
|
||||||
* @returns {Uint8Array}
|
* @param {TextDecodeOptions} options
|
||||||
*/
|
*/
|
||||||
encode(input = "") {
|
decode(input = new Uint8Array(), options = undefined) {
|
||||||
webidl.assertBranded(this, TextEncoderPrototype);
|
webidl.assertBranded(this, TextDecoderPrototype);
|
||||||
const prefix = "Failed to execute 'encode' on 'TextEncoder'";
|
const prefix = "Failed to execute 'decode' on 'TextDecoder'";
|
||||||
// The WebIDL type of `input` is `USVString`, but `core.encode` already
|
if (input !== undefined) {
|
||||||
// converts lone surrogates to the replacement character.
|
input = webidl.converters.BufferSource(input, {
|
||||||
input = webidl.converters.DOMString(input, {
|
|
||||||
prefix,
|
prefix,
|
||||||
context: "Argument 1",
|
context: "Argument 1",
|
||||||
});
|
|
||||||
return core.encode(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} source
|
|
||||||
* @param {Uint8Array} destination
|
|
||||||
* @returns {TextEncoderEncodeIntoResult}
|
|
||||||
*/
|
|
||||||
encodeInto(source, destination) {
|
|
||||||
webidl.assertBranded(this, TextEncoderPrototype);
|
|
||||||
const prefix = "Failed to execute 'encodeInto' on 'TextEncoder'";
|
|
||||||
// The WebIDL type of `source` is `USVString`, but the ops bindings
|
|
||||||
// already convert lone surrogates to the replacement character.
|
|
||||||
source = webidl.converters.DOMString(source, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
destination = webidl.converters.Uint8Array(destination, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
allowShared: true,
|
allowShared: true,
|
||||||
});
|
});
|
||||||
ops.op_encoding_encode_into(source, destination, encodeIntoBuf);
|
|
||||||
return {
|
|
||||||
read: encodeIntoBuf[0],
|
|
||||||
written: encodeIntoBuf[1],
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
let stream = false;
|
||||||
|
if (options !== undefined) {
|
||||||
const encodeIntoBuf = new Uint32Array(2);
|
options = webidl.converters.TextDecodeOptions(options, {
|
||||||
|
|
||||||
webidl.configurePrototype(TextEncoder);
|
|
||||||
const TextEncoderPrototype = TextEncoder.prototype;
|
|
||||||
|
|
||||||
class TextDecoderStream {
|
|
||||||
/** @type {TextDecoder} */
|
|
||||||
#decoder;
|
|
||||||
/** @type {TransformStream<BufferSource, string>} */
|
|
||||||
#transform;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} label
|
|
||||||
* @param {TextDecoderOptions} options
|
|
||||||
*/
|
|
||||||
constructor(label = "utf-8", options = {}) {
|
|
||||||
const prefix = "Failed to construct 'TextDecoderStream'";
|
|
||||||
label = webidl.converters.DOMString(label, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
options = webidl.converters.TextDecoderOptions(options, {
|
|
||||||
prefix,
|
prefix,
|
||||||
context: "Argument 2",
|
context: "Argument 2",
|
||||||
});
|
});
|
||||||
this.#decoder = new TextDecoder(label, options);
|
stream = options.stream;
|
||||||
this.#transform = new TransformStream({
|
|
||||||
// The transform and flush functions need access to TextDecoderStream's
|
|
||||||
// `this`, so they are defined as functions rather than methods.
|
|
||||||
transform: (chunk, controller) => {
|
|
||||||
try {
|
|
||||||
chunk = webidl.converters.BufferSource(chunk, {
|
|
||||||
allowShared: true,
|
|
||||||
});
|
|
||||||
const decoded = this.#decoder.decode(chunk, { stream: true });
|
|
||||||
if (decoded) {
|
|
||||||
controller.enqueue(decoded);
|
|
||||||
}
|
|
||||||
return PromiseResolve();
|
|
||||||
} catch (err) {
|
|
||||||
return PromiseReject(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
flush: (controller) => {
|
|
||||||
try {
|
|
||||||
const final = this.#decoder.decode();
|
|
||||||
if (final) {
|
|
||||||
controller.enqueue(final);
|
|
||||||
}
|
|
||||||
return PromiseResolve();
|
|
||||||
} catch (err) {
|
|
||||||
return PromiseReject(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {string} */
|
try {
|
||||||
get encoding() {
|
// Note from spec: implementations are strongly encouraged to use an implementation strategy that avoids this copy.
|
||||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
// When doing so they will have to make sure that changes to input do not affect future calls to decode().
|
||||||
return this.#decoder.encoding;
|
if (
|
||||||
}
|
ObjectPrototypeIsPrototypeOf(
|
||||||
|
// deno-lint-ignore prefer-primordials
|
||||||
|
SharedArrayBuffer.prototype,
|
||||||
|
input || input.buffer,
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
// We clone the data into a non-shared ArrayBuffer so we can pass it
|
||||||
|
// to Rust.
|
||||||
|
// `input` is now a Uint8Array, and calling the TypedArray constructor
|
||||||
|
// with a TypedArray argument copies the data.
|
||||||
|
if (ArrayBufferIsView(input)) {
|
||||||
|
input = new Uint8Array(
|
||||||
|
input.buffer,
|
||||||
|
input.byteOffset,
|
||||||
|
input.byteLength,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
input = new Uint8Array(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {boolean} */
|
// Fast path for single pass encoding.
|
||||||
get fatal() {
|
if (!stream && this.#rid === null) {
|
||||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
// Fast path for utf8 single pass encoding.
|
||||||
return this.#decoder.fatal;
|
if (this.#utf8SinglePass) {
|
||||||
}
|
return ops.op_encoding_decode_utf8(input, this.#ignoreBOM);
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {boolean} */
|
return ops.op_encoding_decode_single(
|
||||||
get ignoreBOM() {
|
input,
|
||||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
this.#encoding,
|
||||||
return this.#decoder.ignoreBOM;
|
this.#fatal,
|
||||||
}
|
this.#ignoreBOM,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/** @returns {ReadableStream<string>} */
|
if (this.#rid === null) {
|
||||||
get readable() {
|
this.#rid = ops.op_encoding_new_decoder(
|
||||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
this.#encoding,
|
||||||
return this.#transform.readable;
|
this.#fatal,
|
||||||
}
|
this.#ignoreBOM,
|
||||||
|
);
|
||||||
/** @returns {WritableStream<BufferSource>} */
|
}
|
||||||
get writable() {
|
return ops.op_encoding_decode(input, this.#rid, stream);
|
||||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
} finally {
|
||||||
return this.#transform.writable;
|
if (!stream && this.#rid !== null) {
|
||||||
|
core.close(this.#rid);
|
||||||
|
this.#rid = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(TextDecoderStream);
|
webidl.configurePrototype(TextDecoder);
|
||||||
const TextDecoderStreamPrototype = TextDecoderStream.prototype;
|
const TextDecoderPrototype = TextDecoder.prototype;
|
||||||
|
|
||||||
class TextEncoderStream {
|
class TextEncoder {
|
||||||
/** @type {string | null} */
|
constructor() {
|
||||||
#pendingHighSurrogate = null;
|
this[webidl.brand] = webidl.brand;
|
||||||
/** @type {TransformStream<string, Uint8Array>} */
|
|
||||||
#transform;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
this.#transform = new TransformStream({
|
|
||||||
// The transform and flush functions need access to TextEncoderStream's
|
|
||||||
// `this`, so they are defined as functions rather than methods.
|
|
||||||
transform: (chunk, controller) => {
|
|
||||||
try {
|
|
||||||
chunk = webidl.converters.DOMString(chunk);
|
|
||||||
if (chunk === "") {
|
|
||||||
return PromiseResolve();
|
|
||||||
}
|
|
||||||
if (this.#pendingHighSurrogate !== null) {
|
|
||||||
chunk = this.#pendingHighSurrogate + chunk;
|
|
||||||
}
|
|
||||||
const lastCodeUnit = StringPrototypeCharCodeAt(
|
|
||||||
chunk,
|
|
||||||
chunk.length - 1,
|
|
||||||
);
|
|
||||||
if (0xD800 <= lastCodeUnit && lastCodeUnit <= 0xDBFF) {
|
|
||||||
this.#pendingHighSurrogate = StringPrototypeSlice(chunk, -1);
|
|
||||||
chunk = StringPrototypeSlice(chunk, 0, -1);
|
|
||||||
} else {
|
|
||||||
this.#pendingHighSurrogate = null;
|
|
||||||
}
|
|
||||||
if (chunk) {
|
|
||||||
controller.enqueue(core.encode(chunk));
|
|
||||||
}
|
|
||||||
return PromiseResolve();
|
|
||||||
} catch (err) {
|
|
||||||
return PromiseReject(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
flush: (controller) => {
|
|
||||||
try {
|
|
||||||
if (this.#pendingHighSurrogate !== null) {
|
|
||||||
controller.enqueue(new Uint8Array([0xEF, 0xBF, 0xBD]));
|
|
||||||
}
|
|
||||||
return PromiseResolve();
|
|
||||||
} catch (err) {
|
|
||||||
return PromiseReject(err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {string} */
|
|
||||||
get encoding() {
|
|
||||||
webidl.assertBranded(this, TextEncoderStreamPrototype);
|
|
||||||
return "utf-8";
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {ReadableStream<Uint8Array>} */
|
|
||||||
get readable() {
|
|
||||||
webidl.assertBranded(this, TextEncoderStreamPrototype);
|
|
||||||
return this.#transform.readable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @returns {WritableStream<string>} */
|
|
||||||
get writable() {
|
|
||||||
webidl.assertBranded(this, TextEncoderStreamPrototype);
|
|
||||||
return this.#transform.writable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(TextEncoderStream);
|
/** @returns {string} */
|
||||||
const TextEncoderStreamPrototype = TextEncoderStream.prototype;
|
get encoding() {
|
||||||
|
webidl.assertBranded(this, TextEncoderPrototype);
|
||||||
webidl.converters.TextDecoderOptions = webidl.createDictionaryConverter(
|
return "utf-8";
|
||||||
"TextDecoderOptions",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: "fatal",
|
|
||||||
converter: webidl.converters.boolean,
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "ignoreBOM",
|
|
||||||
converter: webidl.converters.boolean,
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
webidl.converters.TextDecodeOptions = webidl.createDictionaryConverter(
|
|
||||||
"TextDecodeOptions",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: "stream",
|
|
||||||
converter: webidl.converters.boolean,
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Uint8Array} bytes
|
|
||||||
*/
|
|
||||||
function decode(bytes, encoding) {
|
|
||||||
const BOMEncoding = BOMSniff(bytes);
|
|
||||||
if (BOMEncoding !== null) {
|
|
||||||
encoding = BOMEncoding;
|
|
||||||
const start = BOMEncoding === "UTF-8" ? 3 : 2;
|
|
||||||
bytes = TypedArrayPrototypeSubarray(bytes, start);
|
|
||||||
}
|
|
||||||
return new TextDecoder(encoding).decode(bytes);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Uint8Array} bytes
|
* @param {string} input
|
||||||
|
* @returns {Uint8Array}
|
||||||
*/
|
*/
|
||||||
function BOMSniff(bytes) {
|
encode(input = "") {
|
||||||
if (bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) {
|
webidl.assertBranded(this, TextEncoderPrototype);
|
||||||
return "UTF-8";
|
const prefix = "Failed to execute 'encode' on 'TextEncoder'";
|
||||||
}
|
// The WebIDL type of `input` is `USVString`, but `core.encode` already
|
||||||
if (bytes[0] === 0xFE && bytes[1] === 0xFF) return "UTF-16BE";
|
// converts lone surrogates to the replacement character.
|
||||||
if (bytes[0] === 0xFF && bytes[1] === 0xFE) return "UTF-16LE";
|
input = webidl.converters.DOMString(input, {
|
||||||
return null;
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
return core.encode(input);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.encoding = {
|
/**
|
||||||
TextEncoder,
|
* @param {string} source
|
||||||
TextDecoder,
|
* @param {Uint8Array} destination
|
||||||
TextEncoderStream,
|
* @returns {TextEncoderEncodeIntoResult}
|
||||||
TextDecoderStream,
|
*/
|
||||||
decode,
|
encodeInto(source, destination) {
|
||||||
};
|
webidl.assertBranded(this, TextEncoderPrototype);
|
||||||
})(this);
|
const prefix = "Failed to execute 'encodeInto' on 'TextEncoder'";
|
||||||
|
// The WebIDL type of `source` is `USVString`, but the ops bindings
|
||||||
|
// already convert lone surrogates to the replacement character.
|
||||||
|
source = webidl.converters.DOMString(source, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
destination = webidl.converters.Uint8Array(destination, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
allowShared: true,
|
||||||
|
});
|
||||||
|
ops.op_encoding_encode_into(source, destination, encodeIntoBuf);
|
||||||
|
return {
|
||||||
|
read: encodeIntoBuf[0],
|
||||||
|
written: encodeIntoBuf[1],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const encodeIntoBuf = new Uint32Array(2);
|
||||||
|
|
||||||
|
webidl.configurePrototype(TextEncoder);
|
||||||
|
const TextEncoderPrototype = TextEncoder.prototype;
|
||||||
|
|
||||||
|
class TextDecoderStream {
|
||||||
|
/** @type {TextDecoder} */
|
||||||
|
#decoder;
|
||||||
|
/** @type {TransformStream<BufferSource, string>} */
|
||||||
|
#transform;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} label
|
||||||
|
* @param {TextDecoderOptions} options
|
||||||
|
*/
|
||||||
|
constructor(label = "utf-8", options = {}) {
|
||||||
|
const prefix = "Failed to construct 'TextDecoderStream'";
|
||||||
|
label = webidl.converters.DOMString(label, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
options = webidl.converters.TextDecoderOptions(options, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
this.#decoder = new TextDecoder(label, options);
|
||||||
|
this.#transform = new TransformStream({
|
||||||
|
// The transform and flush functions need access to TextDecoderStream's
|
||||||
|
// `this`, so they are defined as functions rather than methods.
|
||||||
|
transform: (chunk, controller) => {
|
||||||
|
try {
|
||||||
|
chunk = webidl.converters.BufferSource(chunk, {
|
||||||
|
allowShared: true,
|
||||||
|
});
|
||||||
|
const decoded = this.#decoder.decode(chunk, { stream: true });
|
||||||
|
if (decoded) {
|
||||||
|
controller.enqueue(decoded);
|
||||||
|
}
|
||||||
|
return PromiseResolve();
|
||||||
|
} catch (err) {
|
||||||
|
return PromiseReject(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
flush: (controller) => {
|
||||||
|
try {
|
||||||
|
const final = this.#decoder.decode();
|
||||||
|
if (final) {
|
||||||
|
controller.enqueue(final);
|
||||||
|
}
|
||||||
|
return PromiseResolve();
|
||||||
|
} catch (err) {
|
||||||
|
return PromiseReject(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {string} */
|
||||||
|
get encoding() {
|
||||||
|
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||||
|
return this.#decoder.encoding;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
get fatal() {
|
||||||
|
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||||
|
return this.#decoder.fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {boolean} */
|
||||||
|
get ignoreBOM() {
|
||||||
|
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||||
|
return this.#decoder.ignoreBOM;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {ReadableStream<string>} */
|
||||||
|
get readable() {
|
||||||
|
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||||
|
return this.#transform.readable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {WritableStream<BufferSource>} */
|
||||||
|
get writable() {
|
||||||
|
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||||
|
return this.#transform.writable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(TextDecoderStream);
|
||||||
|
const TextDecoderStreamPrototype = TextDecoderStream.prototype;
|
||||||
|
|
||||||
|
class TextEncoderStream {
|
||||||
|
/** @type {string | null} */
|
||||||
|
#pendingHighSurrogate = null;
|
||||||
|
/** @type {TransformStream<string, Uint8Array>} */
|
||||||
|
#transform;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.#transform = new TransformStream({
|
||||||
|
// The transform and flush functions need access to TextEncoderStream's
|
||||||
|
// `this`, so they are defined as functions rather than methods.
|
||||||
|
transform: (chunk, controller) => {
|
||||||
|
try {
|
||||||
|
chunk = webidl.converters.DOMString(chunk);
|
||||||
|
if (chunk === "") {
|
||||||
|
return PromiseResolve();
|
||||||
|
}
|
||||||
|
if (this.#pendingHighSurrogate !== null) {
|
||||||
|
chunk = this.#pendingHighSurrogate + chunk;
|
||||||
|
}
|
||||||
|
const lastCodeUnit = StringPrototypeCharCodeAt(
|
||||||
|
chunk,
|
||||||
|
chunk.length - 1,
|
||||||
|
);
|
||||||
|
if (0xD800 <= lastCodeUnit && lastCodeUnit <= 0xDBFF) {
|
||||||
|
this.#pendingHighSurrogate = StringPrototypeSlice(chunk, -1);
|
||||||
|
chunk = StringPrototypeSlice(chunk, 0, -1);
|
||||||
|
} else {
|
||||||
|
this.#pendingHighSurrogate = null;
|
||||||
|
}
|
||||||
|
if (chunk) {
|
||||||
|
controller.enqueue(core.encode(chunk));
|
||||||
|
}
|
||||||
|
return PromiseResolve();
|
||||||
|
} catch (err) {
|
||||||
|
return PromiseReject(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
flush: (controller) => {
|
||||||
|
try {
|
||||||
|
if (this.#pendingHighSurrogate !== null) {
|
||||||
|
controller.enqueue(new Uint8Array([0xEF, 0xBF, 0xBD]));
|
||||||
|
}
|
||||||
|
return PromiseResolve();
|
||||||
|
} catch (err) {
|
||||||
|
return PromiseReject(err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {string} */
|
||||||
|
get encoding() {
|
||||||
|
webidl.assertBranded(this, TextEncoderStreamPrototype);
|
||||||
|
return "utf-8";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {ReadableStream<Uint8Array>} */
|
||||||
|
get readable() {
|
||||||
|
webidl.assertBranded(this, TextEncoderStreamPrototype);
|
||||||
|
return this.#transform.readable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {WritableStream<string>} */
|
||||||
|
get writable() {
|
||||||
|
webidl.assertBranded(this, TextEncoderStreamPrototype);
|
||||||
|
return this.#transform.writable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(TextEncoderStream);
|
||||||
|
const TextEncoderStreamPrototype = TextEncoderStream.prototype;
|
||||||
|
|
||||||
|
webidl.converters.TextDecoderOptions = webidl.createDictionaryConverter(
|
||||||
|
"TextDecoderOptions",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: "fatal",
|
||||||
|
converter: webidl.converters.boolean,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "ignoreBOM",
|
||||||
|
converter: webidl.converters.boolean,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
webidl.converters.TextDecodeOptions = webidl.createDictionaryConverter(
|
||||||
|
"TextDecodeOptions",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: "stream",
|
||||||
|
converter: webidl.converters.boolean,
|
||||||
|
defaultValue: false,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} bytes
|
||||||
|
*/
|
||||||
|
function decode(bytes, encoding) {
|
||||||
|
const BOMEncoding = BOMSniff(bytes);
|
||||||
|
if (BOMEncoding !== null) {
|
||||||
|
encoding = BOMEncoding;
|
||||||
|
const start = BOMEncoding === "UTF-8" ? 3 : 2;
|
||||||
|
bytes = TypedArrayPrototypeSubarray(bytes, start);
|
||||||
|
}
|
||||||
|
return new TextDecoder(encoding).decode(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Uint8Array} bytes
|
||||||
|
*/
|
||||||
|
function BOMSniff(bytes) {
|
||||||
|
if (bytes[0] === 0xEF && bytes[1] === 0xBB && bytes[2] === 0xBF) {
|
||||||
|
return "UTF-8";
|
||||||
|
}
|
||||||
|
if (bytes[0] === 0xFE && bytes[1] === 0xFF) return "UTF-16BE";
|
||||||
|
if (bytes[0] === 0xFF && bytes[1] === 0xFE) return "UTF-16LE";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
decode,
|
||||||
|
TextDecoder,
|
||||||
|
TextDecoderStream,
|
||||||
|
TextEncoder,
|
||||||
|
TextEncoderStream,
|
||||||
|
};
|
||||||
|
|
1186
ext/web/09_file.js
1186
ext/web/09_file.js
File diff suppressed because it is too large
Load diff
|
@ -10,487 +10,482 @@
|
||||||
/// <reference path="./internal.d.ts" />
|
/// <reference path="./internal.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
import { forgivingBase64Encode } from "internal:ext/web/00_infra.js";
|
||||||
|
import { EventTarget, ProgressEvent } from "internal:ext/web/02_event.js";
|
||||||
|
import { decode, TextDecoder } from "internal:ext/web/08_text_encoding.js";
|
||||||
|
import { parseMimeType } from "internal:ext/web/01_mimesniff.js";
|
||||||
|
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||||
|
const {
|
||||||
|
ArrayPrototypePush,
|
||||||
|
ArrayPrototypeReduce,
|
||||||
|
FunctionPrototypeCall,
|
||||||
|
Map,
|
||||||
|
MapPrototypeGet,
|
||||||
|
MapPrototypeSet,
|
||||||
|
ObjectDefineProperty,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
queueMicrotask,
|
||||||
|
SafeArrayIterator,
|
||||||
|
Symbol,
|
||||||
|
TypedArrayPrototypeSet,
|
||||||
|
TypeError,
|
||||||
|
Uint8Array,
|
||||||
|
Uint8ArrayPrototype,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
const state = Symbol("[[state]]");
|
||||||
const core = window.Deno.core;
|
const result = Symbol("[[result]]");
|
||||||
const webidl = window.__bootstrap.webidl;
|
const error = Symbol("[[error]]");
|
||||||
const { forgivingBase64Encode } = window.__bootstrap.infra;
|
const aborted = Symbol("[[aborted]]");
|
||||||
const { ProgressEvent } = window.__bootstrap.event;
|
const handlerSymbol = Symbol("eventHandlers");
|
||||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
|
||||||
const { decode, TextDecoder } = window.__bootstrap.encoding;
|
|
||||||
const { parseMimeType } = window.__bootstrap.mimesniff;
|
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
|
||||||
const {
|
|
||||||
ArrayPrototypePush,
|
|
||||||
ArrayPrototypeReduce,
|
|
||||||
FunctionPrototypeCall,
|
|
||||||
Map,
|
|
||||||
MapPrototypeGet,
|
|
||||||
MapPrototypeSet,
|
|
||||||
ObjectDefineProperty,
|
|
||||||
ObjectPrototypeIsPrototypeOf,
|
|
||||||
queueMicrotask,
|
|
||||||
SafeArrayIterator,
|
|
||||||
Symbol,
|
|
||||||
TypedArrayPrototypeSet,
|
|
||||||
TypeError,
|
|
||||||
Uint8Array,
|
|
||||||
Uint8ArrayPrototype,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
const state = Symbol("[[state]]");
|
class FileReader extends EventTarget {
|
||||||
const result = Symbol("[[result]]");
|
/** @type {"empty" | "loading" | "done"} */
|
||||||
const error = Symbol("[[error]]");
|
[state] = "empty";
|
||||||
const aborted = Symbol("[[aborted]]");
|
/** @type {null | string | ArrayBuffer} */
|
||||||
const handlerSymbol = Symbol("eventHandlers");
|
[result] = null;
|
||||||
|
/** @type {null | DOMException} */
|
||||||
|
[error] = null;
|
||||||
|
/** @type {null | {aborted: boolean}} */
|
||||||
|
[aborted] = null;
|
||||||
|
|
||||||
class FileReader extends EventTarget {
|
/**
|
||||||
/** @type {"empty" | "loading" | "done"} */
|
* @param {Blob} blob
|
||||||
[state] = "empty";
|
* @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype
|
||||||
/** @type {null | string | ArrayBuffer} */
|
*/
|
||||||
[result] = null;
|
#readOperation(blob, readtype) {
|
||||||
/** @type {null | DOMException} */
|
// 1. If fr’s state is "loading", throw an InvalidStateError DOMException.
|
||||||
[error] = null;
|
if (this[state] === "loading") {
|
||||||
/** @type {null | {aborted: boolean}} */
|
throw new DOMException(
|
||||||
[aborted] = null;
|
"Invalid FileReader state.",
|
||||||
|
"InvalidStateError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// 2. Set fr’s state to "loading".
|
||||||
|
this[state] = "loading";
|
||||||
|
// 3. Set fr’s result to null.
|
||||||
|
this[result] = null;
|
||||||
|
// 4. Set fr’s error to null.
|
||||||
|
this[error] = null;
|
||||||
|
|
||||||
/**
|
// We set this[aborted] to a new object, and keep track of it in a
|
||||||
* @param {Blob} blob
|
// separate variable, so if a new read operation starts while there are
|
||||||
* @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype
|
// remaining tasks from a previous aborted operation, the new operation
|
||||||
*/
|
// will run while the tasks from the previous one are still aborted.
|
||||||
#readOperation(blob, readtype) {
|
const abortedState = this[aborted] = { aborted: false };
|
||||||
// 1. If fr’s state is "loading", throw an InvalidStateError DOMException.
|
|
||||||
if (this[state] === "loading") {
|
|
||||||
throw new DOMException(
|
|
||||||
"Invalid FileReader state.",
|
|
||||||
"InvalidStateError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 2. Set fr’s state to "loading".
|
|
||||||
this[state] = "loading";
|
|
||||||
// 3. Set fr’s result to null.
|
|
||||||
this[result] = null;
|
|
||||||
// 4. Set fr’s error to null.
|
|
||||||
this[error] = null;
|
|
||||||
|
|
||||||
// We set this[aborted] to a new object, and keep track of it in a
|
// 5. Let stream be the result of calling get stream on blob.
|
||||||
// separate variable, so if a new read operation starts while there are
|
const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream();
|
||||||
// remaining tasks from a previous aborted operation, the new operation
|
|
||||||
// will run while the tasks from the previous one are still aborted.
|
|
||||||
const abortedState = this[aborted] = { aborted: false };
|
|
||||||
|
|
||||||
// 5. Let stream be the result of calling get stream on blob.
|
// 6. Let reader be the result of getting a reader from stream.
|
||||||
const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream();
|
const reader = stream.getReader();
|
||||||
|
|
||||||
// 6. Let reader be the result of getting a reader from stream.
|
// 7. Let bytes be an empty byte sequence.
|
||||||
const reader = stream.getReader();
|
/** @type {Uint8Array[]} */
|
||||||
|
const chunks = [];
|
||||||
|
|
||||||
// 7. Let bytes be an empty byte sequence.
|
// 8. Let chunkPromise be the result of reading a chunk from stream with reader.
|
||||||
/** @type {Uint8Array[]} */
|
let chunkPromise = reader.read();
|
||||||
const chunks = [];
|
|
||||||
|
|
||||||
// 8. Let chunkPromise be the result of reading a chunk from stream with reader.
|
// 9. Let isFirstChunk be true.
|
||||||
let chunkPromise = reader.read();
|
let isFirstChunk = true;
|
||||||
|
|
||||||
// 9. Let isFirstChunk be true.
|
// 10 in parallel while true
|
||||||
let isFirstChunk = true;
|
(async () => {
|
||||||
|
while (!abortedState.aborted) {
|
||||||
|
// 1. Wait for chunkPromise to be fulfilled or rejected.
|
||||||
|
try {
|
||||||
|
const chunk = await chunkPromise;
|
||||||
|
if (abortedState.aborted) return;
|
||||||
|
|
||||||
// 10 in parallel while true
|
// 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr.
|
||||||
(async () => {
|
if (isFirstChunk) {
|
||||||
while (!abortedState.aborted) {
|
|
||||||
// 1. Wait for chunkPromise to be fulfilled or rejected.
|
|
||||||
try {
|
|
||||||
const chunk = await chunkPromise;
|
|
||||||
if (abortedState.aborted) return;
|
|
||||||
|
|
||||||
// 2. If chunkPromise is fulfilled, and isFirstChunk is true, queue a task to fire a progress event called loadstart at fr.
|
|
||||||
if (isFirstChunk) {
|
|
||||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
|
||||||
queueMicrotask(() => {
|
|
||||||
if (abortedState.aborted) return;
|
|
||||||
// fire a progress event for loadstart
|
|
||||||
const ev = new ProgressEvent("loadstart", {});
|
|
||||||
this.dispatchEvent(ev);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 3. Set isFirstChunk to false.
|
|
||||||
isFirstChunk = false;
|
|
||||||
|
|
||||||
// 4. If chunkPromise is fulfilled with an object whose done property is false
|
|
||||||
// and whose value property is a Uint8Array object, run these steps:
|
|
||||||
if (
|
|
||||||
!chunk.done &&
|
|
||||||
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk.value)
|
|
||||||
) {
|
|
||||||
ArrayPrototypePush(chunks, chunk.value);
|
|
||||||
|
|
||||||
// TODO(bartlomieju): (only) If roughly 50ms have passed since last progress
|
|
||||||
{
|
|
||||||
const size = ArrayPrototypeReduce(
|
|
||||||
chunks,
|
|
||||||
(p, i) => p + i.byteLength,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const ev = new ProgressEvent("progress", {
|
|
||||||
loaded: size,
|
|
||||||
});
|
|
||||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
|
||||||
queueMicrotask(() => {
|
|
||||||
if (abortedState.aborted) return;
|
|
||||||
this.dispatchEvent(ev);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
chunkPromise = reader.read();
|
|
||||||
} // 5 Otherwise, if chunkPromise is fulfilled with an object whose done property is true, queue a task to run the following steps and abort this algorithm:
|
|
||||||
else if (chunk.done === true) {
|
|
||||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
|
||||||
queueMicrotask(() => {
|
|
||||||
if (abortedState.aborted) return;
|
|
||||||
// 1. Set fr’s state to "done".
|
|
||||||
this[state] = "done";
|
|
||||||
// 2. Let result be the result of package data given bytes, type, blob’s type, and encodingName.
|
|
||||||
const size = ArrayPrototypeReduce(
|
|
||||||
chunks,
|
|
||||||
(p, i) => p + i.byteLength,
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
const bytes = new Uint8Array(size);
|
|
||||||
let offs = 0;
|
|
||||||
for (let i = 0; i < chunks.length; ++i) {
|
|
||||||
const chunk = chunks[i];
|
|
||||||
TypedArrayPrototypeSet(bytes, chunk, offs);
|
|
||||||
offs += chunk.byteLength;
|
|
||||||
}
|
|
||||||
switch (readtype.kind) {
|
|
||||||
case "ArrayBuffer": {
|
|
||||||
this[result] = bytes.buffer;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "BinaryString":
|
|
||||||
this[result] = core.ops.op_encode_binary_string(bytes);
|
|
||||||
break;
|
|
||||||
case "Text": {
|
|
||||||
let decoder = undefined;
|
|
||||||
if (readtype.encoding) {
|
|
||||||
try {
|
|
||||||
decoder = new TextDecoder(readtype.encoding);
|
|
||||||
} catch {
|
|
||||||
// don't care about the error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (decoder === undefined) {
|
|
||||||
const mimeType = parseMimeType(blob.type);
|
|
||||||
if (mimeType) {
|
|
||||||
const charset = MapPrototypeGet(
|
|
||||||
mimeType.parameters,
|
|
||||||
"charset",
|
|
||||||
);
|
|
||||||
if (charset) {
|
|
||||||
try {
|
|
||||||
decoder = new TextDecoder(charset);
|
|
||||||
} catch {
|
|
||||||
// don't care about the error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (decoder === undefined) {
|
|
||||||
decoder = new TextDecoder();
|
|
||||||
}
|
|
||||||
this[result] = decode(bytes, decoder.encoding);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "DataUrl": {
|
|
||||||
const mediaType = blob.type || "application/octet-stream";
|
|
||||||
this[result] = `data:${mediaType};base64,${
|
|
||||||
forgivingBase64Encode(bytes)
|
|
||||||
}`;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 4.2 Fire a progress event called load at the fr.
|
|
||||||
{
|
|
||||||
const ev = new ProgressEvent("load", {
|
|
||||||
lengthComputable: true,
|
|
||||||
loaded: size,
|
|
||||||
total: size,
|
|
||||||
});
|
|
||||||
this.dispatchEvent(ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. If fr’s state is not "loading", fire a progress event called loadend at the fr.
|
|
||||||
//Note: Event handler for the load or error events could have started another load, if that happens the loadend event for this load is not fired.
|
|
||||||
if (this[state] !== "loading") {
|
|
||||||
const ev = new ProgressEvent("loadend", {
|
|
||||||
lengthComputable: true,
|
|
||||||
loaded: size,
|
|
||||||
total: size,
|
|
||||||
});
|
|
||||||
this.dispatchEvent(ev);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||||
queueMicrotask(() => {
|
queueMicrotask(() => {
|
||||||
if (abortedState.aborted) return;
|
if (abortedState.aborted) return;
|
||||||
|
// fire a progress event for loadstart
|
||||||
|
const ev = new ProgressEvent("loadstart", {});
|
||||||
|
this.dispatchEvent(ev);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 3. Set isFirstChunk to false.
|
||||||
|
isFirstChunk = false;
|
||||||
|
|
||||||
// chunkPromise rejected
|
// 4. If chunkPromise is fulfilled with an object whose done property is false
|
||||||
|
// and whose value property is a Uint8Array object, run these steps:
|
||||||
|
if (
|
||||||
|
!chunk.done &&
|
||||||
|
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk.value)
|
||||||
|
) {
|
||||||
|
ArrayPrototypePush(chunks, chunk.value);
|
||||||
|
|
||||||
|
// TODO(bartlomieju): (only) If roughly 50ms have passed since last progress
|
||||||
|
{
|
||||||
|
const size = ArrayPrototypeReduce(
|
||||||
|
chunks,
|
||||||
|
(p, i) => p + i.byteLength,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
const ev = new ProgressEvent("progress", {
|
||||||
|
loaded: size,
|
||||||
|
});
|
||||||
|
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (abortedState.aborted) return;
|
||||||
|
this.dispatchEvent(ev);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
chunkPromise = reader.read();
|
||||||
|
} // 5 Otherwise, if chunkPromise is fulfilled with an object whose done property is true, queue a task to run the following steps and abort this algorithm:
|
||||||
|
else if (chunk.done === true) {
|
||||||
|
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (abortedState.aborted) return;
|
||||||
|
// 1. Set fr’s state to "done".
|
||||||
this[state] = "done";
|
this[state] = "done";
|
||||||
this[error] = err;
|
// 2. Let result be the result of package data given bytes, type, blob’s type, and encodingName.
|
||||||
|
const size = ArrayPrototypeReduce(
|
||||||
|
chunks,
|
||||||
|
(p, i) => p + i.byteLength,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
const bytes = new Uint8Array(size);
|
||||||
|
let offs = 0;
|
||||||
|
for (let i = 0; i < chunks.length; ++i) {
|
||||||
|
const chunk = chunks[i];
|
||||||
|
TypedArrayPrototypeSet(bytes, chunk, offs);
|
||||||
|
offs += chunk.byteLength;
|
||||||
|
}
|
||||||
|
switch (readtype.kind) {
|
||||||
|
case "ArrayBuffer": {
|
||||||
|
this[result] = bytes.buffer;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "BinaryString":
|
||||||
|
this[result] = ops.op_encode_binary_string(bytes);
|
||||||
|
break;
|
||||||
|
case "Text": {
|
||||||
|
let decoder = undefined;
|
||||||
|
if (readtype.encoding) {
|
||||||
|
try {
|
||||||
|
decoder = new TextDecoder(readtype.encoding);
|
||||||
|
} catch {
|
||||||
|
// don't care about the error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (decoder === undefined) {
|
||||||
|
const mimeType = parseMimeType(blob.type);
|
||||||
|
if (mimeType) {
|
||||||
|
const charset = MapPrototypeGet(
|
||||||
|
mimeType.parameters,
|
||||||
|
"charset",
|
||||||
|
);
|
||||||
|
if (charset) {
|
||||||
|
try {
|
||||||
|
decoder = new TextDecoder(charset);
|
||||||
|
} catch {
|
||||||
|
// don't care about the error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (decoder === undefined) {
|
||||||
|
decoder = new TextDecoder();
|
||||||
|
}
|
||||||
|
this[result] = decode(bytes, decoder.encoding);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DataUrl": {
|
||||||
|
const mediaType = blob.type || "application/octet-stream";
|
||||||
|
this[result] = `data:${mediaType};base64,${
|
||||||
|
forgivingBase64Encode(bytes)
|
||||||
|
}`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 4.2 Fire a progress event called load at the fr.
|
||||||
{
|
{
|
||||||
const ev = new ProgressEvent("error", {});
|
const ev = new ProgressEvent("load", {
|
||||||
|
lengthComputable: true,
|
||||||
|
loaded: size,
|
||||||
|
total: size,
|
||||||
|
});
|
||||||
this.dispatchEvent(ev);
|
this.dispatchEvent(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
//If fr’s state is not "loading", fire a progress event called loadend at fr.
|
// 5. If fr’s state is not "loading", fire a progress event called loadend at the fr.
|
||||||
//Note: Event handler for the error event could have started another load, if that happens the loadend event for this load is not fired.
|
//Note: Event handler for the load or error events could have started another load, if that happens the loadend event for this load is not fired.
|
||||||
if (this[state] !== "loading") {
|
if (this[state] !== "loading") {
|
||||||
const ev = new ProgressEvent("loadend", {});
|
const ev = new ProgressEvent("loadend", {
|
||||||
|
lengthComputable: true,
|
||||||
|
loaded: size,
|
||||||
|
total: size,
|
||||||
|
});
|
||||||
this.dispatchEvent(ev);
|
this.dispatchEvent(ev);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
// TODO(lucacasonato): this is wrong, should be HTML "queue a task"
|
||||||
|
queueMicrotask(() => {
|
||||||
|
if (abortedState.aborted) return;
|
||||||
|
|
||||||
|
// chunkPromise rejected
|
||||||
|
this[state] = "done";
|
||||||
|
this[error] = err;
|
||||||
|
|
||||||
|
{
|
||||||
|
const ev = new ProgressEvent("error", {});
|
||||||
|
this.dispatchEvent(ev);
|
||||||
|
}
|
||||||
|
|
||||||
|
//If fr’s state is not "loading", fire a progress event called loadend at fr.
|
||||||
|
//Note: Event handler for the error event could have started another load, if that happens the loadend event for this load is not fired.
|
||||||
|
if (this[state] !== "loading") {
|
||||||
|
const ev = new ProgressEvent("loadend", {});
|
||||||
|
this.dispatchEvent(ev);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
#getEventHandlerFor(name) {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
|
|
||||||
const maybeMap = this[handlerSymbol];
|
|
||||||
if (!maybeMap) return null;
|
|
||||||
|
|
||||||
return MapPrototypeGet(maybeMap, name)?.handler ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#setEventHandlerFor(name, value) {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
|
|
||||||
if (!this[handlerSymbol]) {
|
|
||||||
this[handlerSymbol] = new Map();
|
|
||||||
}
|
|
||||||
let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name);
|
|
||||||
if (handlerWrapper) {
|
|
||||||
handlerWrapper.handler = value;
|
|
||||||
} else {
|
|
||||||
handlerWrapper = makeWrappedHandler(value);
|
|
||||||
this.addEventListener(name, handlerWrapper);
|
|
||||||
}
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
MapPrototypeSet(this[handlerSymbol], name, handlerWrapper);
|
#getEventHandlerFor(name) {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
|
||||||
|
const maybeMap = this[handlerSymbol];
|
||||||
|
if (!maybeMap) return null;
|
||||||
|
|
||||||
|
return MapPrototypeGet(maybeMap, name)?.handler ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
#setEventHandlerFor(name, value) {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
|
||||||
|
if (!this[handlerSymbol]) {
|
||||||
|
this[handlerSymbol] = new Map();
|
||||||
|
}
|
||||||
|
let handlerWrapper = MapPrototypeGet(this[handlerSymbol], name);
|
||||||
|
if (handlerWrapper) {
|
||||||
|
handlerWrapper.handler = value;
|
||||||
|
} else {
|
||||||
|
handlerWrapper = makeWrappedHandler(value);
|
||||||
|
this.addEventListener(name, handlerWrapper);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
MapPrototypeSet(this[handlerSymbol], name, handlerWrapper);
|
||||||
super();
|
}
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {number} */
|
||||||
|
get readyState() {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
switch (this[state]) {
|
||||||
|
case "empty":
|
||||||
|
return FileReader.EMPTY;
|
||||||
|
case "loading":
|
||||||
|
return FileReader.LOADING;
|
||||||
|
case "done":
|
||||||
|
return FileReader.DONE;
|
||||||
|
default:
|
||||||
|
throw new TypeError("Invalid state");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get result() {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
return this[result];
|
||||||
|
}
|
||||||
|
|
||||||
|
get error() {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
return this[error];
|
||||||
|
}
|
||||||
|
|
||||||
|
abort() {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
// If context object's state is "empty" or if context object's state is "done" set context object's result to null and terminate this algorithm.
|
||||||
|
if (
|
||||||
|
this[state] === "empty" ||
|
||||||
|
this[state] === "done"
|
||||||
|
) {
|
||||||
|
this[result] = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If context object's state is "loading" set context object's state to "done" and set context object's result to null.
|
||||||
|
if (this[state] === "loading") {
|
||||||
|
this[state] = "done";
|
||||||
|
this[result] = null;
|
||||||
|
}
|
||||||
|
// If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue.
|
||||||
|
// Terminate the algorithm for the read method being processed.
|
||||||
|
if (this[aborted] !== null) {
|
||||||
|
this[aborted].aborted = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {number} */
|
// Fire a progress event called abort at the context object.
|
||||||
get readyState() {
|
const ev = new ProgressEvent("abort", {});
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
this.dispatchEvent(ev);
|
||||||
switch (this[state]) {
|
|
||||||
case "empty":
|
|
||||||
return FileReader.EMPTY;
|
|
||||||
case "loading":
|
|
||||||
return FileReader.LOADING;
|
|
||||||
case "done":
|
|
||||||
return FileReader.DONE;
|
|
||||||
default:
|
|
||||||
throw new TypeError("Invalid state");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get result() {
|
// If context object's state is not "loading", fire a progress event called loadend at the context object.
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
if (this[state] !== "loading") {
|
||||||
return this[result];
|
const ev = new ProgressEvent("loadend", {});
|
||||||
}
|
|
||||||
|
|
||||||
get error() {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
return this[error];
|
|
||||||
}
|
|
||||||
|
|
||||||
abort() {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
// If context object's state is "empty" or if context object's state is "done" set context object's result to null and terminate this algorithm.
|
|
||||||
if (
|
|
||||||
this[state] === "empty" ||
|
|
||||||
this[state] === "done"
|
|
||||||
) {
|
|
||||||
this[result] = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If context object's state is "loading" set context object's state to "done" and set context object's result to null.
|
|
||||||
if (this[state] === "loading") {
|
|
||||||
this[state] = "done";
|
|
||||||
this[result] = null;
|
|
||||||
}
|
|
||||||
// If there are any tasks from the context object on the file reading task source in an affiliated task queue, then remove those tasks from that task queue.
|
|
||||||
// Terminate the algorithm for the read method being processed.
|
|
||||||
if (this[aborted] !== null) {
|
|
||||||
this[aborted].aborted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fire a progress event called abort at the context object.
|
|
||||||
const ev = new ProgressEvent("abort", {});
|
|
||||||
this.dispatchEvent(ev);
|
this.dispatchEvent(ev);
|
||||||
|
|
||||||
// If context object's state is not "loading", fire a progress event called loadend at the context object.
|
|
||||||
if (this[state] !== "loading") {
|
|
||||||
const ev = new ProgressEvent("loadend", {});
|
|
||||||
this.dispatchEvent(ev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {Blob} blob */
|
|
||||||
readAsArrayBuffer(blob) {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
const prefix = "Failed to execute 'readAsArrayBuffer' on 'FileReader'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
this.#readOperation(blob, { kind: "ArrayBuffer" });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {Blob} blob */
|
|
||||||
readAsBinaryString(blob) {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
// alias for readAsArrayBuffer
|
|
||||||
this.#readOperation(blob, { kind: "BinaryString" });
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {Blob} blob */
|
|
||||||
readAsDataURL(blob) {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
const prefix = "Failed to execute 'readAsDataURL' on 'FileReader'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
// alias for readAsArrayBuffer
|
|
||||||
this.#readOperation(blob, { kind: "DataUrl" });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Blob} blob
|
|
||||||
* @param {string} [encoding]
|
|
||||||
*/
|
|
||||||
readAsText(blob, encoding = undefined) {
|
|
||||||
webidl.assertBranded(this, FileReaderPrototype);
|
|
||||||
const prefix = "Failed to execute 'readAsText' on 'FileReader'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
if (encoding !== undefined) {
|
|
||||||
encoding = webidl.converters["DOMString"](encoding, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// alias for readAsArrayBuffer
|
|
||||||
this.#readOperation(blob, { kind: "Text", encoding });
|
|
||||||
}
|
|
||||||
|
|
||||||
get onerror() {
|
|
||||||
return this.#getEventHandlerFor("error");
|
|
||||||
}
|
|
||||||
set onerror(value) {
|
|
||||||
this.#setEventHandlerFor("error", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get onloadstart() {
|
|
||||||
return this.#getEventHandlerFor("loadstart");
|
|
||||||
}
|
|
||||||
set onloadstart(value) {
|
|
||||||
this.#setEventHandlerFor("loadstart", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get onload() {
|
|
||||||
return this.#getEventHandlerFor("load");
|
|
||||||
}
|
|
||||||
set onload(value) {
|
|
||||||
this.#setEventHandlerFor("load", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get onloadend() {
|
|
||||||
return this.#getEventHandlerFor("loadend");
|
|
||||||
}
|
|
||||||
set onloadend(value) {
|
|
||||||
this.#setEventHandlerFor("loadend", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get onprogress() {
|
|
||||||
return this.#getEventHandlerFor("progress");
|
|
||||||
}
|
|
||||||
set onprogress(value) {
|
|
||||||
this.#setEventHandlerFor("progress", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
get onabort() {
|
|
||||||
return this.#getEventHandlerFor("abort");
|
|
||||||
}
|
|
||||||
set onabort(value) {
|
|
||||||
this.#setEventHandlerFor("abort", value);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(FileReader);
|
/** @param {Blob} blob */
|
||||||
const FileReaderPrototype = FileReader.prototype;
|
readAsArrayBuffer(blob) {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
ObjectDefineProperty(FileReader, "EMPTY", {
|
const prefix = "Failed to execute 'readAsArrayBuffer' on 'FileReader'";
|
||||||
writable: false,
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
enumerable: true,
|
this.#readOperation(blob, { kind: "ArrayBuffer" });
|
||||||
configurable: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
ObjectDefineProperty(FileReader, "LOADING", {
|
|
||||||
writable: false,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: false,
|
|
||||||
value: 1,
|
|
||||||
});
|
|
||||||
ObjectDefineProperty(FileReader, "DONE", {
|
|
||||||
writable: false,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: false,
|
|
||||||
value: 2,
|
|
||||||
});
|
|
||||||
ObjectDefineProperty(FileReader.prototype, "EMPTY", {
|
|
||||||
writable: false,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: false,
|
|
||||||
value: 0,
|
|
||||||
});
|
|
||||||
ObjectDefineProperty(FileReader.prototype, "LOADING", {
|
|
||||||
writable: false,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: false,
|
|
||||||
value: 1,
|
|
||||||
});
|
|
||||||
ObjectDefineProperty(FileReader.prototype, "DONE", {
|
|
||||||
writable: false,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: false,
|
|
||||||
value: 2,
|
|
||||||
});
|
|
||||||
|
|
||||||
function makeWrappedHandler(handler) {
|
|
||||||
function wrappedHandler(...args) {
|
|
||||||
if (typeof wrappedHandler.handler !== "function") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return FunctionPrototypeCall(
|
|
||||||
wrappedHandler.handler,
|
|
||||||
this,
|
|
||||||
...new SafeArrayIterator(args),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
wrappedHandler.handler = handler;
|
|
||||||
return wrappedHandler;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.fileReader = {
|
/** @param {Blob} blob */
|
||||||
FileReader,
|
readAsBinaryString(blob) {
|
||||||
};
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
})(this);
|
const prefix = "Failed to execute 'readAsBinaryString' on 'FileReader'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
// alias for readAsArrayBuffer
|
||||||
|
this.#readOperation(blob, { kind: "BinaryString" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param {Blob} blob */
|
||||||
|
readAsDataURL(blob) {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
const prefix = "Failed to execute 'readAsDataURL' on 'FileReader'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
// alias for readAsArrayBuffer
|
||||||
|
this.#readOperation(blob, { kind: "DataUrl" });
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Blob} blob
|
||||||
|
* @param {string} [encoding]
|
||||||
|
*/
|
||||||
|
readAsText(blob, encoding = undefined) {
|
||||||
|
webidl.assertBranded(this, FileReaderPrototype);
|
||||||
|
const prefix = "Failed to execute 'readAsText' on 'FileReader'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
if (encoding !== undefined) {
|
||||||
|
encoding = webidl.converters["DOMString"](encoding, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// alias for readAsArrayBuffer
|
||||||
|
this.#readOperation(blob, { kind: "Text", encoding });
|
||||||
|
}
|
||||||
|
|
||||||
|
get onerror() {
|
||||||
|
return this.#getEventHandlerFor("error");
|
||||||
|
}
|
||||||
|
set onerror(value) {
|
||||||
|
this.#setEventHandlerFor("error", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get onloadstart() {
|
||||||
|
return this.#getEventHandlerFor("loadstart");
|
||||||
|
}
|
||||||
|
set onloadstart(value) {
|
||||||
|
this.#setEventHandlerFor("loadstart", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get onload() {
|
||||||
|
return this.#getEventHandlerFor("load");
|
||||||
|
}
|
||||||
|
set onload(value) {
|
||||||
|
this.#setEventHandlerFor("load", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get onloadend() {
|
||||||
|
return this.#getEventHandlerFor("loadend");
|
||||||
|
}
|
||||||
|
set onloadend(value) {
|
||||||
|
this.#setEventHandlerFor("loadend", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get onprogress() {
|
||||||
|
return this.#getEventHandlerFor("progress");
|
||||||
|
}
|
||||||
|
set onprogress(value) {
|
||||||
|
this.#setEventHandlerFor("progress", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
get onabort() {
|
||||||
|
return this.#getEventHandlerFor("abort");
|
||||||
|
}
|
||||||
|
set onabort(value) {
|
||||||
|
this.#setEventHandlerFor("abort", value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(FileReader);
|
||||||
|
const FileReaderPrototype = FileReader.prototype;
|
||||||
|
|
||||||
|
ObjectDefineProperty(FileReader, "EMPTY", {
|
||||||
|
writable: false,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
value: 0,
|
||||||
|
});
|
||||||
|
ObjectDefineProperty(FileReader, "LOADING", {
|
||||||
|
writable: false,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
value: 1,
|
||||||
|
});
|
||||||
|
ObjectDefineProperty(FileReader, "DONE", {
|
||||||
|
writable: false,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
value: 2,
|
||||||
|
});
|
||||||
|
ObjectDefineProperty(FileReader.prototype, "EMPTY", {
|
||||||
|
writable: false,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
value: 0,
|
||||||
|
});
|
||||||
|
ObjectDefineProperty(FileReader.prototype, "LOADING", {
|
||||||
|
writable: false,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
value: 1,
|
||||||
|
});
|
||||||
|
ObjectDefineProperty(FileReader.prototype, "DONE", {
|
||||||
|
writable: false,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: false,
|
||||||
|
value: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeWrappedHandler(handler) {
|
||||||
|
function wrappedHandler(...args) {
|
||||||
|
if (typeof wrappedHandler.handler !== "function") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return FunctionPrototypeCall(
|
||||||
|
wrappedHandler.handler,
|
||||||
|
this,
|
||||||
|
...new SafeArrayIterator(args),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
wrappedHandler.handler = handler;
|
||||||
|
return wrappedHandler;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { FileReader };
|
||||||
|
|
|
@ -10,50 +10,42 @@
|
||||||
/// <reference path="../url/lib.deno_url.d.ts" />
|
/// <reference path="../url/lib.deno_url.d.ts" />
|
||||||
/// <reference path="./internal.d.ts" />
|
/// <reference path="./internal.d.ts" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = window.__bootstrap.webidl;
|
import { getParts } from "internal:ext/web/09_file.js";
|
||||||
const { getParts } = window.__bootstrap.file;
|
import { URL } from "internal:ext/url/00_url.js";
|
||||||
const { URL } = window.__bootstrap.url;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Blob} blob
|
* @param {Blob} blob
|
||||||
* @returns {string}
|
* @returns {string}
|
||||||
*/
|
*/
|
||||||
function createObjectURL(blob) {
|
function createObjectURL(blob) {
|
||||||
const prefix = "Failed to execute 'createObjectURL' on 'URL'";
|
const prefix = "Failed to execute 'createObjectURL' on 'URL'";
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
blob = webidl.converters["Blob"](blob, {
|
blob = webidl.converters["Blob"](blob, {
|
||||||
context: "Argument 1",
|
context: "Argument 1",
|
||||||
prefix,
|
prefix,
|
||||||
});
|
});
|
||||||
|
|
||||||
const url = ops.op_blob_create_object_url(
|
return ops.op_blob_create_object_url(blob.type, getParts(blob));
|
||||||
blob.type,
|
}
|
||||||
getParts(blob),
|
|
||||||
);
|
|
||||||
|
|
||||||
return url;
|
/**
|
||||||
}
|
* @param {string} url
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function revokeObjectURL(url) {
|
||||||
|
const prefix = "Failed to execute 'revokeObjectURL' on 'URL'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
url = webidl.converters["DOMString"](url, {
|
||||||
|
context: "Argument 1",
|
||||||
|
prefix,
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
ops.op_blob_revoke_object_url(url);
|
||||||
* @param {string} url
|
}
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
function revokeObjectURL(url) {
|
|
||||||
const prefix = "Failed to execute 'revokeObjectURL' on 'URL'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
url = webidl.converters["DOMString"](url, {
|
|
||||||
context: "Argument 1",
|
|
||||||
prefix,
|
|
||||||
});
|
|
||||||
|
|
||||||
ops.op_blob_revoke_object_url(url);
|
URL.createObjectURL = createObjectURL;
|
||||||
}
|
URL.revokeObjectURL = revokeObjectURL;
|
||||||
|
|
||||||
URL.createObjectURL = createObjectURL;
|
|
||||||
URL.revokeObjectURL = revokeObjectURL;
|
|
||||||
})(globalThis);
|
|
||||||
|
|
|
@ -1,403 +1,410 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
((window) => {
|
import { URL } from "internal:ext/url/00_url.js";
|
||||||
const { URL } = window.__bootstrap.url;
|
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
Error,
|
Error,
|
||||||
ObjectDefineProperties,
|
ObjectDefineProperties,
|
||||||
Symbol,
|
Symbol,
|
||||||
SymbolFor,
|
SymbolFor,
|
||||||
SymbolToStringTag,
|
SymbolToStringTag,
|
||||||
TypeError,
|
TypeError,
|
||||||
WeakMap,
|
WeakMap,
|
||||||
WeakMapPrototypeGet,
|
WeakMapPrototypeGet,
|
||||||
WeakMapPrototypeSet,
|
WeakMapPrototypeSet,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
|
|
||||||
const locationConstructorKey = Symbol("locationConstuctorKey");
|
const locationConstructorKey = Symbol("locationConstuctorKey");
|
||||||
|
|
||||||
// The differences between the definitions of `Location` and `WorkerLocation`
|
// The differences between the definitions of `Location` and `WorkerLocation`
|
||||||
// are because of the `LegacyUnforgeable` attribute only specified upon
|
// are because of the `LegacyUnforgeable` attribute only specified upon
|
||||||
// `Location`'s properties. See:
|
// `Location`'s properties. See:
|
||||||
// - https://html.spec.whatwg.org/multipage/history.html#the-location-interface
|
// - https://html.spec.whatwg.org/multipage/history.html#the-location-interface
|
||||||
// - https://heycam.github.io/webidl/#LegacyUnforgeable
|
// - https://heycam.github.io/webidl/#LegacyUnforgeable
|
||||||
class Location {
|
class Location {
|
||||||
constructor(href = null, key = null) {
|
constructor(href = null, key = null) {
|
||||||
if (key != locationConstructorKey) {
|
if (key != locationConstructorKey) {
|
||||||
throw new TypeError("Illegal constructor.");
|
throw new TypeError("Illegal constructor.");
|
||||||
}
|
|
||||||
const url = new URL(href);
|
|
||||||
url.username = "";
|
|
||||||
url.password = "";
|
|
||||||
ObjectDefineProperties(this, {
|
|
||||||
hash: {
|
|
||||||
get() {
|
|
||||||
return url.hash;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.hash".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
host: {
|
|
||||||
get() {
|
|
||||||
return url.host;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.host".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
hostname: {
|
|
||||||
get() {
|
|
||||||
return url.hostname;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.hostname".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
href: {
|
|
||||||
get() {
|
|
||||||
return url.href;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.href".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
origin: {
|
|
||||||
get() {
|
|
||||||
return url.origin;
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
pathname: {
|
|
||||||
get() {
|
|
||||||
return url.pathname;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.pathname".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
port: {
|
|
||||||
get() {
|
|
||||||
return url.port;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.port".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
protocol: {
|
|
||||||
get() {
|
|
||||||
return url.protocol;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.protocol".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
get() {
|
|
||||||
return url.search;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot set "location.search".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
ancestorOrigins: {
|
|
||||||
get() {
|
|
||||||
// TODO(nayeemrmn): Replace with a `DOMStringList` instance.
|
|
||||||
return {
|
|
||||||
length: 0,
|
|
||||||
item: () => null,
|
|
||||||
contains: () => false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
assign: {
|
|
||||||
value: function assign() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot call "location.assign()".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
reload: {
|
|
||||||
value: function reload() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot call "location.reload()".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
replace: {
|
|
||||||
value: function replace() {
|
|
||||||
throw new DOMException(
|
|
||||||
`Cannot call "location.replace()".`,
|
|
||||||
"NotSupportedError",
|
|
||||||
);
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
toString: {
|
|
||||||
value: function toString() {
|
|
||||||
return url.href;
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
[SymbolFor("Deno.privateCustomInspect")]: {
|
|
||||||
value: function (inspect) {
|
|
||||||
const object = {
|
|
||||||
hash: this.hash,
|
|
||||||
host: this.host,
|
|
||||||
hostname: this.hostname,
|
|
||||||
href: this.href,
|
|
||||||
origin: this.origin,
|
|
||||||
pathname: this.pathname,
|
|
||||||
port: this.port,
|
|
||||||
protocol: this.protocol,
|
|
||||||
search: this.search,
|
|
||||||
};
|
|
||||||
return `${this.constructor.name} ${inspect(object)}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
const url = new URL(href);
|
||||||
|
url.username = "";
|
||||||
ObjectDefineProperties(Location.prototype, {
|
url.password = "";
|
||||||
[SymbolToStringTag]: {
|
ObjectDefineProperties(this, {
|
||||||
value: "Location",
|
hash: {
|
||||||
configurable: true,
|
get() {
|
||||||
},
|
return url.hash;
|
||||||
});
|
},
|
||||||
|
set() {
|
||||||
const workerLocationUrls = new WeakMap();
|
throw new DOMException(
|
||||||
|
`Cannot set "location.hash".`,
|
||||||
class WorkerLocation {
|
"NotSupportedError",
|
||||||
constructor(href = null, key = null) {
|
|
||||||
if (key != locationConstructorKey) {
|
|
||||||
throw new TypeError("Illegal constructor.");
|
|
||||||
}
|
|
||||||
const url = new URL(href);
|
|
||||||
url.username = "";
|
|
||||||
url.password = "";
|
|
||||||
WeakMapPrototypeSet(workerLocationUrls, this, url);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ObjectDefineProperties(WorkerLocation.prototype, {
|
|
||||||
hash: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.hash;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
host: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.host;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
hostname: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.hostname;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
href: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.href;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
origin: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.origin;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
pathname: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.pathname;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
port: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.port;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
protocol: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.protocol;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
get() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.search;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
toString: {
|
|
||||||
value: function toString() {
|
|
||||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
|
||||||
if (url == null) {
|
|
||||||
throw new TypeError("Illegal invocation.");
|
|
||||||
}
|
|
||||||
return url.href;
|
|
||||||
},
|
|
||||||
configurable: true,
|
|
||||||
enumerable: true,
|
|
||||||
writable: true,
|
|
||||||
},
|
|
||||||
[SymbolToStringTag]: {
|
|
||||||
value: "WorkerLocation",
|
|
||||||
configurable: true,
|
|
||||||
},
|
|
||||||
[SymbolFor("Deno.privateCustomInspect")]: {
|
|
||||||
value: function (inspect) {
|
|
||||||
const object = {
|
|
||||||
hash: this.hash,
|
|
||||||
host: this.host,
|
|
||||||
hostname: this.hostname,
|
|
||||||
href: this.href,
|
|
||||||
origin: this.origin,
|
|
||||||
pathname: this.pathname,
|
|
||||||
port: this.port,
|
|
||||||
protocol: this.protocol,
|
|
||||||
search: this.search,
|
|
||||||
};
|
|
||||||
return `${this.constructor.name} ${inspect(object)}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
let location = undefined;
|
|
||||||
let workerLocation = undefined;
|
|
||||||
|
|
||||||
function setLocationHref(href) {
|
|
||||||
location = new Location(href, locationConstructorKey);
|
|
||||||
workerLocation = new WorkerLocation(href, locationConstructorKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.location = {
|
|
||||||
locationConstructorDescriptor: {
|
|
||||||
value: Location,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
},
|
|
||||||
workerLocationConstructorDescriptor: {
|
|
||||||
value: WorkerLocation,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
},
|
|
||||||
locationDescriptor: {
|
|
||||||
get() {
|
|
||||||
return location;
|
|
||||||
},
|
|
||||||
set() {
|
|
||||||
throw new DOMException(`Cannot set "location".`, "NotSupportedError");
|
|
||||||
},
|
|
||||||
enumerable: true,
|
|
||||||
},
|
|
||||||
workerLocationDescriptor: {
|
|
||||||
get() {
|
|
||||||
if (workerLocation == null) {
|
|
||||||
throw new Error(
|
|
||||||
`Assertion: "globalThis.location" must be defined in a worker.`,
|
|
||||||
);
|
);
|
||||||
}
|
},
|
||||||
return workerLocation;
|
enumerable: true,
|
||||||
},
|
},
|
||||||
configurable: true,
|
host: {
|
||||||
enumerable: true,
|
get() {
|
||||||
|
return url.host;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot set "location.host".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
hostname: {
|
||||||
|
get() {
|
||||||
|
return url.hostname;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot set "location.hostname".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
href: {
|
||||||
|
get() {
|
||||||
|
return url.href;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot set "location.href".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
origin: {
|
||||||
|
get() {
|
||||||
|
return url.origin;
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
pathname: {
|
||||||
|
get() {
|
||||||
|
return url.pathname;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot set "location.pathname".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
port: {
|
||||||
|
get() {
|
||||||
|
return url.port;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot set "location.port".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
protocol: {
|
||||||
|
get() {
|
||||||
|
return url.protocol;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot set "location.protocol".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
get() {
|
||||||
|
return url.search;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot set "location.search".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
ancestorOrigins: {
|
||||||
|
get() {
|
||||||
|
// TODO(nayeemrmn): Replace with a `DOMStringList` instance.
|
||||||
|
return {
|
||||||
|
length: 0,
|
||||||
|
item: () => null,
|
||||||
|
contains: () => false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
assign: {
|
||||||
|
value: function assign() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot call "location.assign()".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
reload: {
|
||||||
|
value: function reload() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot call "location.reload()".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
replace: {
|
||||||
|
value: function replace() {
|
||||||
|
throw new DOMException(
|
||||||
|
`Cannot call "location.replace()".`,
|
||||||
|
"NotSupportedError",
|
||||||
|
);
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
toString: {
|
||||||
|
value: function toString() {
|
||||||
|
return url.href;
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
[SymbolFor("Deno.privateCustomInspect")]: {
|
||||||
|
value: function (inspect) {
|
||||||
|
const object = {
|
||||||
|
hash: this.hash,
|
||||||
|
host: this.host,
|
||||||
|
hostname: this.hostname,
|
||||||
|
href: this.href,
|
||||||
|
origin: this.origin,
|
||||||
|
pathname: this.pathname,
|
||||||
|
port: this.port,
|
||||||
|
protocol: this.protocol,
|
||||||
|
search: this.search,
|
||||||
|
};
|
||||||
|
return `${this.constructor.name} ${inspect(object)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectDefineProperties(Location.prototype, {
|
||||||
|
[SymbolToStringTag]: {
|
||||||
|
value: "Location",
|
||||||
|
configurable: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const workerLocationUrls = new WeakMap();
|
||||||
|
|
||||||
|
class WorkerLocation {
|
||||||
|
constructor(href = null, key = null) {
|
||||||
|
if (key != locationConstructorKey) {
|
||||||
|
throw new TypeError("Illegal constructor.");
|
||||||
|
}
|
||||||
|
const url = new URL(href);
|
||||||
|
url.username = "";
|
||||||
|
url.password = "";
|
||||||
|
WeakMapPrototypeSet(workerLocationUrls, this, url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectDefineProperties(WorkerLocation.prototype, {
|
||||||
|
hash: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.hash;
|
||||||
},
|
},
|
||||||
setLocationHref,
|
configurable: true,
|
||||||
getLocationHref() {
|
enumerable: true,
|
||||||
return location?.href;
|
},
|
||||||
|
host: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.host;
|
||||||
},
|
},
|
||||||
};
|
configurable: true,
|
||||||
})(this);
|
enumerable: true,
|
||||||
|
},
|
||||||
|
hostname: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.hostname;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
href: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.href;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
origin: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.origin;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
pathname: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.pathname;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
port: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.port;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
protocol: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.protocol;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
get() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.search;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
},
|
||||||
|
toString: {
|
||||||
|
value: function toString() {
|
||||||
|
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||||
|
if (url == null) {
|
||||||
|
throw new TypeError("Illegal invocation.");
|
||||||
|
}
|
||||||
|
return url.href;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
writable: true,
|
||||||
|
},
|
||||||
|
[SymbolToStringTag]: {
|
||||||
|
value: "WorkerLocation",
|
||||||
|
configurable: true,
|
||||||
|
},
|
||||||
|
[SymbolFor("Deno.privateCustomInspect")]: {
|
||||||
|
value: function (inspect) {
|
||||||
|
const object = {
|
||||||
|
hash: this.hash,
|
||||||
|
host: this.host,
|
||||||
|
hostname: this.hostname,
|
||||||
|
href: this.href,
|
||||||
|
origin: this.origin,
|
||||||
|
pathname: this.pathname,
|
||||||
|
port: this.port,
|
||||||
|
protocol: this.protocol,
|
||||||
|
search: this.search,
|
||||||
|
};
|
||||||
|
return `${this.constructor.name} ${inspect(object)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
let location = undefined;
|
||||||
|
let workerLocation = undefined;
|
||||||
|
|
||||||
|
function setLocationHref(href) {
|
||||||
|
location = new Location(href, locationConstructorKey);
|
||||||
|
workerLocation = new WorkerLocation(href, locationConstructorKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocationHref() {
|
||||||
|
return location?.href;
|
||||||
|
}
|
||||||
|
|
||||||
|
const locationConstructorDescriptor = {
|
||||||
|
value: Location,
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const workerLocationConstructorDescriptor = {
|
||||||
|
value: WorkerLocation,
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
const locationDescriptor = {
|
||||||
|
get() {
|
||||||
|
return location;
|
||||||
|
},
|
||||||
|
set() {
|
||||||
|
throw new DOMException(`Cannot set "location".`, "NotSupportedError");
|
||||||
|
},
|
||||||
|
enumerable: true,
|
||||||
|
};
|
||||||
|
const workerLocationDescriptor = {
|
||||||
|
get() {
|
||||||
|
if (workerLocation == null) {
|
||||||
|
throw new Error(
|
||||||
|
`Assertion: "globalThis.location" must be defined in a worker.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return workerLocation;
|
||||||
|
},
|
||||||
|
configurable: true,
|
||||||
|
enumerable: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
getLocationHref,
|
||||||
|
locationConstructorDescriptor,
|
||||||
|
locationDescriptor,
|
||||||
|
setLocationHref,
|
||||||
|
workerLocationConstructorDescriptor,
|
||||||
|
workerLocationDescriptor,
|
||||||
|
};
|
||||||
|
|
|
@ -6,338 +6,339 @@
|
||||||
/// <reference path="./internal.d.ts" />
|
/// <reference path="./internal.d.ts" />
|
||||||
/// <reference path="./lib.deno_web.d.ts" />
|
/// <reference path="./lib.deno_web.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const { InterruptedPrototype, ops } = core;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import {
|
||||||
|
defineEventHandler,
|
||||||
|
EventTarget,
|
||||||
|
MessageEvent,
|
||||||
|
setEventTargetData,
|
||||||
|
} from "internal:ext/web/02_event.js";
|
||||||
|
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayBufferPrototype,
|
||||||
|
ArrayPrototypeFilter,
|
||||||
|
ArrayPrototypeIncludes,
|
||||||
|
ArrayPrototypePush,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
ObjectSetPrototypeOf,
|
||||||
|
Symbol,
|
||||||
|
SymbolFor,
|
||||||
|
SymbolIterator,
|
||||||
|
TypeError,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
((window) => {
|
class MessageChannel {
|
||||||
const core = window.Deno.core;
|
/** @type {MessagePort} */
|
||||||
const { InterruptedPrototype, ops } = core;
|
#port1;
|
||||||
const webidl = window.__bootstrap.webidl;
|
/** @type {MessagePort} */
|
||||||
const { EventTarget, setEventTargetData } = window.__bootstrap.eventTarget;
|
#port2;
|
||||||
const { MessageEvent, defineEventHandler } = window.__bootstrap.event;
|
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
|
||||||
const {
|
|
||||||
ArrayBufferPrototype,
|
|
||||||
ArrayPrototypeFilter,
|
|
||||||
ArrayPrototypeIncludes,
|
|
||||||
ArrayPrototypePush,
|
|
||||||
ObjectPrototypeIsPrototypeOf,
|
|
||||||
ObjectSetPrototypeOf,
|
|
||||||
Symbol,
|
|
||||||
SymbolFor,
|
|
||||||
SymbolIterator,
|
|
||||||
TypeError,
|
|
||||||
} = window.__bootstrap.primordials;
|
|
||||||
|
|
||||||
class MessageChannel {
|
constructor() {
|
||||||
/** @type {MessagePort} */
|
this[webidl.brand] = webidl.brand;
|
||||||
#port1;
|
const { 0: port1Id, 1: port2Id } = opCreateEntangledMessagePort();
|
||||||
/** @type {MessagePort} */
|
const port1 = createMessagePort(port1Id);
|
||||||
#port2;
|
const port2 = createMessagePort(port2Id);
|
||||||
|
this.#port1 = port1;
|
||||||
constructor() {
|
this.#port2 = port2;
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
const { 0: port1Id, 1: port2Id } = opCreateEntangledMessagePort();
|
|
||||||
const port1 = createMessagePort(port1Id);
|
|
||||||
const port2 = createMessagePort(port2Id);
|
|
||||||
this.#port1 = port1;
|
|
||||||
this.#port2 = port2;
|
|
||||||
}
|
|
||||||
|
|
||||||
get port1() {
|
|
||||||
webidl.assertBranded(this, MessageChannelPrototype);
|
|
||||||
return this.#port1;
|
|
||||||
}
|
|
||||||
|
|
||||||
get port2() {
|
|
||||||
webidl.assertBranded(this, MessageChannelPrototype);
|
|
||||||
return this.#port2;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SymbolFor("Deno.inspect")](inspect) {
|
|
||||||
return `MessageChannel ${
|
|
||||||
inspect({ port1: this.port1, port2: this.port2 })
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(MessageChannel);
|
get port1() {
|
||||||
const MessageChannelPrototype = MessageChannel.prototype;
|
webidl.assertBranded(this, MessageChannelPrototype);
|
||||||
|
return this.#port1;
|
||||||
const _id = Symbol("id");
|
|
||||||
const _enabled = Symbol("enabled");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {number} id
|
|
||||||
* @returns {MessagePort}
|
|
||||||
*/
|
|
||||||
function createMessagePort(id) {
|
|
||||||
const port = core.createHostObject();
|
|
||||||
ObjectSetPrototypeOf(port, MessagePortPrototype);
|
|
||||||
port[webidl.brand] = webidl.brand;
|
|
||||||
setEventTargetData(port);
|
|
||||||
port[_id] = id;
|
|
||||||
return port;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MessagePort extends EventTarget {
|
get port2() {
|
||||||
/** @type {number | null} */
|
webidl.assertBranded(this, MessageChannelPrototype);
|
||||||
[_id] = null;
|
return this.#port2;
|
||||||
/** @type {boolean} */
|
|
||||||
[_enabled] = false;
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
webidl.illegalConstructor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {any} message
|
|
||||||
* @param {object[] | StructuredSerializeOptions} transferOrOptions
|
|
||||||
*/
|
|
||||||
postMessage(message, transferOrOptions = {}) {
|
|
||||||
webidl.assertBranded(this, MessagePortPrototype);
|
|
||||||
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
message = webidl.converters.any(message);
|
|
||||||
let options;
|
|
||||||
if (
|
|
||||||
webidl.type(transferOrOptions) === "Object" &&
|
|
||||||
transferOrOptions !== undefined &&
|
|
||||||
transferOrOptions[SymbolIterator] !== undefined
|
|
||||||
) {
|
|
||||||
const transfer = webidl.converters["sequence<object>"](
|
|
||||||
transferOrOptions,
|
|
||||||
{ prefix, context: "Argument 2" },
|
|
||||||
);
|
|
||||||
options = { transfer };
|
|
||||||
} else {
|
|
||||||
options = webidl.converters.StructuredSerializeOptions(
|
|
||||||
transferOrOptions,
|
|
||||||
{
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const { transfer } = options;
|
|
||||||
if (ArrayPrototypeIncludes(transfer, this)) {
|
|
||||||
throw new DOMException("Can not tranfer self", "DataCloneError");
|
|
||||||
}
|
|
||||||
const data = serializeJsMessageData(message, transfer);
|
|
||||||
if (this[_id] === null) return;
|
|
||||||
ops.op_message_port_post_message(this[_id], data);
|
|
||||||
}
|
|
||||||
|
|
||||||
start() {
|
|
||||||
webidl.assertBranded(this, MessagePortPrototype);
|
|
||||||
if (this[_enabled]) return;
|
|
||||||
(async () => {
|
|
||||||
this[_enabled] = true;
|
|
||||||
while (true) {
|
|
||||||
if (this[_id] === null) break;
|
|
||||||
let data;
|
|
||||||
try {
|
|
||||||
data = await core.opAsync(
|
|
||||||
"op_message_port_recv_message",
|
|
||||||
this[_id],
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(InterruptedPrototype, err)) break;
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
if (data === null) break;
|
|
||||||
let message, transferables;
|
|
||||||
try {
|
|
||||||
const v = deserializeJsMessageData(data);
|
|
||||||
message = v[0];
|
|
||||||
transferables = v[1];
|
|
||||||
} catch (err) {
|
|
||||||
const event = new MessageEvent("messageerror", { data: err });
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const event = new MessageEvent("message", {
|
|
||||||
data: message,
|
|
||||||
ports: ArrayPrototypeFilter(
|
|
||||||
transferables,
|
|
||||||
(t) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t),
|
|
||||||
),
|
|
||||||
});
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
}
|
|
||||||
this[_enabled] = false;
|
|
||||||
})();
|
|
||||||
}
|
|
||||||
|
|
||||||
close() {
|
|
||||||
webidl.assertBranded(this, MessagePortPrototype);
|
|
||||||
if (this[_id] !== null) {
|
|
||||||
core.close(this[_id]);
|
|
||||||
this[_id] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineEventHandler(MessagePort.prototype, "message", function (self) {
|
[SymbolFor("Deno.inspect")](inspect) {
|
||||||
self.start();
|
return `MessageChannel ${
|
||||||
});
|
inspect({ port1: this.port1, port2: this.port2 })
|
||||||
defineEventHandler(MessagePort.prototype, "messageerror");
|
}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(MessagePort);
|
webidl.configurePrototype(MessageChannel);
|
||||||
const MessagePortPrototype = MessagePort.prototype;
|
const MessageChannelPrototype = MessageChannel.prototype;
|
||||||
|
|
||||||
/**
|
const _id = Symbol("id");
|
||||||
* @returns {[number, number]}
|
const _enabled = Symbol("enabled");
|
||||||
*/
|
|
||||||
function opCreateEntangledMessagePort() {
|
/**
|
||||||
return ops.op_message_port_create_entangled();
|
* @param {number} id
|
||||||
|
* @returns {MessagePort}
|
||||||
|
*/
|
||||||
|
function createMessagePort(id) {
|
||||||
|
const port = core.createHostObject();
|
||||||
|
ObjectSetPrototypeOf(port, MessagePortPrototype);
|
||||||
|
port[webidl.brand] = webidl.brand;
|
||||||
|
setEventTargetData(port);
|
||||||
|
port[_id] = id;
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessagePort extends EventTarget {
|
||||||
|
/** @type {number | null} */
|
||||||
|
[_id] = null;
|
||||||
|
/** @type {boolean} */
|
||||||
|
[_enabled] = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
webidl.illegalConstructor();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {globalThis.__bootstrap.messagePort.MessageData} messageData
|
* @param {any} message
|
||||||
* @returns {[any, object[]]}
|
* @param {object[] | StructuredSerializeOptions} transferOrOptions
|
||||||
*/
|
*/
|
||||||
function deserializeJsMessageData(messageData) {
|
postMessage(message, transferOrOptions = {}) {
|
||||||
/** @type {object[]} */
|
webidl.assertBranded(this, MessagePortPrototype);
|
||||||
const transferables = [];
|
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
|
||||||
const hostObjects = [];
|
|
||||||
const arrayBufferIdsInTransferables = [];
|
|
||||||
const transferredArrayBuffers = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < messageData.transferables.length; ++i) {
|
|
||||||
const transferable = messageData.transferables[i];
|
|
||||||
switch (transferable.kind) {
|
|
||||||
case "messagePort": {
|
|
||||||
const port = createMessagePort(transferable.data);
|
|
||||||
ArrayPrototypePush(transferables, port);
|
|
||||||
ArrayPrototypePush(hostObjects, port);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "arrayBuffer": {
|
|
||||||
ArrayPrototypePush(transferredArrayBuffers, transferable.data);
|
|
||||||
const index = ArrayPrototypePush(transferables, null);
|
|
||||||
ArrayPrototypePush(arrayBufferIdsInTransferables, index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new TypeError("Unreachable");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = core.deserialize(messageData.data, {
|
|
||||||
hostObjects,
|
|
||||||
transferredArrayBuffers,
|
|
||||||
});
|
|
||||||
|
|
||||||
for (let i = 0; i < arrayBufferIdsInTransferables.length; ++i) {
|
|
||||||
const id = arrayBufferIdsInTransferables[i];
|
|
||||||
transferables[id] = transferredArrayBuffers[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [data, transferables];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {any} data
|
|
||||||
* @param {object[]} transferables
|
|
||||||
* @returns {globalThis.__bootstrap.messagePort.MessageData}
|
|
||||||
*/
|
|
||||||
function serializeJsMessageData(data, transferables) {
|
|
||||||
const transferredArrayBuffers = [];
|
|
||||||
for (let i = 0, j = 0; i < transferables.length; i++) {
|
|
||||||
const ab = transferables[i];
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, ab)) {
|
|
||||||
if (ab.byteLength === 0 && core.ops.op_arraybuffer_was_detached(ab)) {
|
|
||||||
throw new DOMException(
|
|
||||||
`ArrayBuffer at index ${j} is already detached`,
|
|
||||||
"DataCloneError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
j++;
|
|
||||||
transferredArrayBuffers.push(ab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const serializedData = core.serialize(data, {
|
|
||||||
hostObjects: ArrayPrototypeFilter(
|
|
||||||
transferables,
|
|
||||||
(a) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, a),
|
|
||||||
),
|
|
||||||
transferredArrayBuffers,
|
|
||||||
}, (err) => {
|
|
||||||
throw new DOMException(err, "DataCloneError");
|
|
||||||
});
|
|
||||||
|
|
||||||
/** @type {globalThis.__bootstrap.messagePort.Transferable[]} */
|
|
||||||
const serializedTransferables = [];
|
|
||||||
|
|
||||||
let arrayBufferI = 0;
|
|
||||||
for (let i = 0; i < transferables.length; ++i) {
|
|
||||||
const transferable = transferables[i];
|
|
||||||
if (ObjectPrototypeIsPrototypeOf(MessagePortPrototype, transferable)) {
|
|
||||||
webidl.assertBranded(transferable, MessagePortPrototype);
|
|
||||||
const id = transferable[_id];
|
|
||||||
if (id === null) {
|
|
||||||
throw new DOMException(
|
|
||||||
"Can not transfer disentangled message port",
|
|
||||||
"DataCloneError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
transferable[_id] = null;
|
|
||||||
ArrayPrototypePush(serializedTransferables, {
|
|
||||||
kind: "messagePort",
|
|
||||||
data: id,
|
|
||||||
});
|
|
||||||
} else if (
|
|
||||||
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, transferable)
|
|
||||||
) {
|
|
||||||
ArrayPrototypePush(serializedTransferables, {
|
|
||||||
kind: "arrayBuffer",
|
|
||||||
data: transferredArrayBuffers[arrayBufferI],
|
|
||||||
});
|
|
||||||
arrayBufferI++;
|
|
||||||
} else {
|
|
||||||
throw new DOMException("Value not transferable", "DataCloneError");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
data: serializedData,
|
|
||||||
transferables: serializedTransferables,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
webidl.converters.StructuredSerializeOptions = webidl
|
|
||||||
.createDictionaryConverter(
|
|
||||||
"StructuredSerializeOptions",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: "transfer",
|
|
||||||
converter: webidl.converters["sequence<object>"],
|
|
||||||
get defaultValue() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
function structuredClone(value, options) {
|
|
||||||
const prefix = "Failed to execute 'structuredClone'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
options = webidl.converters.StructuredSerializeOptions(options, {
|
message = webidl.converters.any(message);
|
||||||
prefix,
|
let options;
|
||||||
context: "Argument 2",
|
if (
|
||||||
});
|
webidl.type(transferOrOptions) === "Object" &&
|
||||||
const messageData = serializeJsMessageData(value, options.transfer);
|
transferOrOptions !== undefined &&
|
||||||
return deserializeJsMessageData(messageData)[0];
|
transferOrOptions[SymbolIterator] !== undefined
|
||||||
|
) {
|
||||||
|
const transfer = webidl.converters["sequence<object>"](
|
||||||
|
transferOrOptions,
|
||||||
|
{ prefix, context: "Argument 2" },
|
||||||
|
);
|
||||||
|
options = { transfer };
|
||||||
|
} else {
|
||||||
|
options = webidl.converters.StructuredSerializeOptions(
|
||||||
|
transferOrOptions,
|
||||||
|
{
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { transfer } = options;
|
||||||
|
if (ArrayPrototypeIncludes(transfer, this)) {
|
||||||
|
throw new DOMException("Can not tranfer self", "DataCloneError");
|
||||||
|
}
|
||||||
|
const data = serializeJsMessageData(message, transfer);
|
||||||
|
if (this[_id] === null) return;
|
||||||
|
ops.op_message_port_post_message(this[_id], data);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.messagePort = {
|
start() {
|
||||||
MessageChannel,
|
webidl.assertBranded(this, MessagePortPrototype);
|
||||||
MessagePort,
|
if (this[_enabled]) return;
|
||||||
MessagePortPrototype,
|
(async () => {
|
||||||
deserializeJsMessageData,
|
this[_enabled] = true;
|
||||||
serializeJsMessageData,
|
while (true) {
|
||||||
structuredClone,
|
if (this[_id] === null) break;
|
||||||
|
let data;
|
||||||
|
try {
|
||||||
|
data = await core.opAsync(
|
||||||
|
"op_message_port_recv_message",
|
||||||
|
this[_id],
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(InterruptedPrototype, err)) break;
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
if (data === null) break;
|
||||||
|
let message, transferables;
|
||||||
|
try {
|
||||||
|
const v = deserializeJsMessageData(data);
|
||||||
|
message = v[0];
|
||||||
|
transferables = v[1];
|
||||||
|
} catch (err) {
|
||||||
|
const event = new MessageEvent("messageerror", { data: err });
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const event = new MessageEvent("message", {
|
||||||
|
data: message,
|
||||||
|
ports: ArrayPrototypeFilter(
|
||||||
|
transferables,
|
||||||
|
(t) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
this.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
this[_enabled] = false;
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
close() {
|
||||||
|
webidl.assertBranded(this, MessagePortPrototype);
|
||||||
|
if (this[_id] !== null) {
|
||||||
|
core.close(this[_id]);
|
||||||
|
this[_id] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineEventHandler(MessagePort.prototype, "message", function (self) {
|
||||||
|
self.start();
|
||||||
|
});
|
||||||
|
defineEventHandler(MessagePort.prototype, "messageerror");
|
||||||
|
|
||||||
|
webidl.configurePrototype(MessagePort);
|
||||||
|
const MessagePortPrototype = MessagePort.prototype;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns {[number, number]}
|
||||||
|
*/
|
||||||
|
function opCreateEntangledMessagePort() {
|
||||||
|
return ops.op_message_port_create_entangled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {messagePort.MessageData} messageData
|
||||||
|
* @returns {[any, object[]]}
|
||||||
|
*/
|
||||||
|
function deserializeJsMessageData(messageData) {
|
||||||
|
/** @type {object[]} */
|
||||||
|
const transferables = [];
|
||||||
|
const hostObjects = [];
|
||||||
|
const arrayBufferIdsInTransferables = [];
|
||||||
|
const transferredArrayBuffers = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < messageData.transferables.length; ++i) {
|
||||||
|
const transferable = messageData.transferables[i];
|
||||||
|
switch (transferable.kind) {
|
||||||
|
case "messagePort": {
|
||||||
|
const port = createMessagePort(transferable.data);
|
||||||
|
ArrayPrototypePush(transferables, port);
|
||||||
|
ArrayPrototypePush(hostObjects, port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "arrayBuffer": {
|
||||||
|
ArrayPrototypePush(transferredArrayBuffers, transferable.data);
|
||||||
|
const index = ArrayPrototypePush(transferables, null);
|
||||||
|
ArrayPrototypePush(arrayBufferIdsInTransferables, index);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new TypeError("Unreachable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = core.deserialize(messageData.data, {
|
||||||
|
hostObjects,
|
||||||
|
transferredArrayBuffers,
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = 0; i < arrayBufferIdsInTransferables.length; ++i) {
|
||||||
|
const id = arrayBufferIdsInTransferables[i];
|
||||||
|
transferables[id] = transferredArrayBuffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [data, transferables];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {any} data
|
||||||
|
* @param {object[]} transferables
|
||||||
|
* @returns {messagePort.MessageData}
|
||||||
|
*/
|
||||||
|
function serializeJsMessageData(data, transferables) {
|
||||||
|
const transferredArrayBuffers = [];
|
||||||
|
for (let i = 0, j = 0; i < transferables.length; i++) {
|
||||||
|
const ab = transferables[i];
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, ab)) {
|
||||||
|
if (ab.byteLength === 0 && ops.op_arraybuffer_was_detached(ab)) {
|
||||||
|
throw new DOMException(
|
||||||
|
`ArrayBuffer at index ${j} is already detached`,
|
||||||
|
"DataCloneError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
j++;
|
||||||
|
transferredArrayBuffers.push(ab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const serializedData = core.serialize(data, {
|
||||||
|
hostObjects: ArrayPrototypeFilter(
|
||||||
|
transferables,
|
||||||
|
(a) => ObjectPrototypeIsPrototypeOf(MessagePortPrototype, a),
|
||||||
|
),
|
||||||
|
transferredArrayBuffers,
|
||||||
|
}, (err) => {
|
||||||
|
throw new DOMException(err, "DataCloneError");
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @type {messagePort.Transferable[]} */
|
||||||
|
const serializedTransferables = [];
|
||||||
|
|
||||||
|
let arrayBufferI = 0;
|
||||||
|
for (let i = 0; i < transferables.length; ++i) {
|
||||||
|
const transferable = transferables[i];
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(MessagePortPrototype, transferable)) {
|
||||||
|
webidl.assertBranded(transferable, MessagePortPrototype);
|
||||||
|
const id = transferable[_id];
|
||||||
|
if (id === null) {
|
||||||
|
throw new DOMException(
|
||||||
|
"Can not transfer disentangled message port",
|
||||||
|
"DataCloneError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
transferable[_id] = null;
|
||||||
|
ArrayPrototypePush(serializedTransferables, {
|
||||||
|
kind: "messagePort",
|
||||||
|
data: id,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, transferable)
|
||||||
|
) {
|
||||||
|
ArrayPrototypePush(serializedTransferables, {
|
||||||
|
kind: "arrayBuffer",
|
||||||
|
data: transferredArrayBuffers[arrayBufferI],
|
||||||
|
});
|
||||||
|
arrayBufferI++;
|
||||||
|
} else {
|
||||||
|
throw new DOMException("Value not transferable", "DataCloneError");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: serializedData,
|
||||||
|
transferables: serializedTransferables,
|
||||||
};
|
};
|
||||||
})(globalThis);
|
}
|
||||||
|
|
||||||
|
webidl.converters.StructuredSerializeOptions = webidl
|
||||||
|
.createDictionaryConverter(
|
||||||
|
"StructuredSerializeOptions",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: "transfer",
|
||||||
|
converter: webidl.converters["sequence<object>"],
|
||||||
|
get defaultValue() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
function structuredClone(value, options) {
|
||||||
|
const prefix = "Failed to execute 'structuredClone'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
options = webidl.converters.StructuredSerializeOptions(options, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
const messageData = serializeJsMessageData(value, options.transfer);
|
||||||
|
return deserializeJsMessageData(messageData)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
deserializeJsMessageData,
|
||||||
|
MessageChannel,
|
||||||
|
MessagePort,
|
||||||
|
MessagePortPrototype,
|
||||||
|
serializeJsMessageData,
|
||||||
|
structuredClone,
|
||||||
|
};
|
||||||
|
|
|
@ -5,127 +5,120 @@
|
||||||
/// <reference path="./internal.d.ts" />
|
/// <reference path="./internal.d.ts" />
|
||||||
/// <reference path="./lib.deno_web.d.ts" />
|
/// <reference path="./lib.deno_web.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import { TransformStream } from "internal:ext/web/06_streams.js";
|
||||||
|
|
||||||
((window) => {
|
webidl.converters.CompressionFormat = webidl.createEnumConverter(
|
||||||
const core = window.Deno.core;
|
"CompressionFormat",
|
||||||
const ops = core.ops;
|
[
|
||||||
const webidl = window.__bootstrap.webidl;
|
"deflate",
|
||||||
const { TransformStream } = window.__bootstrap.streams;
|
"deflate-raw",
|
||||||
|
"gzip",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
webidl.converters.CompressionFormat = webidl.createEnumConverter(
|
class CompressionStream {
|
||||||
"CompressionFormat",
|
#transform;
|
||||||
[
|
|
||||||
"deflate",
|
|
||||||
"deflate-raw",
|
|
||||||
"gzip",
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
class CompressionStream {
|
constructor(format) {
|
||||||
#transform;
|
const prefix = "Failed to construct 'CompressionStream'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
format = webidl.converters.CompressionFormat(format, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
constructor(format) {
|
const rid = ops.op_compression_new(format, false);
|
||||||
const prefix = "Failed to construct 'CompressionStream'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
format = webidl.converters.CompressionFormat(format, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
const rid = ops.op_compression_new(format, false);
|
this.#transform = new TransformStream({
|
||||||
|
transform(chunk, controller) {
|
||||||
|
chunk = webidl.converters.BufferSource(chunk, {
|
||||||
|
prefix,
|
||||||
|
context: "chunk",
|
||||||
|
});
|
||||||
|
const output = ops.op_compression_write(
|
||||||
|
rid,
|
||||||
|
chunk,
|
||||||
|
);
|
||||||
|
maybeEnqueue(controller, output);
|
||||||
|
},
|
||||||
|
flush(controller) {
|
||||||
|
const output = ops.op_compression_finish(rid);
|
||||||
|
maybeEnqueue(controller, output);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
this.#transform = new TransformStream({
|
this[webidl.brand] = webidl.brand;
|
||||||
transform(chunk, controller) {
|
|
||||||
chunk = webidl.converters.BufferSource(chunk, {
|
|
||||||
prefix,
|
|
||||||
context: "chunk",
|
|
||||||
});
|
|
||||||
const output = ops.op_compression_write(
|
|
||||||
rid,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
maybeEnqueue(controller, output);
|
|
||||||
},
|
|
||||||
flush(controller) {
|
|
||||||
const output = ops.op_compression_finish(rid);
|
|
||||||
maybeEnqueue(controller, output);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
get readable() {
|
|
||||||
webidl.assertBranded(this, CompressionStreamPrototype);
|
|
||||||
return this.#transform.readable;
|
|
||||||
}
|
|
||||||
|
|
||||||
get writable() {
|
|
||||||
webidl.assertBranded(this, CompressionStreamPrototype);
|
|
||||||
return this.#transform.writable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(CompressionStream);
|
get readable() {
|
||||||
const CompressionStreamPrototype = CompressionStream.prototype;
|
webidl.assertBranded(this, CompressionStreamPrototype);
|
||||||
|
return this.#transform.readable;
|
||||||
class DecompressionStream {
|
|
||||||
#transform;
|
|
||||||
|
|
||||||
constructor(format) {
|
|
||||||
const prefix = "Failed to construct 'DecompressionStream'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
format = webidl.converters.CompressionFormat(format, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
const rid = ops.op_compression_new(format, true);
|
|
||||||
|
|
||||||
this.#transform = new TransformStream({
|
|
||||||
transform(chunk, controller) {
|
|
||||||
chunk = webidl.converters.BufferSource(chunk, {
|
|
||||||
prefix,
|
|
||||||
context: "chunk",
|
|
||||||
});
|
|
||||||
const output = ops.op_compression_write(
|
|
||||||
rid,
|
|
||||||
chunk,
|
|
||||||
);
|
|
||||||
maybeEnqueue(controller, output);
|
|
||||||
},
|
|
||||||
flush(controller) {
|
|
||||||
const output = ops.op_compression_finish(rid);
|
|
||||||
maybeEnqueue(controller, output);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
this[webidl.brand] = webidl.brand;
|
|
||||||
}
|
|
||||||
|
|
||||||
get readable() {
|
|
||||||
webidl.assertBranded(this, DecompressionStreamPrototype);
|
|
||||||
return this.#transform.readable;
|
|
||||||
}
|
|
||||||
|
|
||||||
get writable() {
|
|
||||||
webidl.assertBranded(this, DecompressionStreamPrototype);
|
|
||||||
return this.#transform.writable;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function maybeEnqueue(controller, output) {
|
get writable() {
|
||||||
if (output && output.byteLength > 0) {
|
webidl.assertBranded(this, CompressionStreamPrototype);
|
||||||
controller.enqueue(output);
|
return this.#transform.writable;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(CompressionStream);
|
||||||
|
const CompressionStreamPrototype = CompressionStream.prototype;
|
||||||
|
|
||||||
|
class DecompressionStream {
|
||||||
|
#transform;
|
||||||
|
|
||||||
|
constructor(format) {
|
||||||
|
const prefix = "Failed to construct 'DecompressionStream'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
format = webidl.converters.CompressionFormat(format, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
const rid = ops.op_compression_new(format, true);
|
||||||
|
|
||||||
|
this.#transform = new TransformStream({
|
||||||
|
transform(chunk, controller) {
|
||||||
|
chunk = webidl.converters.BufferSource(chunk, {
|
||||||
|
prefix,
|
||||||
|
context: "chunk",
|
||||||
|
});
|
||||||
|
const output = ops.op_compression_write(
|
||||||
|
rid,
|
||||||
|
chunk,
|
||||||
|
);
|
||||||
|
maybeEnqueue(controller, output);
|
||||||
|
},
|
||||||
|
flush(controller) {
|
||||||
|
const output = ops.op_compression_finish(rid);
|
||||||
|
maybeEnqueue(controller, output);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this[webidl.brand] = webidl.brand;
|
||||||
}
|
}
|
||||||
|
|
||||||
webidl.configurePrototype(DecompressionStream);
|
get readable() {
|
||||||
const DecompressionStreamPrototype = DecompressionStream.prototype;
|
webidl.assertBranded(this, DecompressionStreamPrototype);
|
||||||
|
return this.#transform.readable;
|
||||||
|
}
|
||||||
|
|
||||||
window.__bootstrap.compression = {
|
get writable() {
|
||||||
CompressionStream,
|
webidl.assertBranded(this, DecompressionStreamPrototype);
|
||||||
DecompressionStream,
|
return this.#transform.writable;
|
||||||
};
|
}
|
||||||
})(globalThis);
|
}
|
||||||
|
|
||||||
|
function maybeEnqueue(controller, output) {
|
||||||
|
if (output && output.byteLength > 0) {
|
||||||
|
controller.enqueue(output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webidl.configurePrototype(DecompressionStream);
|
||||||
|
const DecompressionStreamPrototype = DecompressionStream.prototype;
|
||||||
|
|
||||||
|
export { CompressionStream, DecompressionStream };
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,11 +29,12 @@ fn setup() -> Vec<Extension> {
|
||||||
deno_console::init(),
|
deno_console::init(),
|
||||||
deno_web::init::<Permissions>(BlobStore::default(), None),
|
deno_web::init::<Permissions>(BlobStore::default(), None),
|
||||||
Extension::builder("bench_setup")
|
Extension::builder("bench_setup")
|
||||||
.js(vec![(
|
.esm(vec![(
|
||||||
"setup",
|
"internal:setup",
|
||||||
r#"
|
r#"
|
||||||
const { TextDecoder } = globalThis.__bootstrap.encoding;
|
import { TextDecoder } from "internal:ext/web/08_text_encoding.js";
|
||||||
const hello12k = Deno.core.encode("hello world\n".repeat(1e3));
|
globalThis.TextDecoder = TextDecoder;
|
||||||
|
globalThis.hello12k = Deno.core.encode("hello world\n".repeat(1e3));
|
||||||
"#,
|
"#,
|
||||||
)])
|
)])
|
||||||
.state(|state| {
|
.state(|state| {
|
||||||
|
|
|
@ -28,9 +28,10 @@ fn setup() -> Vec<Extension> {
|
||||||
deno_console::init(),
|
deno_console::init(),
|
||||||
deno_web::init::<Permissions>(BlobStore::default(), None),
|
deno_web::init::<Permissions>(BlobStore::default(), None),
|
||||||
Extension::builder("bench_setup")
|
Extension::builder("bench_setup")
|
||||||
.js(vec![
|
.esm(vec![
|
||||||
("setup", r#"
|
("internal:setup", r#"
|
||||||
const { setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers;
|
import { setTimeout, handleTimerMacrotask } from "internal:ext/web/02_timers.js";
|
||||||
|
globalThis.setTimeout = setTimeout;
|
||||||
Deno.core.setMacrotaskCallback(handleTimerMacrotask);
|
Deno.core.setMacrotaskCallback(handleTimerMacrotask);
|
||||||
"#),
|
"#),
|
||||||
])
|
])
|
||||||
|
|
201
ext/web/internal.d.ts
vendored
201
ext/web/internal.d.ts
vendored
|
@ -1,120 +1,111 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
|
|
||||||
// deno-lint-ignore-file no-var
|
|
||||||
|
|
||||||
/// <reference no-default-lib="true" />
|
/// <reference no-default-lib="true" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
declare namespace globalThis {
|
declare module "internal:ext/web/00_infra.js" {
|
||||||
declare namespace __bootstrap {
|
function collectSequenceOfCodepoints(
|
||||||
declare var infra: {
|
input: string,
|
||||||
collectSequenceOfCodepoints(
|
position: number,
|
||||||
input: string,
|
condition: (char: string) => boolean,
|
||||||
position: number,
|
): {
|
||||||
condition: (char: string) => boolean,
|
result: string;
|
||||||
): {
|
position: number;
|
||||||
result: string;
|
};
|
||||||
position: number;
|
const ASCII_DIGIT: string[];
|
||||||
};
|
const ASCII_UPPER_ALPHA: string[];
|
||||||
ASCII_DIGIT: string[];
|
const ASCII_LOWER_ALPHA: string[];
|
||||||
ASCII_UPPER_ALPHA: string[];
|
const ASCII_ALPHA: string[];
|
||||||
ASCII_LOWER_ALPHA: string[];
|
const ASCII_ALPHANUMERIC: string[];
|
||||||
ASCII_ALPHA: string[];
|
const HTTP_TAB_OR_SPACE: string[];
|
||||||
ASCII_ALPHANUMERIC: string[];
|
const HTTP_WHITESPACE: string[];
|
||||||
HTTP_TAB_OR_SPACE: string[];
|
const HTTP_TOKEN_CODE_POINT: string[];
|
||||||
HTTP_WHITESPACE: string[];
|
const HTTP_TOKEN_CODE_POINT_RE: RegExp;
|
||||||
HTTP_TOKEN_CODE_POINT: string[];
|
const HTTP_QUOTED_STRING_TOKEN_POINT: string[];
|
||||||
HTTP_TOKEN_CODE_POINT_RE: RegExp;
|
const HTTP_QUOTED_STRING_TOKEN_POINT_RE: RegExp;
|
||||||
HTTP_QUOTED_STRING_TOKEN_POINT: string[];
|
const HTTP_TAB_OR_SPACE_PREFIX_RE: RegExp;
|
||||||
HTTP_QUOTED_STRING_TOKEN_POINT_RE: RegExp;
|
const HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp;
|
||||||
HTTP_TAB_OR_SPACE_PREFIX_RE: RegExp;
|
const HTTP_WHITESPACE_PREFIX_RE: RegExp;
|
||||||
HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp;
|
const HTTP_WHITESPACE_SUFFIX_RE: RegExp;
|
||||||
HTTP_WHITESPACE_PREFIX_RE: RegExp;
|
function httpTrim(s: string): string;
|
||||||
HTTP_WHITESPACE_SUFFIX_RE: RegExp;
|
function regexMatcher(chars: string[]): string;
|
||||||
httpTrim(s: string): string;
|
function byteUpperCase(s: string): string;
|
||||||
regexMatcher(chars: string[]): string;
|
function byteLowerCase(s: string): string;
|
||||||
byteUpperCase(s: string): string;
|
function collectHttpQuotedString(
|
||||||
byteLowerCase(s: string): string;
|
input: string,
|
||||||
collectHttpQuotedString(
|
position: number,
|
||||||
input: string,
|
extractValue: boolean,
|
||||||
position: number,
|
): {
|
||||||
extractValue: boolean,
|
result: string;
|
||||||
): {
|
position: number;
|
||||||
result: string;
|
};
|
||||||
position: number;
|
function forgivingBase64Encode(data: Uint8Array): string;
|
||||||
};
|
function forgivingBase64Decode(data: string): Uint8Array;
|
||||||
forgivingBase64Encode(data: Uint8Array): string;
|
function serializeJSValueToJSONString(value: unknown): string;
|
||||||
forgivingBase64Decode(data: string): Uint8Array;
|
}
|
||||||
serializeJSValueToJSONString(value: unknown): string;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare var domException: {
|
declare module "internal:ext/web/01_dom_exception.js" {
|
||||||
DOMException: typeof DOMException;
|
export = DOMException;
|
||||||
};
|
}
|
||||||
|
|
||||||
declare namespace mimesniff {
|
declare module "internal:ext/web/01_mimesniff.js" {
|
||||||
declare interface MimeType {
|
interface MimeType {
|
||||||
type: string;
|
type: string;
|
||||||
subtype: string;
|
subtype: string;
|
||||||
parameters: Map<string, string>;
|
parameters: Map<string, string>;
|
||||||
}
|
}
|
||||||
declare function parseMimeType(input: string): MimeType | null;
|
function parseMimeType(input: string): MimeType | null;
|
||||||
declare function essence(mimeType: MimeType): string;
|
function essence(mimeType: MimeType): string;
|
||||||
declare function serializeMimeType(mimeType: MimeType): string;
|
function serializeMimeType(mimeType: MimeType): string;
|
||||||
declare function extractMimeType(
|
function extractMimeType(
|
||||||
headerValues: string[] | null,
|
headerValues: string[] | null,
|
||||||
): MimeType | null;
|
): MimeType | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
declare var eventTarget: {
|
declare module "internal:ext/web/02_event.js" {
|
||||||
EventTarget: typeof EventTarget;
|
const EventTarget: typeof EventTarget;
|
||||||
};
|
const Event: typeof event;
|
||||||
|
const ErrorEvent: typeof ErrorEvent;
|
||||||
|
const CloseEvent: typeof CloseEvent;
|
||||||
|
const MessageEvent: typeof MessageEvent;
|
||||||
|
const CustomEvent: typeof CustomEvent;
|
||||||
|
const ProgressEvent: typeof ProgressEvent;
|
||||||
|
const PromiseRejectionEvent: typeof PromiseRejectionEvent;
|
||||||
|
const reportError: typeof reportError;
|
||||||
|
}
|
||||||
|
|
||||||
declare var event: {
|
declare module "internal:ext/web/12_location.js" {
|
||||||
Event: typeof event;
|
function getLocationHref(): string | undefined;
|
||||||
ErrorEvent: typeof ErrorEvent;
|
}
|
||||||
CloseEvent: typeof CloseEvent;
|
|
||||||
MessageEvent: typeof MessageEvent;
|
|
||||||
CustomEvent: typeof CustomEvent;
|
|
||||||
ProgressEvent: typeof ProgressEvent;
|
|
||||||
PromiseRejectionEvent: typeof PromiseRejectionEvent;
|
|
||||||
reportError: typeof reportError;
|
|
||||||
};
|
|
||||||
|
|
||||||
declare var location: {
|
declare module "internal:ext/web/05_base64.js" {
|
||||||
getLocationHref(): string | undefined;
|
function atob(data: string): string;
|
||||||
};
|
function btoa(data: string): string;
|
||||||
|
}
|
||||||
|
|
||||||
declare var base64: {
|
declare module "internal:ext/web/09_file.js" {
|
||||||
atob(data: string): string;
|
function blobFromObjectUrl(url: string): Blob | null;
|
||||||
btoa(data: string): string;
|
function getParts(blob: Blob): string[];
|
||||||
};
|
const Blob: typeof Blob;
|
||||||
|
const File: typeof File;
|
||||||
|
}
|
||||||
|
|
||||||
declare var file: {
|
declare module "internal:ext/web/06_streams.js" {
|
||||||
blobFromObjectUrl(url: string): Blob | null;
|
const ReadableStream: typeof ReadableStream;
|
||||||
getParts(blob: Blob): string[];
|
function isReadableStreamDisturbed(stream: ReadableStream): boolean;
|
||||||
Blob: typeof Blob;
|
function createProxy<T>(stream: ReadableStream<T>): ReadableStream<T>;
|
||||||
File: typeof File;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
declare var streams: {
|
declare module "internal:ext/web/13_message_port.js" {
|
||||||
ReadableStream: typeof ReadableStream;
|
type Transferable = {
|
||||||
isReadableStreamDisturbed(stream: ReadableStream): boolean;
|
kind: "messagePort";
|
||||||
createProxy<T>(stream: ReadableStream<T>): ReadableStream<T>;
|
data: number;
|
||||||
};
|
} | {
|
||||||
|
kind: "arrayBuffer";
|
||||||
declare namespace messagePort {
|
data: number;
|
||||||
declare type Transferable = {
|
};
|
||||||
kind: "messagePort";
|
interface MessageData {
|
||||||
data: number;
|
data: Uint8Array;
|
||||||
} | {
|
transferables: Transferable[];
|
||||||
kind: "arrayBuffer";
|
|
||||||
data: number;
|
|
||||||
};
|
|
||||||
declare interface MessageData {
|
|
||||||
data: Uint8Array;
|
|
||||||
transferables: Transferable[];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub fn init<P: TimersPermission + 'static>(
|
||||||
) -> Extension {
|
) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl", "deno_console", "deno_url"])
|
.dependencies(vec!["deno_webidl", "deno_console", "deno_url"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/web",
|
prefix "internal:ext/web",
|
||||||
"00_infra.js",
|
"00_infra.js",
|
||||||
"01_dom_exception.js",
|
"01_dom_exception.js",
|
||||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -6,144 +6,141 @@
|
||||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
/// <reference path="./lib.deno_webgpu.d.ts" />
|
/// <reference path="./lib.deno_webgpu.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
const core = globalThis.Deno.core;
|
||||||
|
const ops = core.ops;
|
||||||
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const { Symbol } = primordials;
|
||||||
|
import {
|
||||||
|
_device,
|
||||||
|
assertDevice,
|
||||||
|
createGPUTexture,
|
||||||
|
} from "internal:ext/webgpu/01_webgpu.js";
|
||||||
|
|
||||||
((window) => {
|
const _surfaceRid = Symbol("[[surfaceRid]]");
|
||||||
const core = window.Deno.core;
|
const _configuration = Symbol("[[configuration]]");
|
||||||
const ops = core.ops;
|
const _canvas = Symbol("[[canvas]]");
|
||||||
const webidl = window.__bootstrap.webidl;
|
const _currentTexture = Symbol("[[currentTexture]]");
|
||||||
const { Symbol } = window.__bootstrap.primordials;
|
class GPUCanvasContext {
|
||||||
const { _device, assertDevice, createGPUTexture } = window.__bootstrap.webgpu;
|
/** @type {number} */
|
||||||
|
[_surfaceRid];
|
||||||
|
/** @type {InnerGPUDevice} */
|
||||||
|
[_device];
|
||||||
|
[_configuration];
|
||||||
|
[_canvas];
|
||||||
|
/** @type {GPUTexture | undefined} */
|
||||||
|
[_currentTexture];
|
||||||
|
|
||||||
const _surfaceRid = Symbol("[[surfaceRid]]");
|
get canvas() {
|
||||||
const _configuration = Symbol("[[configuration]]");
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||||
const _canvas = Symbol("[[canvas]]");
|
return this[_canvas];
|
||||||
const _currentTexture = Symbol("[[currentTexture]]");
|
}
|
||||||
class GPUCanvasContext {
|
|
||||||
/** @type {number} */
|
|
||||||
[_surfaceRid];
|
|
||||||
/** @type {InnerGPUDevice} */
|
|
||||||
[_device];
|
|
||||||
[_configuration];
|
|
||||||
[_canvas];
|
|
||||||
/** @type {GPUTexture | undefined} */
|
|
||||||
[_currentTexture];
|
|
||||||
|
|
||||||
get canvas() {
|
constructor() {
|
||||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
webidl.illegalConstructor();
|
||||||
return this[_canvas];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
configure(configuration) {
|
||||||
webidl.illegalConstructor();
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||||
}
|
const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
configuration = webidl.converters.GPUCanvasConfiguration(configuration, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
configure(configuration) {
|
this[_device] = configuration.device[_device];
|
||||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
this[_configuration] = configuration;
|
||||||
const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'";
|
const device = assertDevice(this, {
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
prefix,
|
||||||
configuration = webidl.converters.GPUCanvasConfiguration(configuration, {
|
context: "configuration.device",
|
||||||
prefix,
|
});
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
this[_device] = configuration.device[_device];
|
const { err } = ops.op_webgpu_surface_configure({
|
||||||
this[_configuration] = configuration;
|
surfaceRid: this[_surfaceRid],
|
||||||
const device = assertDevice(this, {
|
deviceRid: device.rid,
|
||||||
prefix,
|
format: configuration.format,
|
||||||
context: "configuration.device",
|
viewFormats: configuration.viewFormats,
|
||||||
});
|
usage: configuration.usage,
|
||||||
|
width: configuration.width,
|
||||||
|
height: configuration.height,
|
||||||
|
alphaMode: configuration.alphaMode,
|
||||||
|
});
|
||||||
|
|
||||||
const { err } = ops.op_webgpu_surface_configure({
|
device.pushError(err);
|
||||||
surfaceRid: this[_surfaceRid],
|
}
|
||||||
deviceRid: device.rid,
|
|
||||||
format: configuration.format,
|
|
||||||
viewFormats: configuration.viewFormats,
|
|
||||||
usage: configuration.usage,
|
|
||||||
width: configuration.width,
|
|
||||||
height: configuration.height,
|
|
||||||
alphaMode: configuration.alphaMode,
|
|
||||||
});
|
|
||||||
|
|
||||||
device.pushError(err);
|
unconfigure() {
|
||||||
}
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||||
|
|
||||||
unconfigure() {
|
this[_configuration] = null;
|
||||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
this[_device] = null;
|
||||||
|
}
|
||||||
|
|
||||||
this[_configuration] = null;
|
getCurrentTexture() {
|
||||||
this[_device] = null;
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||||
}
|
const prefix =
|
||||||
|
"Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
|
||||||
|
|
||||||
getCurrentTexture() {
|
if (this[_configuration] === null) {
|
||||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
throw new DOMException(
|
||||||
const prefix =
|
"context is not configured.",
|
||||||
"Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
|
"InvalidStateError",
|
||||||
|
|
||||||
if (this[_configuration] === null) {
|
|
||||||
throw new DOMException(
|
|
||||||
"context is not configured.",
|
|
||||||
"InvalidStateError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const device = assertDevice(this, { prefix, context: "this" });
|
|
||||||
|
|
||||||
if (this[_currentTexture]) {
|
|
||||||
return this[_currentTexture];
|
|
||||||
}
|
|
||||||
|
|
||||||
const { rid } = ops.op_webgpu_surface_get_current_texture(
|
|
||||||
device.rid,
|
|
||||||
this[_surfaceRid],
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
const texture = createGPUTexture(
|
const device = assertDevice(this, { prefix, context: "this" });
|
||||||
{
|
|
||||||
size: {
|
if (this[_currentTexture]) {
|
||||||
width: this[_configuration].width,
|
return this[_currentTexture];
|
||||||
height: this[_configuration].height,
|
}
|
||||||
depthOrArrayLayers: 1,
|
|
||||||
},
|
const { rid } = ops.op_webgpu_surface_get_current_texture(
|
||||||
mipLevelCount: 1,
|
device.rid,
|
||||||
sampleCount: 1,
|
this[_surfaceRid],
|
||||||
dimension: "2d",
|
);
|
||||||
format: this[_configuration].format,
|
|
||||||
usage: this[_configuration].usage,
|
const texture = createGPUTexture(
|
||||||
|
{
|
||||||
|
size: {
|
||||||
|
width: this[_configuration].width,
|
||||||
|
height: this[_configuration].height,
|
||||||
|
depthOrArrayLayers: 1,
|
||||||
},
|
},
|
||||||
device,
|
mipLevelCount: 1,
|
||||||
rid,
|
sampleCount: 1,
|
||||||
);
|
dimension: "2d",
|
||||||
device.trackResource(texture);
|
format: this[_configuration].format,
|
||||||
this[_currentTexture] = texture;
|
usage: this[_configuration].usage,
|
||||||
return texture;
|
},
|
||||||
}
|
device,
|
||||||
|
rid,
|
||||||
// Extended from spec. Required to present the texture; browser don't need this.
|
);
|
||||||
present() {
|
device.trackResource(texture);
|
||||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
this[_currentTexture] = texture;
|
||||||
const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
|
return texture;
|
||||||
const device = assertDevice(this[_currentTexture], {
|
|
||||||
prefix,
|
|
||||||
context: "this",
|
|
||||||
});
|
|
||||||
ops.op_webgpu_surface_present(device.rid, this[_surfaceRid]);
|
|
||||||
this[_currentTexture].destroy();
|
|
||||||
this[_currentTexture] = undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const GPUCanvasContextPrototype = GPUCanvasContext.prototype;
|
|
||||||
|
|
||||||
function createCanvasContext(options) {
|
|
||||||
const canvasContext = webidl.createBranded(GPUCanvasContext);
|
|
||||||
canvasContext[_surfaceRid] = options.surfaceRid;
|
|
||||||
canvasContext[_canvas] = options.canvas;
|
|
||||||
return canvasContext;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.webgpu = {
|
// Extended from spec. Required to present the texture; browser don't need this.
|
||||||
...window.__bootstrap.webgpu,
|
present() {
|
||||||
GPUCanvasContext,
|
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||||
createCanvasContext,
|
const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
|
||||||
};
|
const device = assertDevice(this[_currentTexture], {
|
||||||
})(this);
|
prefix,
|
||||||
|
context: "this",
|
||||||
|
});
|
||||||
|
ops.op_webgpu_surface_present(device.rid, this[_surfaceRid]);
|
||||||
|
this[_currentTexture].destroy();
|
||||||
|
this[_currentTexture] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const GPUCanvasContextPrototype = GPUCanvasContext.prototype;
|
||||||
|
|
||||||
|
function createCanvasContext(options) {
|
||||||
|
const canvasContext = webidl.createBranded(GPUCanvasContext);
|
||||||
|
canvasContext[_surfaceRid] = options.surfaceRid;
|
||||||
|
canvasContext[_canvas] = options.canvas;
|
||||||
|
return canvasContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { createCanvasContext, GPUCanvasContext };
|
||||||
|
|
|
@ -6,81 +6,77 @@
|
||||||
/// <reference path="../web/lib.deno_web.d.ts" />
|
/// <reference path="../web/lib.deno_web.d.ts" />
|
||||||
/// <reference path="./lib.deno_webgpu.d.ts" />
|
/// <reference path="./lib.deno_webgpu.d.ts" />
|
||||||
|
|
||||||
"use strict";
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
|
import { GPUTextureUsage } from "internal:ext/webgpu/01_webgpu.js";
|
||||||
|
|
||||||
((window) => {
|
// ENUM: GPUCanvasAlphaMode
|
||||||
const webidl = window.__bootstrap.webidl;
|
webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
|
||||||
const { GPUTextureUsage } = window.__bootstrap.webgpu;
|
"GPUCanvasAlphaMode",
|
||||||
|
[
|
||||||
|
"opaque",
|
||||||
|
"premultiplied",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
// ENUM: GPUCanvasAlphaMode
|
// NON-SPEC: ENUM: GPUPresentMode
|
||||||
webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
|
webidl.converters["GPUPresentMode"] = webidl.createEnumConverter(
|
||||||
"GPUCanvasAlphaMode",
|
"GPUPresentMode",
|
||||||
[
|
[
|
||||||
"opaque",
|
"autoVsync",
|
||||||
"premultiplied",
|
"autoNoVsync",
|
||||||
],
|
"fifo",
|
||||||
|
"fifoRelaxed",
|
||||||
|
"immediate",
|
||||||
|
"mailbox",
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
// DICT: GPUCanvasConfiguration
|
||||||
|
const dictMembersGPUCanvasConfiguration = [
|
||||||
|
{ key: "device", converter: webidl.converters.GPUDevice, required: true },
|
||||||
|
{
|
||||||
|
key: "format",
|
||||||
|
converter: webidl.converters.GPUTextureFormat,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "usage",
|
||||||
|
converter: webidl.converters["GPUTextureUsageFlags"],
|
||||||
|
defaultValue: GPUTextureUsage.RENDER_ATTACHMENT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "alphaMode",
|
||||||
|
converter: webidl.converters["GPUCanvasAlphaMode"],
|
||||||
|
defaultValue: "opaque",
|
||||||
|
},
|
||||||
|
|
||||||
|
// Extended from spec
|
||||||
|
{
|
||||||
|
key: "presentMode",
|
||||||
|
converter: webidl.converters["GPUPresentMode"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "width",
|
||||||
|
converter: webidl.converters["long"],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "height",
|
||||||
|
converter: webidl.converters["long"],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "viewFormats",
|
||||||
|
converter: webidl.createSequenceConverter(
|
||||||
|
webidl.converters["GPUTextureFormat"],
|
||||||
|
),
|
||||||
|
get defaultValue() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
webidl.converters["GPUCanvasConfiguration"] = webidl
|
||||||
|
.createDictionaryConverter(
|
||||||
|
"GPUCanvasConfiguration",
|
||||||
|
dictMembersGPUCanvasConfiguration,
|
||||||
);
|
);
|
||||||
|
|
||||||
// NON-SPEC: ENUM: GPUPresentMode
|
|
||||||
webidl.converters["GPUPresentMode"] = webidl.createEnumConverter(
|
|
||||||
"GPUPresentMode",
|
|
||||||
[
|
|
||||||
"autoVsync",
|
|
||||||
"autoNoVsync",
|
|
||||||
"fifo",
|
|
||||||
"fifoRelaxed",
|
|
||||||
"immediate",
|
|
||||||
"mailbox",
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
// DICT: GPUCanvasConfiguration
|
|
||||||
const dictMembersGPUCanvasConfiguration = [
|
|
||||||
{ key: "device", converter: webidl.converters.GPUDevice, required: true },
|
|
||||||
{
|
|
||||||
key: "format",
|
|
||||||
converter: webidl.converters.GPUTextureFormat,
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "usage",
|
|
||||||
converter: webidl.converters["GPUTextureUsageFlags"],
|
|
||||||
defaultValue: GPUTextureUsage.RENDER_ATTACHMENT,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "alphaMode",
|
|
||||||
converter: webidl.converters["GPUCanvasAlphaMode"],
|
|
||||||
defaultValue: "opaque",
|
|
||||||
},
|
|
||||||
|
|
||||||
// Extended from spec
|
|
||||||
{
|
|
||||||
key: "presentMode",
|
|
||||||
converter: webidl.converters["GPUPresentMode"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "width",
|
|
||||||
converter: webidl.converters["long"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "height",
|
|
||||||
converter: webidl.converters["long"],
|
|
||||||
required: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "viewFormats",
|
|
||||||
converter: webidl.createSequenceConverter(
|
|
||||||
webidl.converters["GPUTextureFormat"],
|
|
||||||
),
|
|
||||||
get defaultValue() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
webidl.converters["GPUCanvasConfiguration"] = webidl
|
|
||||||
.createDictionaryConverter(
|
|
||||||
"GPUCanvasConfiguration",
|
|
||||||
dictMembersGPUCanvasConfiguration,
|
|
||||||
);
|
|
||||||
})(this);
|
|
||||||
|
|
|
@ -119,7 +119,7 @@ impl Resource for WebGpuQuerySet {
|
||||||
pub fn init(unstable: bool) -> Extension {
|
pub fn init(unstable: bool) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl", "deno_web"])
|
.dependencies(vec!["deno_webidl", "deno_web"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/webgpu",
|
prefix "internal:ext/webgpu",
|
||||||
"01_webgpu.js",
|
"01_webgpu.js",
|
||||||
"02_idl_types.js",
|
"02_idl_types.js",
|
||||||
|
|
|
@ -15,8 +15,8 @@ use wgpu_types::SurfaceStatus;
|
||||||
pub fn init_surface(unstable: bool) -> Extension {
|
pub fn init_surface(unstable: bool) -> Extension {
|
||||||
Extension::builder("deno_webgpu_surface")
|
Extension::builder("deno_webgpu_surface")
|
||||||
.dependencies(vec!["deno_webidl", "deno_web", "deno_webgpu"])
|
.dependencies(vec!["deno_webidl", "deno_web", "deno_webgpu"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:deno_webgpu",
|
prefix "internal:ext/webgpu",
|
||||||
"03_surface.js",
|
"03_surface.js",
|
||||||
"04_surface_idl_types.js",
|
"04_surface_idl_types.js",
|
||||||
))
|
))
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,10 @@
|
||||||
|
|
||||||
// deno-lint-ignore-file
|
// deno-lint-ignore-file
|
||||||
|
|
||||||
const { createDictionaryConverter, converters } = globalThis.__bootstrap.webidl;
|
import {
|
||||||
|
converters,
|
||||||
|
createDictionaryConverter,
|
||||||
|
} from "internal:ext/webidl/00_webidl.js";
|
||||||
|
|
||||||
const TextDecodeOptions = createDictionaryConverter(
|
const TextDecodeOptions = createDictionaryConverter(
|
||||||
"TextDecodeOptions",
|
"TextDecodeOptions",
|
||||||
|
@ -14,6 +17,7 @@ const TextDecodeOptions = createDictionaryConverter(
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
globalThis.TextDecodeOptions = TextDecodeOptions;
|
||||||
|
|
||||||
// Sanity check
|
// Sanity check
|
||||||
{
|
{
|
||||||
|
@ -33,3 +37,4 @@ function handwrittenConverter(V) {
|
||||||
}
|
}
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
}
|
}
|
||||||
|
globalThis.handwrittenConverter = handwrittenConverter;
|
||||||
|
|
|
@ -11,7 +11,7 @@ fn setup() -> Vec<Extension> {
|
||||||
vec![
|
vec![
|
||||||
deno_webidl::init(),
|
deno_webidl::init(),
|
||||||
Extension::builder("deno_webidl_bench")
|
Extension::builder("deno_webidl_bench")
|
||||||
.js(vec![("setup", include_str!("dict.js"))])
|
.esm(vec![("internal:setup", include_str!("dict.js"))])
|
||||||
.build(),
|
.build(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
662
ext/webidl/internal.d.ts
vendored
662
ext/webidl/internal.d.ts
vendored
|
@ -4,338 +4,334 @@
|
||||||
/// <reference no-default-lib="true" />
|
/// <reference no-default-lib="true" />
|
||||||
/// <reference lib="esnext" />
|
/// <reference lib="esnext" />
|
||||||
|
|
||||||
declare namespace globalThis {
|
declare module "internal:ext/webidl/00_webidl.js" {
|
||||||
declare namespace __bootstrap {
|
interface ConverterOpts {
|
||||||
declare namespace webidl {
|
/**
|
||||||
declare interface ConverterOpts {
|
* The prefix for error messages created by this converter.
|
||||||
/**
|
* Examples:
|
||||||
* The prefix for error messages created by this converter.
|
* - `Failed to construct 'Event'`
|
||||||
* Examples:
|
* - `Failed to execute 'removeEventListener' on 'EventTarget'`
|
||||||
* - `Failed to construct 'Event'`
|
*/
|
||||||
* - `Failed to execute 'removeEventListener' on 'EventTarget'`
|
prefix: string;
|
||||||
*/
|
|
||||||
prefix: string;
|
|
||||||
}
|
|
||||||
declare interface ValueConverterOpts extends ConverterOpts {
|
|
||||||
/**
|
|
||||||
* The context of this value error messages created by this converter.
|
|
||||||
* Examples:
|
|
||||||
* - `Argument 1`
|
|
||||||
* - `Argument 3`
|
|
||||||
*/
|
|
||||||
context: string;
|
|
||||||
}
|
|
||||||
declare function makeException(
|
|
||||||
ErrorType: any,
|
|
||||||
message: string,
|
|
||||||
opts: ValueConverterOpts,
|
|
||||||
): any;
|
|
||||||
declare interface IntConverterOpts extends ValueConverterOpts {
|
|
||||||
/**
|
|
||||||
* Wether to throw if the number is outside of the acceptable values for
|
|
||||||
* this type.
|
|
||||||
*/
|
|
||||||
enforceRange?: boolean;
|
|
||||||
/**
|
|
||||||
* Wether to clamp this number to the acceptable values for this type.
|
|
||||||
*/
|
|
||||||
clamp?: boolean;
|
|
||||||
}
|
|
||||||
declare interface StringConverterOpts extends ValueConverterOpts {
|
|
||||||
/**
|
|
||||||
* Wether to treat `null` value as an empty string.
|
|
||||||
*/
|
|
||||||
treatNullAsEmptyString?: boolean;
|
|
||||||
}
|
|
||||||
declare interface BufferConverterOpts extends ValueConverterOpts {
|
|
||||||
/**
|
|
||||||
* Wether to allow `SharedArrayBuffer` (not just `ArrayBuffer`).
|
|
||||||
*/
|
|
||||||
allowShared?: boolean;
|
|
||||||
}
|
|
||||||
declare const converters: {
|
|
||||||
any(v: any): any;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `boolean` (bool).
|
|
||||||
*/
|
|
||||||
boolean(v: any, opts?: IntConverterOpts): boolean;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `byte` (int8).
|
|
||||||
*/
|
|
||||||
byte(v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `octet` (uint8).
|
|
||||||
*/
|
|
||||||
octet(v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `short` (int16).
|
|
||||||
*/
|
|
||||||
short(v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `unsigned short` (uint16).
|
|
||||||
*/
|
|
||||||
["unsigned short"](v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `long` (int32).
|
|
||||||
*/
|
|
||||||
long(v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `unsigned long` (uint32).
|
|
||||||
*/
|
|
||||||
["unsigned long"](v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `long long` (int64).
|
|
||||||
* **Note this is truncated to a JS number (53 bit precision).**
|
|
||||||
*/
|
|
||||||
["long long"](v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `unsigned long long` (uint64).
|
|
||||||
* **Note this is truncated to a JS number (53 bit precision).**
|
|
||||||
*/
|
|
||||||
["unsigned long long"](v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `float` (f32).
|
|
||||||
*/
|
|
||||||
float(v: any, opts?: ValueConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `unrestricted float` (f32, infinity, or NaN).
|
|
||||||
*/
|
|
||||||
["unrestricted float"](v: any, opts?: ValueConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `double` (f64).
|
|
||||||
*/
|
|
||||||
double(v: any, opts?: ValueConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `unrestricted double` (f64, infinity, or NaN).
|
|
||||||
*/
|
|
||||||
["unrestricted double"](v: any, opts?: ValueConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `DOMString` (string).
|
|
||||||
*/
|
|
||||||
DOMString(v: any, opts?: StringConverterOpts): string;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `ByteString` (string with only u8 codepoints).
|
|
||||||
*/
|
|
||||||
ByteString(v: any, opts?: StringConverterOpts): string;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `USVString` (string with only valid non
|
|
||||||
* surrogate Unicode code points).
|
|
||||||
*/
|
|
||||||
USVString(v: any, opts?: StringConverterOpts): string;
|
|
||||||
/**
|
|
||||||
* Convert a value into an `object` (object).
|
|
||||||
*/
|
|
||||||
object(v: any, opts?: ValueConverterOpts): object;
|
|
||||||
/**
|
|
||||||
* Convert a value into an `ArrayBuffer` (ArrayBuffer).
|
|
||||||
*/
|
|
||||||
ArrayBuffer(v: any, opts?: BufferConverterOpts): ArrayBuffer;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `DataView` (ArrayBuffer).
|
|
||||||
*/
|
|
||||||
DataView(v: any, opts?: BufferConverterOpts): DataView;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Int8Array` (Int8Array).
|
|
||||||
*/
|
|
||||||
Int8Array(v: any, opts?: BufferConverterOpts): Int8Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Int16Array` (Int16Array).
|
|
||||||
*/
|
|
||||||
Int16Array(v: any, opts?: BufferConverterOpts): Int16Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Int32Array` (Int32Array).
|
|
||||||
*/
|
|
||||||
Int32Array(v: any, opts?: BufferConverterOpts): Int32Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Uint8Array` (Uint8Array).
|
|
||||||
*/
|
|
||||||
Uint8Array(v: any, opts?: BufferConverterOpts): Uint8Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Uint16Array` (Uint16Array).
|
|
||||||
*/
|
|
||||||
Uint16Array(v: any, opts?: BufferConverterOpts): Uint16Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Uint32Array` (Uint32Array).
|
|
||||||
*/
|
|
||||||
Uint32Array(v: any, opts?: BufferConverterOpts): Uint32Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Uint8ClampedArray` (Uint8ClampedArray).
|
|
||||||
*/
|
|
||||||
Uint8ClampedArray(
|
|
||||||
v: any,
|
|
||||||
opts?: BufferConverterOpts,
|
|
||||||
): Uint8ClampedArray;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Float32Array` (Float32Array).
|
|
||||||
*/
|
|
||||||
Float32Array(v: any, opts?: BufferConverterOpts): Float32Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Float64Array` (Float64Array).
|
|
||||||
*/
|
|
||||||
Float64Array(v: any, opts?: BufferConverterOpts): Float64Array;
|
|
||||||
/**
|
|
||||||
* Convert a value into an `ArrayBufferView` (ArrayBufferView).
|
|
||||||
*/
|
|
||||||
ArrayBufferView(v: any, opts?: BufferConverterOpts): ArrayBufferView;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `BufferSource` (ArrayBuffer or ArrayBufferView).
|
|
||||||
*/
|
|
||||||
BufferSource(
|
|
||||||
v: any,
|
|
||||||
opts?: BufferConverterOpts,
|
|
||||||
): ArrayBuffer | ArrayBufferView;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `DOMTimeStamp` (u64). Alias for unsigned long long
|
|
||||||
*/
|
|
||||||
DOMTimeStamp(v: any, opts?: IntConverterOpts): number;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `Function` ((...args: any[]) => any).
|
|
||||||
*/
|
|
||||||
Function(v: any, opts?: ValueConverterOpts): (...args: any) => any;
|
|
||||||
/**
|
|
||||||
* Convert a value into a `VoidFunction` (() => void).
|
|
||||||
*/
|
|
||||||
VoidFunction(v: any, opts?: ValueConverterOpts): () => void;
|
|
||||||
["UVString?"](v: any, opts?: ValueConverterOpts): string | null;
|
|
||||||
["sequence<double>"](v: any, opts?: ValueConverterOpts): number[];
|
|
||||||
|
|
||||||
[type: string]: (v: any, opts: ValueConverterOpts) => any;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert that the a function has at least a required amount of arguments.
|
|
||||||
*/
|
|
||||||
declare function requiredArguments(
|
|
||||||
length: number,
|
|
||||||
required: number,
|
|
||||||
opts: ConverterOpts,
|
|
||||||
): void;
|
|
||||||
declare type Dictionary = DictionaryMember[];
|
|
||||||
declare interface DictionaryMember {
|
|
||||||
key: string;
|
|
||||||
converter: (v: any, opts: ValueConverterOpts) => any;
|
|
||||||
defaultValue?: any;
|
|
||||||
required?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a converter for dictionaries.
|
|
||||||
*/
|
|
||||||
declare function createDictionaryConverter<T>(
|
|
||||||
name: string,
|
|
||||||
...dictionaries: Dictionary[]
|
|
||||||
): (v: any, opts: ValueConverterOpts) => T;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a converter for enums.
|
|
||||||
*/
|
|
||||||
declare function createEnumConverter(
|
|
||||||
name: string,
|
|
||||||
values: string[],
|
|
||||||
): (v: any, opts: ValueConverterOpts) => string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a converter that makes the contained type nullable.
|
|
||||||
*/
|
|
||||||
declare function createNullableConverter<T>(
|
|
||||||
converter: (v: any, opts: ValueConverterOpts) => T,
|
|
||||||
): (v: any, opts: ValueConverterOpts) => T | null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a converter that converts a sequence of the inner type.
|
|
||||||
*/
|
|
||||||
declare function createSequenceConverter<T>(
|
|
||||||
converter: (v: any, opts: ValueConverterOpts) => T,
|
|
||||||
): (v: any, opts: ValueConverterOpts) => T[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a converter that converts a Promise of the inner type.
|
|
||||||
*/
|
|
||||||
declare function createPromiseConverter<T>(
|
|
||||||
converter: (v: any, opts: ValueConverterOpts) => T,
|
|
||||||
): (v: any, opts: ValueConverterOpts) => Promise<T>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invoke a callback function.
|
|
||||||
*/
|
|
||||||
declare function invokeCallbackFunction<T>(
|
|
||||||
callable: (...args: any) => any,
|
|
||||||
args: any[],
|
|
||||||
thisArg: any,
|
|
||||||
returnValueConverter: (v: any, opts: ValueConverterOpts) => T,
|
|
||||||
opts: ConverterOpts & { returnsPromise?: boolean },
|
|
||||||
): T;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throw an illegal constructor error.
|
|
||||||
*/
|
|
||||||
declare function illegalConstructor(): never;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The branding symbol.
|
|
||||||
*/
|
|
||||||
declare const brand: unique symbol;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a branded instance of an interface.
|
|
||||||
*/
|
|
||||||
declare function createBranded(self: any): any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert that self is branded.
|
|
||||||
*/
|
|
||||||
declare function assertBranded(self: any, type: any): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a converter for interfaces.
|
|
||||||
*/
|
|
||||||
declare function createInterfaceConverter(
|
|
||||||
name: string,
|
|
||||||
prototype: any,
|
|
||||||
): (v: any, opts: ValueConverterOpts) => any;
|
|
||||||
|
|
||||||
declare function createRecordConverter<
|
|
||||||
K extends string | number | symbol,
|
|
||||||
V,
|
|
||||||
>(
|
|
||||||
keyConverter: (v: any, opts: ValueConverterOpts) => K,
|
|
||||||
valueConverter: (v: any, opts: ValueConverterOpts) => V,
|
|
||||||
): (
|
|
||||||
v: Record<K, V>,
|
|
||||||
opts: ValueConverterOpts,
|
|
||||||
) => any;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mix in the iterable declarations defined in WebIDL.
|
|
||||||
* https://heycam.github.io/webidl/#es-iterable
|
|
||||||
*/
|
|
||||||
declare function mixinPairIterable(
|
|
||||||
name: string,
|
|
||||||
prototype: any,
|
|
||||||
dataSymbol: symbol,
|
|
||||||
keyKey: string | number | symbol,
|
|
||||||
valueKey: string | number | symbol,
|
|
||||||
): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Configure prototype properties enumerability / writability / configurability.
|
|
||||||
*/
|
|
||||||
declare function configurePrototype(prototype: any);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the WebIDL / ES type of a value.
|
|
||||||
*/
|
|
||||||
declare function type(
|
|
||||||
v: any,
|
|
||||||
):
|
|
||||||
| "Null"
|
|
||||||
| "Undefined"
|
|
||||||
| "Boolean"
|
|
||||||
| "Number"
|
|
||||||
| "String"
|
|
||||||
| "Symbol"
|
|
||||||
| "BigInt"
|
|
||||||
| "Object";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
interface ValueConverterOpts extends ConverterOpts {
|
||||||
|
/**
|
||||||
|
* The context of this value error messages created by this converter.
|
||||||
|
* Examples:
|
||||||
|
* - `Argument 1`
|
||||||
|
* - `Argument 3`
|
||||||
|
*/
|
||||||
|
context: string;
|
||||||
|
}
|
||||||
|
function makeException(
|
||||||
|
ErrorType: any,
|
||||||
|
message: string,
|
||||||
|
opts: ValueConverterOpts,
|
||||||
|
): any;
|
||||||
|
interface IntConverterOpts extends ValueConverterOpts {
|
||||||
|
/**
|
||||||
|
* Wether to throw if the number is outside of the acceptable values for
|
||||||
|
* this type.
|
||||||
|
*/
|
||||||
|
enforceRange?: boolean;
|
||||||
|
/**
|
||||||
|
* Wether to clamp this number to the acceptable values for this type.
|
||||||
|
*/
|
||||||
|
clamp?: boolean;
|
||||||
|
}
|
||||||
|
interface StringConverterOpts extends ValueConverterOpts {
|
||||||
|
/**
|
||||||
|
* Wether to treat `null` value as an empty string.
|
||||||
|
*/
|
||||||
|
treatNullAsEmptyString?: boolean;
|
||||||
|
}
|
||||||
|
interface BufferConverterOpts extends ValueConverterOpts {
|
||||||
|
/**
|
||||||
|
* Wether to allow `SharedArrayBuffer` (not just `ArrayBuffer`).
|
||||||
|
*/
|
||||||
|
allowShared?: boolean;
|
||||||
|
}
|
||||||
|
const converters: {
|
||||||
|
any(v: any): any;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `boolean` (bool).
|
||||||
|
*/
|
||||||
|
boolean(v: any, opts?: IntConverterOpts): boolean;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `byte` (int8).
|
||||||
|
*/
|
||||||
|
byte(v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `octet` (uint8).
|
||||||
|
*/
|
||||||
|
octet(v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `short` (int16).
|
||||||
|
*/
|
||||||
|
short(v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `unsigned short` (uint16).
|
||||||
|
*/
|
||||||
|
["unsigned short"](v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `long` (int32).
|
||||||
|
*/
|
||||||
|
long(v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `unsigned long` (uint32).
|
||||||
|
*/
|
||||||
|
["unsigned long"](v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `long long` (int64).
|
||||||
|
* **Note this is truncated to a JS number (53 bit precision).**
|
||||||
|
*/
|
||||||
|
["long long"](v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `unsigned long long` (uint64).
|
||||||
|
* **Note this is truncated to a JS number (53 bit precision).**
|
||||||
|
*/
|
||||||
|
["unsigned long long"](v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `float` (f32).
|
||||||
|
*/
|
||||||
|
float(v: any, opts?: ValueConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `unrestricted float` (f32, infinity, or NaN).
|
||||||
|
*/
|
||||||
|
["unrestricted float"](v: any, opts?: ValueConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `double` (f64).
|
||||||
|
*/
|
||||||
|
double(v: any, opts?: ValueConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `unrestricted double` (f64, infinity, or NaN).
|
||||||
|
*/
|
||||||
|
["unrestricted double"](v: any, opts?: ValueConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `DOMString` (string).
|
||||||
|
*/
|
||||||
|
DOMString(v: any, opts?: StringConverterOpts): string;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `ByteString` (string with only u8 codepoints).
|
||||||
|
*/
|
||||||
|
ByteString(v: any, opts?: StringConverterOpts): string;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `USVString` (string with only valid non
|
||||||
|
* surrogate Unicode code points).
|
||||||
|
*/
|
||||||
|
USVString(v: any, opts?: StringConverterOpts): string;
|
||||||
|
/**
|
||||||
|
* Convert a value into an `object` (object).
|
||||||
|
*/
|
||||||
|
object(v: any, opts?: ValueConverterOpts): object;
|
||||||
|
/**
|
||||||
|
* Convert a value into an `ArrayBuffer` (ArrayBuffer).
|
||||||
|
*/
|
||||||
|
ArrayBuffer(v: any, opts?: BufferConverterOpts): ArrayBuffer;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `DataView` (ArrayBuffer).
|
||||||
|
*/
|
||||||
|
DataView(v: any, opts?: BufferConverterOpts): DataView;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Int8Array` (Int8Array).
|
||||||
|
*/
|
||||||
|
Int8Array(v: any, opts?: BufferConverterOpts): Int8Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Int16Array` (Int16Array).
|
||||||
|
*/
|
||||||
|
Int16Array(v: any, opts?: BufferConverterOpts): Int16Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Int32Array` (Int32Array).
|
||||||
|
*/
|
||||||
|
Int32Array(v: any, opts?: BufferConverterOpts): Int32Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Uint8Array` (Uint8Array).
|
||||||
|
*/
|
||||||
|
Uint8Array(v: any, opts?: BufferConverterOpts): Uint8Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Uint16Array` (Uint16Array).
|
||||||
|
*/
|
||||||
|
Uint16Array(v: any, opts?: BufferConverterOpts): Uint16Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Uint32Array` (Uint32Array).
|
||||||
|
*/
|
||||||
|
Uint32Array(v: any, opts?: BufferConverterOpts): Uint32Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Uint8ClampedArray` (Uint8ClampedArray).
|
||||||
|
*/
|
||||||
|
Uint8ClampedArray(
|
||||||
|
v: any,
|
||||||
|
opts?: BufferConverterOpts,
|
||||||
|
): Uint8ClampedArray;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Float32Array` (Float32Array).
|
||||||
|
*/
|
||||||
|
Float32Array(v: any, opts?: BufferConverterOpts): Float32Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Float64Array` (Float64Array).
|
||||||
|
*/
|
||||||
|
Float64Array(v: any, opts?: BufferConverterOpts): Float64Array;
|
||||||
|
/**
|
||||||
|
* Convert a value into an `ArrayBufferView` (ArrayBufferView).
|
||||||
|
*/
|
||||||
|
ArrayBufferView(v: any, opts?: BufferConverterOpts): ArrayBufferView;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `BufferSource` (ArrayBuffer or ArrayBufferView).
|
||||||
|
*/
|
||||||
|
BufferSource(
|
||||||
|
v: any,
|
||||||
|
opts?: BufferConverterOpts,
|
||||||
|
): ArrayBuffer | ArrayBufferView;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `DOMTimeStamp` (u64). Alias for unsigned long long
|
||||||
|
*/
|
||||||
|
DOMTimeStamp(v: any, opts?: IntConverterOpts): number;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `Function` ((...args: any[]) => any).
|
||||||
|
*/
|
||||||
|
Function(v: any, opts?: ValueConverterOpts): (...args: any) => any;
|
||||||
|
/**
|
||||||
|
* Convert a value into a `VoidFunction` (() => void).
|
||||||
|
*/
|
||||||
|
VoidFunction(v: any, opts?: ValueConverterOpts): () => void;
|
||||||
|
["UVString?"](v: any, opts?: ValueConverterOpts): string | null;
|
||||||
|
["sequence<double>"](v: any, opts?: ValueConverterOpts): number[];
|
||||||
|
|
||||||
|
[type: string]: (v: any, opts: ValueConverterOpts) => any;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the a function has at least a required amount of arguments.
|
||||||
|
*/
|
||||||
|
function requiredArguments(
|
||||||
|
length: number,
|
||||||
|
required: number,
|
||||||
|
opts: ConverterOpts,
|
||||||
|
): void;
|
||||||
|
type Dictionary = DictionaryMember[];
|
||||||
|
interface DictionaryMember {
|
||||||
|
key: string;
|
||||||
|
converter: (v: any, opts: ValueConverterOpts) => any;
|
||||||
|
defaultValue?: any;
|
||||||
|
required?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a converter for dictionaries.
|
||||||
|
*/
|
||||||
|
function createDictionaryConverter<T>(
|
||||||
|
name: string,
|
||||||
|
...dictionaries: Dictionary[]
|
||||||
|
): (v: any, opts: ValueConverterOpts) => T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a converter for enums.
|
||||||
|
*/
|
||||||
|
function createEnumConverter(
|
||||||
|
name: string,
|
||||||
|
values: string[],
|
||||||
|
): (v: any, opts: ValueConverterOpts) => string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a converter that makes the contained type nullable.
|
||||||
|
*/
|
||||||
|
function createNullableConverter<T>(
|
||||||
|
converter: (v: any, opts: ValueConverterOpts) => T,
|
||||||
|
): (v: any, opts: ValueConverterOpts) => T | null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a converter that converts a sequence of the inner type.
|
||||||
|
*/
|
||||||
|
function createSequenceConverter<T>(
|
||||||
|
converter: (v: any, opts: ValueConverterOpts) => T,
|
||||||
|
): (v: any, opts: ValueConverterOpts) => T[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a converter that converts a Promise of the inner type.
|
||||||
|
*/
|
||||||
|
function createPromiseConverter<T>(
|
||||||
|
converter: (v: any, opts: ValueConverterOpts) => T,
|
||||||
|
): (v: any, opts: ValueConverterOpts) => Promise<T>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke a callback function.
|
||||||
|
*/
|
||||||
|
function invokeCallbackFunction<T>(
|
||||||
|
callable: (...args: any) => any,
|
||||||
|
args: any[],
|
||||||
|
thisArg: any,
|
||||||
|
returnValueConverter: (v: any, opts: ValueConverterOpts) => T,
|
||||||
|
opts: ConverterOpts & { returnsPromise?: boolean },
|
||||||
|
): T;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throw an illegal constructor error.
|
||||||
|
*/
|
||||||
|
function illegalConstructor(): never;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The branding symbol.
|
||||||
|
*/
|
||||||
|
const brand: unique symbol;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a branded instance of an interface.
|
||||||
|
*/
|
||||||
|
function createBranded(self: any): any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that self is branded.
|
||||||
|
*/
|
||||||
|
function assertBranded(self: any, type: any): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a converter for interfaces.
|
||||||
|
*/
|
||||||
|
function createInterfaceConverter(
|
||||||
|
name: string,
|
||||||
|
prototype: any,
|
||||||
|
): (v: any, opts: ValueConverterOpts) => any;
|
||||||
|
|
||||||
|
function createRecordConverter<
|
||||||
|
K extends string | number | symbol,
|
||||||
|
V,
|
||||||
|
>(
|
||||||
|
keyConverter: (v: any, opts: ValueConverterOpts) => K,
|
||||||
|
valueConverter: (v: any, opts: ValueConverterOpts) => V,
|
||||||
|
): (
|
||||||
|
v: Record<K, V>,
|
||||||
|
opts: ValueConverterOpts,
|
||||||
|
) => any;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix in the iterable declarations defined in WebIDL.
|
||||||
|
* https://heycam.github.io/webidl/#es-iterable
|
||||||
|
*/
|
||||||
|
function mixinPairIterable(
|
||||||
|
name: string,
|
||||||
|
prototype: any,
|
||||||
|
dataSymbol: symbol,
|
||||||
|
keyKey: string | number | symbol,
|
||||||
|
valueKey: string | number | symbol,
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configure prototype properties enumerability / writability / configurability.
|
||||||
|
*/
|
||||||
|
function configurePrototype(prototype: any);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the WebIDL / ES type of a value.
|
||||||
|
*/
|
||||||
|
function type(
|
||||||
|
v: any,
|
||||||
|
):
|
||||||
|
| "Null"
|
||||||
|
| "Undefined"
|
||||||
|
| "Boolean"
|
||||||
|
| "Number"
|
||||||
|
| "String"
|
||||||
|
| "Symbol"
|
||||||
|
| "BigInt"
|
||||||
|
| "Object";
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ use deno_core::Extension;
|
||||||
/// Load and execute the javascript code.
|
/// Load and execute the javascript code.
|
||||||
pub fn init() -> Extension {
|
pub fn init() -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/webidl",
|
prefix "internal:ext/webidl",
|
||||||
"00_webidl.js",
|
"00_webidl.js",
|
||||||
))
|
))
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,426 +1,424 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = window.__bootstrap.webidl;
|
import { Deferred, writableStreamClose } from "internal:ext/web/06_streams.js";
|
||||||
const { writableStreamClose, Deferred } = window.__bootstrap.streams;
|
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||||
const { DOMException } = window.__bootstrap.domException;
|
import { add, remove } from "internal:ext/web/03_abort_signal.js";
|
||||||
const { add, remove } = window.__bootstrap.abortSignal;
|
import {
|
||||||
const { headersFromHeaderList, headerListFromHeaders, fillHeaders } =
|
fillHeaders,
|
||||||
window.__bootstrap.headers;
|
headerListFromHeaders,
|
||||||
|
headersFromHeaderList,
|
||||||
|
} from "internal:ext/fetch/20_headers.js";
|
||||||
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
|
const {
|
||||||
|
ArrayPrototypeJoin,
|
||||||
|
ArrayPrototypeMap,
|
||||||
|
Error,
|
||||||
|
ObjectPrototypeIsPrototypeOf,
|
||||||
|
PromisePrototypeCatch,
|
||||||
|
PromisePrototypeThen,
|
||||||
|
Set,
|
||||||
|
StringPrototypeEndsWith,
|
||||||
|
StringPrototypeToLowerCase,
|
||||||
|
Symbol,
|
||||||
|
SymbolFor,
|
||||||
|
TypeError,
|
||||||
|
Uint8ArrayPrototype,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
const {
|
webidl.converters.WebSocketStreamOptions = webidl.createDictionaryConverter(
|
||||||
ArrayPrototypeJoin,
|
"WebSocketStreamOptions",
|
||||||
ArrayPrototypeMap,
|
[
|
||||||
Error,
|
{
|
||||||
ObjectPrototypeIsPrototypeOf,
|
key: "protocols",
|
||||||
PromisePrototypeCatch,
|
converter: webidl.converters["sequence<USVString>"],
|
||||||
PromisePrototypeThen,
|
get defaultValue() {
|
||||||
Set,
|
return [];
|
||||||
StringPrototypeEndsWith,
|
},
|
||||||
StringPrototypeToLowerCase,
|
},
|
||||||
Symbol,
|
{
|
||||||
SymbolFor,
|
key: "signal",
|
||||||
TypeError,
|
converter: webidl.converters.AbortSignal,
|
||||||
Uint8ArrayPrototype,
|
},
|
||||||
} = window.__bootstrap.primordials;
|
{
|
||||||
|
key: "headers",
|
||||||
|
converter: webidl.converters.HeadersInit,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
webidl.converters.WebSocketCloseInfo = webidl.createDictionaryConverter(
|
||||||
|
"WebSocketCloseInfo",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: "code",
|
||||||
|
converter: webidl.converters["unsigned short"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "reason",
|
||||||
|
converter: webidl.converters.USVString,
|
||||||
|
defaultValue: "",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
webidl.converters.WebSocketStreamOptions = webidl.createDictionaryConverter(
|
const CLOSE_RESPONSE_TIMEOUT = 5000;
|
||||||
"WebSocketStreamOptions",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: "protocols",
|
|
||||||
converter: webidl.converters["sequence<USVString>"],
|
|
||||||
get defaultValue() {
|
|
||||||
return [];
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "signal",
|
|
||||||
converter: webidl.converters.AbortSignal,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "headers",
|
|
||||||
converter: webidl.converters.HeadersInit,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
webidl.converters.WebSocketCloseInfo = webidl.createDictionaryConverter(
|
|
||||||
"WebSocketCloseInfo",
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: "code",
|
|
||||||
converter: webidl.converters["unsigned short"],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: "reason",
|
|
||||||
converter: webidl.converters.USVString,
|
|
||||||
defaultValue: "",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
const CLOSE_RESPONSE_TIMEOUT = 5000;
|
const _rid = Symbol("[[rid]]");
|
||||||
|
const _url = Symbol("[[url]]");
|
||||||
|
const _connection = Symbol("[[connection]]");
|
||||||
|
const _closed = Symbol("[[closed]]");
|
||||||
|
const _earlyClose = Symbol("[[earlyClose]]");
|
||||||
|
const _closeSent = Symbol("[[closeSent]]");
|
||||||
|
class WebSocketStream {
|
||||||
|
[_rid];
|
||||||
|
|
||||||
const _rid = Symbol("[[rid]]");
|
[_url];
|
||||||
const _url = Symbol("[[url]]");
|
get url() {
|
||||||
const _connection = Symbol("[[connection]]");
|
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||||
const _closed = Symbol("[[closed]]");
|
return this[_url];
|
||||||
const _earlyClose = Symbol("[[earlyClose]]");
|
}
|
||||||
const _closeSent = Symbol("[[closeSent]]");
|
|
||||||
class WebSocketStream {
|
|
||||||
[_rid];
|
|
||||||
|
|
||||||
[_url];
|
constructor(url, options) {
|
||||||
get url() {
|
this[webidl.brand] = webidl.brand;
|
||||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
const prefix = "Failed to construct 'WebSocketStream'";
|
||||||
return this[_url];
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
url = webidl.converters.USVString(url, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
options = webidl.converters.WebSocketStreamOptions(options, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
|
||||||
|
const wsURL = new URL(url);
|
||||||
|
|
||||||
|
if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
|
||||||
|
throw new DOMException(
|
||||||
|
"Only ws & wss schemes are allowed in a WebSocket URL.",
|
||||||
|
"SyntaxError",
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(url, options) {
|
if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
|
||||||
this[webidl.brand] = webidl.brand;
|
throw new DOMException(
|
||||||
const prefix = "Failed to construct 'WebSocketStream'";
|
"Fragments are not allowed in a WebSocket URL.",
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
"SyntaxError",
|
||||||
url = webidl.converters.USVString(url, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
options = webidl.converters.WebSocketStreamOptions(options, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
|
|
||||||
const wsURL = new URL(url);
|
|
||||||
|
|
||||||
if (wsURL.protocol !== "ws:" && wsURL.protocol !== "wss:") {
|
|
||||||
throw new DOMException(
|
|
||||||
"Only ws & wss schemes are allowed in a WebSocket URL.",
|
|
||||||
"SyntaxError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
|
|
||||||
throw new DOMException(
|
|
||||||
"Fragments are not allowed in a WebSocket URL.",
|
|
||||||
"SyntaxError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
this[_url] = wsURL.href;
|
|
||||||
|
|
||||||
if (
|
|
||||||
options.protocols.length !==
|
|
||||||
new Set(
|
|
||||||
ArrayPrototypeMap(
|
|
||||||
options.protocols,
|
|
||||||
(p) => StringPrototypeToLowerCase(p),
|
|
||||||
),
|
|
||||||
).size
|
|
||||||
) {
|
|
||||||
throw new DOMException(
|
|
||||||
"Can't supply multiple times the same protocol.",
|
|
||||||
"SyntaxError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const headers = headersFromHeaderList([], "request");
|
|
||||||
if (options.headers !== undefined) {
|
|
||||||
fillHeaders(headers, options.headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cancelRid = ops.op_ws_check_permission_and_cancel_handle(
|
|
||||||
"WebSocketStream.abort()",
|
|
||||||
this[_url],
|
|
||||||
true,
|
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.signal?.aborted) {
|
this[_url] = wsURL.href;
|
||||||
core.close(cancelRid);
|
|
||||||
const err = options.signal.reason;
|
if (
|
||||||
this[_connection].reject(err);
|
options.protocols.length !==
|
||||||
this[_closed].reject(err);
|
new Set(
|
||||||
} else {
|
ArrayPrototypeMap(
|
||||||
const abort = () => {
|
options.protocols,
|
||||||
core.close(cancelRid);
|
(p) => StringPrototypeToLowerCase(p),
|
||||||
};
|
|
||||||
options.signal?.[add](abort);
|
|
||||||
PromisePrototypeThen(
|
|
||||||
core.opAsync(
|
|
||||||
"op_ws_create",
|
|
||||||
"new WebSocketStream()",
|
|
||||||
this[_url],
|
|
||||||
options.protocols
|
|
||||||
? ArrayPrototypeJoin(options.protocols, ", ")
|
|
||||||
: "",
|
|
||||||
cancelRid,
|
|
||||||
headerListFromHeaders(headers),
|
|
||||||
),
|
),
|
||||||
(create) => {
|
).size
|
||||||
options.signal?.[remove](abort);
|
) {
|
||||||
if (this[_earlyClose]) {
|
throw new DOMException(
|
||||||
PromisePrototypeThen(
|
"Can't supply multiple times the same protocol.",
|
||||||
core.opAsync("op_ws_close", create.rid),
|
"SyntaxError",
|
||||||
() => {
|
);
|
||||||
PromisePrototypeThen(
|
}
|
||||||
(async () => {
|
|
||||||
while (true) {
|
|
||||||
const { kind } = await core.opAsync(
|
|
||||||
"op_ws_next_event",
|
|
||||||
create.rid,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (kind === "close") {
|
const headers = headersFromHeaderList([], "request");
|
||||||
break;
|
if (options.headers !== undefined) {
|
||||||
}
|
fillHeaders(headers, options.headers);
|
||||||
}
|
}
|
||||||
})(),
|
|
||||||
() => {
|
const cancelRid = ops.op_ws_check_permission_and_cancel_handle(
|
||||||
const err = new DOMException(
|
"WebSocketStream.abort()",
|
||||||
"Closed while connecting",
|
this[_url],
|
||||||
"NetworkError",
|
true,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (options.signal?.aborted) {
|
||||||
|
core.close(cancelRid);
|
||||||
|
const err = options.signal.reason;
|
||||||
|
this[_connection].reject(err);
|
||||||
|
this[_closed].reject(err);
|
||||||
|
} else {
|
||||||
|
const abort = () => {
|
||||||
|
core.close(cancelRid);
|
||||||
|
};
|
||||||
|
options.signal?.[add](abort);
|
||||||
|
PromisePrototypeThen(
|
||||||
|
core.opAsync(
|
||||||
|
"op_ws_create",
|
||||||
|
"new WebSocketStream()",
|
||||||
|
this[_url],
|
||||||
|
options.protocols ? ArrayPrototypeJoin(options.protocols, ", ") : "",
|
||||||
|
cancelRid,
|
||||||
|
headerListFromHeaders(headers),
|
||||||
|
),
|
||||||
|
(create) => {
|
||||||
|
options.signal?.[remove](abort);
|
||||||
|
if (this[_earlyClose]) {
|
||||||
|
PromisePrototypeThen(
|
||||||
|
core.opAsync("op_ws_close", create.rid),
|
||||||
|
() => {
|
||||||
|
PromisePrototypeThen(
|
||||||
|
(async () => {
|
||||||
|
while (true) {
|
||||||
|
const { kind } = await core.opAsync(
|
||||||
|
"op_ws_next_event",
|
||||||
|
create.rid,
|
||||||
);
|
);
|
||||||
this[_connection].reject(err);
|
|
||||||
this[_closed].reject(err);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
() => {
|
|
||||||
const err = new DOMException(
|
|
||||||
"Closed while connecting",
|
|
||||||
"NetworkError",
|
|
||||||
);
|
|
||||||
this[_connection].reject(err);
|
|
||||||
this[_closed].reject(err);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this[_rid] = create.rid;
|
|
||||||
|
|
||||||
const writable = new WritableStream({
|
if (kind === "close") {
|
||||||
write: async (chunk) => {
|
break;
|
||||||
if (typeof chunk === "string") {
|
}
|
||||||
await core.opAsync("op_ws_send", this[_rid], {
|
}
|
||||||
kind: "text",
|
})(),
|
||||||
value: chunk,
|
() => {
|
||||||
});
|
const err = new DOMException(
|
||||||
} else if (
|
"Closed while connecting",
|
||||||
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)
|
"NetworkError",
|
||||||
) {
|
|
||||||
await core.opAsync("op_ws_send", this[_rid], {
|
|
||||||
kind: "binary",
|
|
||||||
value: chunk,
|
|
||||||
}, chunk);
|
|
||||||
} else {
|
|
||||||
throw new TypeError(
|
|
||||||
"A chunk may only be either a string or an Uint8Array",
|
|
||||||
);
|
);
|
||||||
}
|
this[_connection].reject(err);
|
||||||
},
|
|
||||||
close: async (reason) => {
|
|
||||||
try {
|
|
||||||
this.close(reason?.code !== undefined ? reason : {});
|
|
||||||
} catch (_) {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
await this.closed;
|
|
||||||
},
|
|
||||||
abort: async (reason) => {
|
|
||||||
try {
|
|
||||||
this.close(reason?.code !== undefined ? reason : {});
|
|
||||||
} catch (_) {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
await this.closed;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
const pull = async (controller) => {
|
|
||||||
const { kind, value } = await core.opAsync(
|
|
||||||
"op_ws_next_event",
|
|
||||||
this[_rid],
|
|
||||||
);
|
|
||||||
|
|
||||||
switch (kind) {
|
|
||||||
case "string": {
|
|
||||||
controller.enqueue(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "binary": {
|
|
||||||
controller.enqueue(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "ping": {
|
|
||||||
await core.opAsync("op_ws_send", this[_rid], {
|
|
||||||
kind: "pong",
|
|
||||||
});
|
|
||||||
await pull(controller);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "closed":
|
|
||||||
case "close": {
|
|
||||||
this[_closed].resolve(value);
|
|
||||||
core.tryClose(this[_rid]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "error": {
|
|
||||||
const err = new Error(value);
|
|
||||||
this[_closed].reject(err);
|
this[_closed].reject(err);
|
||||||
controller.error(err);
|
},
|
||||||
core.tryClose(this[_rid]);
|
);
|
||||||
break;
|
},
|
||||||
}
|
() => {
|
||||||
}
|
const err = new DOMException(
|
||||||
|
"Closed while connecting",
|
||||||
|
"NetworkError",
|
||||||
|
);
|
||||||
|
this[_connection].reject(err);
|
||||||
|
this[_closed].reject(err);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this[_rid] = create.rid;
|
||||||
|
|
||||||
if (
|
const writable = new WritableStream({
|
||||||
this[_closeSent].state === "fulfilled" &&
|
write: async (chunk) => {
|
||||||
this[_closed].state === "pending"
|
if (typeof chunk === "string") {
|
||||||
|
await core.opAsync("op_ws_send", this[_rid], {
|
||||||
|
kind: "text",
|
||||||
|
value: chunk,
|
||||||
|
});
|
||||||
|
} else if (
|
||||||
|
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)
|
||||||
) {
|
) {
|
||||||
if (
|
await core.opAsync("op_ws_send", this[_rid], {
|
||||||
new Date().getTime() - await this[_closeSent].promise <=
|
kind: "binary",
|
||||||
CLOSE_RESPONSE_TIMEOUT
|
value: chunk,
|
||||||
) {
|
}, chunk);
|
||||||
return pull(controller);
|
} else {
|
||||||
}
|
throw new TypeError(
|
||||||
|
"A chunk may only be either a string or an Uint8Array",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close: async (reason) => {
|
||||||
|
try {
|
||||||
|
this.close(reason?.code !== undefined ? reason : {});
|
||||||
|
} catch (_) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
await this.closed;
|
||||||
|
},
|
||||||
|
abort: async (reason) => {
|
||||||
|
try {
|
||||||
|
this.close(reason?.code !== undefined ? reason : {});
|
||||||
|
} catch (_) {
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
|
await this.closed;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const pull = async (controller) => {
|
||||||
|
const { kind, value } = await core.opAsync(
|
||||||
|
"op_ws_next_event",
|
||||||
|
this[_rid],
|
||||||
|
);
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
case "string": {
|
||||||
|
controller.enqueue(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "binary": {
|
||||||
|
controller.enqueue(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "ping": {
|
||||||
|
await core.opAsync("op_ws_send", this[_rid], {
|
||||||
|
kind: "pong",
|
||||||
|
});
|
||||||
|
await pull(controller);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "closed":
|
||||||
|
case "close": {
|
||||||
this[_closed].resolve(value);
|
this[_closed].resolve(value);
|
||||||
core.tryClose(this[_rid]);
|
core.tryClose(this[_rid]);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
};
|
case "error": {
|
||||||
const readable = new ReadableStream({
|
const err = new Error(value);
|
||||||
start: (controller) => {
|
this[_closed].reject(err);
|
||||||
PromisePrototypeThen(this.closed, () => {
|
controller.error(err);
|
||||||
try {
|
core.tryClose(this[_rid]);
|
||||||
controller.close();
|
break;
|
||||||
} catch (_) {
|
}
|
||||||
// needed to ignore warnings & assertions
|
}
|
||||||
}
|
|
||||||
try {
|
|
||||||
PromisePrototypeCatch(
|
|
||||||
writableStreamClose(writable),
|
|
||||||
() => {},
|
|
||||||
);
|
|
||||||
} catch (_) {
|
|
||||||
// needed to ignore warnings & assertions
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
PromisePrototypeThen(this[_closeSent].promise, () => {
|
if (
|
||||||
if (this[_closed].state === "pending") {
|
this[_closeSent].state === "fulfilled" &&
|
||||||
return pull(controller);
|
this[_closed].state === "pending"
|
||||||
}
|
) {
|
||||||
});
|
if (
|
||||||
},
|
new Date().getTime() - await this[_closeSent].promise <=
|
||||||
pull,
|
CLOSE_RESPONSE_TIMEOUT
|
||||||
cancel: async (reason) => {
|
) {
|
||||||
|
return pull(controller);
|
||||||
|
}
|
||||||
|
|
||||||
|
this[_closed].resolve(value);
|
||||||
|
core.tryClose(this[_rid]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const readable = new ReadableStream({
|
||||||
|
start: (controller) => {
|
||||||
|
PromisePrototypeThen(this.closed, () => {
|
||||||
try {
|
try {
|
||||||
this.close(reason?.code !== undefined ? reason : {});
|
controller.close();
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
this.close();
|
// needed to ignore warnings & assertions
|
||||||
}
|
}
|
||||||
await this.closed;
|
try {
|
||||||
},
|
PromisePrototypeCatch(
|
||||||
});
|
writableStreamClose(writable),
|
||||||
|
() => {},
|
||||||
|
);
|
||||||
|
} catch (_) {
|
||||||
|
// needed to ignore warnings & assertions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this[_connection].resolve({
|
PromisePrototypeThen(this[_closeSent].promise, () => {
|
||||||
readable,
|
if (this[_closed].state === "pending") {
|
||||||
writable,
|
return pull(controller);
|
||||||
extensions: create.extensions ?? "",
|
}
|
||||||
protocol: create.protocol ?? "",
|
});
|
||||||
});
|
},
|
||||||
}
|
pull,
|
||||||
},
|
cancel: async (reason) => {
|
||||||
(err) => {
|
try {
|
||||||
if (ObjectPrototypeIsPrototypeOf(core.InterruptedPrototype, err)) {
|
this.close(reason?.code !== undefined ? reason : {});
|
||||||
// The signal was aborted.
|
} catch (_) {
|
||||||
err = options.signal.reason;
|
this.close();
|
||||||
} else {
|
}
|
||||||
core.tryClose(cancelRid);
|
await this.closed;
|
||||||
}
|
},
|
||||||
this[_connection].reject(err);
|
});
|
||||||
this[_closed].reject(err);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[_connection] = new Deferred();
|
this[_connection].resolve({
|
||||||
get connection() {
|
readable,
|
||||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
writable,
|
||||||
return this[_connection].promise;
|
extensions: create.extensions ?? "",
|
||||||
}
|
protocol: create.protocol ?? "",
|
||||||
|
});
|
||||||
[_earlyClose] = false;
|
}
|
||||||
[_closed] = new Deferred();
|
},
|
||||||
[_closeSent] = new Deferred();
|
(err) => {
|
||||||
get closed() {
|
if (ObjectPrototypeIsPrototypeOf(core.InterruptedPrototype, err)) {
|
||||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
// The signal was aborted.
|
||||||
return this[_closed].promise;
|
err = options.signal.reason;
|
||||||
}
|
} else {
|
||||||
|
core.tryClose(cancelRid);
|
||||||
close(closeInfo) {
|
}
|
||||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
this[_connection].reject(err);
|
||||||
closeInfo = webidl.converters.WebSocketCloseInfo(closeInfo, {
|
this[_closed].reject(err);
|
||||||
prefix: "Failed to execute 'close' on 'WebSocketStream'",
|
},
|
||||||
context: "Argument 1",
|
);
|
||||||
});
|
|
||||||
|
|
||||||
if (
|
|
||||||
closeInfo.code &&
|
|
||||||
!(closeInfo.code === 1000 ||
|
|
||||||
(3000 <= closeInfo.code && closeInfo.code < 5000))
|
|
||||||
) {
|
|
||||||
throw new DOMException(
|
|
||||||
"The close code must be either 1000 or in the range of 3000 to 4999.",
|
|
||||||
"InvalidAccessError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
|
||||||
if (
|
|
||||||
closeInfo.reason && encoder.encode(closeInfo.reason).byteLength > 123
|
|
||||||
) {
|
|
||||||
throw new DOMException(
|
|
||||||
"The close reason may not be longer than 123 bytes.",
|
|
||||||
"SyntaxError",
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let code = closeInfo.code;
|
|
||||||
if (closeInfo.reason && code === undefined) {
|
|
||||||
code = 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this[_connection].state === "pending") {
|
|
||||||
this[_earlyClose] = true;
|
|
||||||
} else if (this[_closed].state === "pending") {
|
|
||||||
PromisePrototypeThen(
|
|
||||||
core.opAsync("op_ws_close", this[_rid], code, closeInfo.reason),
|
|
||||||
() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
this[_closeSent].resolve(new Date().getTime());
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
(err) => {
|
|
||||||
this[_rid] && core.tryClose(this[_rid]);
|
|
||||||
this[_closed].reject(err);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[SymbolFor("Deno.customInspect")](inspect) {
|
|
||||||
return `${this.constructor.name} ${
|
|
||||||
inspect({
|
|
||||||
url: this.url,
|
|
||||||
})
|
|
||||||
}`;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const WebSocketStreamPrototype = WebSocketStream.prototype;
|
[_connection] = new Deferred();
|
||||||
|
get connection() {
|
||||||
|
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||||
|
return this[_connection].promise;
|
||||||
|
}
|
||||||
|
|
||||||
window.__bootstrap.webSocket.WebSocketStream = WebSocketStream;
|
[_earlyClose] = false;
|
||||||
})(this);
|
[_closed] = new Deferred();
|
||||||
|
[_closeSent] = new Deferred();
|
||||||
|
get closed() {
|
||||||
|
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||||
|
return this[_closed].promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(closeInfo) {
|
||||||
|
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||||
|
closeInfo = webidl.converters.WebSocketCloseInfo(closeInfo, {
|
||||||
|
prefix: "Failed to execute 'close' on 'WebSocketStream'",
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
closeInfo.code &&
|
||||||
|
!(closeInfo.code === 1000 ||
|
||||||
|
(3000 <= closeInfo.code && closeInfo.code < 5000))
|
||||||
|
) {
|
||||||
|
throw new DOMException(
|
||||||
|
"The close code must be either 1000 or in the range of 3000 to 4999.",
|
||||||
|
"InvalidAccessError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const encoder = new TextEncoder();
|
||||||
|
if (
|
||||||
|
closeInfo.reason && encoder.encode(closeInfo.reason).byteLength > 123
|
||||||
|
) {
|
||||||
|
throw new DOMException(
|
||||||
|
"The close reason may not be longer than 123 bytes.",
|
||||||
|
"SyntaxError",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let code = closeInfo.code;
|
||||||
|
if (closeInfo.reason && code === undefined) {
|
||||||
|
code = 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this[_connection].state === "pending") {
|
||||||
|
this[_earlyClose] = true;
|
||||||
|
} else if (this[_closed].state === "pending") {
|
||||||
|
PromisePrototypeThen(
|
||||||
|
core.opAsync("op_ws_close", this[_rid], code, closeInfo.reason),
|
||||||
|
() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
this[_closeSent].resolve(new Date().getTime());
|
||||||
|
}, 0);
|
||||||
|
},
|
||||||
|
(err) => {
|
||||||
|
this[_rid] && core.tryClose(this[_rid]);
|
||||||
|
this[_closed].reject(err);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[SymbolFor("Deno.customInspect")](inspect) {
|
||||||
|
return `${this.constructor.name} ${
|
||||||
|
inspect({
|
||||||
|
url: this.url,
|
||||||
|
})
|
||||||
|
}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const WebSocketStreamPrototype = WebSocketStream.prototype;
|
||||||
|
|
||||||
|
export { WebSocketStream };
|
||||||
|
|
|
@ -504,7 +504,7 @@ pub fn init<P: WebSocketPermissions + 'static>(
|
||||||
) -> Extension {
|
) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_url", "deno_webidl"])
|
.dependencies(vec!["deno_url", "deno_webidl"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/websocket",
|
prefix "internal:ext/websocket",
|
||||||
"01_websocket.js",
|
"01_websocket.js",
|
||||||
"02_websocketstream.js",
|
"02_websocketstream.js",
|
||||||
|
|
|
@ -2,191 +2,189 @@
|
||||||
|
|
||||||
/// <reference path="../../core/internal.d.ts" />
|
/// <reference path="../../core/internal.d.ts" />
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const webidl = window.__bootstrap.webidl;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
SafeArrayIterator,
|
SafeArrayIterator,
|
||||||
Symbol,
|
Symbol,
|
||||||
SymbolFor,
|
SymbolFor,
|
||||||
ObjectDefineProperty,
|
ObjectDefineProperty,
|
||||||
ObjectFromEntries,
|
ObjectFromEntries,
|
||||||
ObjectEntries,
|
ObjectEntries,
|
||||||
ReflectGet,
|
ReflectGet,
|
||||||
ReflectHas,
|
ReflectHas,
|
||||||
Proxy,
|
Proxy,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
|
|
||||||
const _persistent = Symbol("[[persistent]]");
|
const _persistent = Symbol("[[persistent]]");
|
||||||
|
|
||||||
class Storage {
|
class Storage {
|
||||||
[_persistent];
|
[_persistent];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
webidl.illegalConstructor();
|
webidl.illegalConstructor();
|
||||||
}
|
|
||||||
|
|
||||||
get length() {
|
|
||||||
webidl.assertBranded(this, StoragePrototype);
|
|
||||||
return ops.op_webstorage_length(this[_persistent]);
|
|
||||||
}
|
|
||||||
|
|
||||||
key(index) {
|
|
||||||
webidl.assertBranded(this, StoragePrototype);
|
|
||||||
const prefix = "Failed to execute 'key' on 'Storage'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
index = webidl.converters["unsigned long"](index, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
return ops.op_webstorage_key(index, this[_persistent]);
|
|
||||||
}
|
|
||||||
|
|
||||||
setItem(key, value) {
|
|
||||||
webidl.assertBranded(this, StoragePrototype);
|
|
||||||
const prefix = "Failed to execute 'setItem' on 'Storage'";
|
|
||||||
webidl.requiredArguments(arguments.length, 2, { prefix });
|
|
||||||
key = webidl.converters.DOMString(key, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
value = webidl.converters.DOMString(value, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
});
|
|
||||||
|
|
||||||
ops.op_webstorage_set(key, value, this[_persistent]);
|
|
||||||
}
|
|
||||||
|
|
||||||
getItem(key) {
|
|
||||||
webidl.assertBranded(this, StoragePrototype);
|
|
||||||
const prefix = "Failed to execute 'getItem' on 'Storage'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
key = webidl.converters.DOMString(key, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
return ops.op_webstorage_get(key, this[_persistent]);
|
|
||||||
}
|
|
||||||
|
|
||||||
removeItem(key) {
|
|
||||||
webidl.assertBranded(this, StoragePrototype);
|
|
||||||
const prefix = "Failed to execute 'removeItem' on 'Storage'";
|
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
|
||||||
key = webidl.converters.DOMString(key, {
|
|
||||||
prefix,
|
|
||||||
context: "Argument 1",
|
|
||||||
});
|
|
||||||
|
|
||||||
ops.op_webstorage_remove(key, this[_persistent]);
|
|
||||||
}
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
webidl.assertBranded(this, StoragePrototype);
|
|
||||||
ops.op_webstorage_clear(this[_persistent]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const StoragePrototype = Storage.prototype;
|
get length() {
|
||||||
|
webidl.assertBranded(this, StoragePrototype);
|
||||||
|
return ops.op_webstorage_length(this[_persistent]);
|
||||||
|
}
|
||||||
|
|
||||||
function createStorage(persistent) {
|
key(index) {
|
||||||
const storage = webidl.createBranded(Storage);
|
webidl.assertBranded(this, StoragePrototype);
|
||||||
storage[_persistent] = persistent;
|
const prefix = "Failed to execute 'key' on 'Storage'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
const proxy = new Proxy(storage, {
|
index = webidl.converters["unsigned long"](index, {
|
||||||
deleteProperty(target, key) {
|
prefix,
|
||||||
if (typeof key == "symbol") {
|
context: "Argument 1",
|
||||||
delete target[key];
|
|
||||||
} else {
|
|
||||||
target.removeItem(key);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
defineProperty(target, key, descriptor) {
|
|
||||||
if (typeof key == "symbol") {
|
|
||||||
ObjectDefineProperty(target, key, descriptor);
|
|
||||||
} else {
|
|
||||||
target.setItem(key, descriptor.value);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
get(target, key) {
|
|
||||||
if (typeof key == "symbol") return target[key];
|
|
||||||
if (ReflectHas(target, key)) {
|
|
||||||
return ReflectGet(...new SafeArrayIterator(arguments));
|
|
||||||
} else {
|
|
||||||
return target.getItem(key) ?? undefined;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
set(target, key, value) {
|
|
||||||
if (typeof key == "symbol") {
|
|
||||||
ObjectDefineProperty(target, key, {
|
|
||||||
value,
|
|
||||||
configurable: true,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
target.setItem(key, value);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
has(target, p) {
|
|
||||||
return p === SymbolFor("Deno.customInspect") ||
|
|
||||||
(typeof target.getItem(p)) === "string";
|
|
||||||
},
|
|
||||||
ownKeys() {
|
|
||||||
return ops.op_webstorage_iterate_keys(persistent);
|
|
||||||
},
|
|
||||||
getOwnPropertyDescriptor(target, key) {
|
|
||||||
if (arguments.length === 1) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
if (ReflectHas(target, key)) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
const value = target.getItem(key);
|
|
||||||
if (value === null) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
writable: true,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
proxy[SymbolFor("Deno.customInspect")] = function (inspect) {
|
return ops.op_webstorage_key(index, this[_persistent]);
|
||||||
return `${this.constructor.name} ${
|
|
||||||
inspect({
|
|
||||||
length: this.length,
|
|
||||||
...ObjectFromEntries(ObjectEntries(proxy)),
|
|
||||||
})
|
|
||||||
}`;
|
|
||||||
};
|
|
||||||
|
|
||||||
return proxy;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let localStorage;
|
setItem(key, value) {
|
||||||
let sessionStorage;
|
webidl.assertBranded(this, StoragePrototype);
|
||||||
|
const prefix = "Failed to execute 'setItem' on 'Storage'";
|
||||||
|
webidl.requiredArguments(arguments.length, 2, { prefix });
|
||||||
|
key = webidl.converters.DOMString(key, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
value = webidl.converters.DOMString(value, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
});
|
||||||
|
|
||||||
window.__bootstrap.webStorage = {
|
ops.op_webstorage_set(key, value, this[_persistent]);
|
||||||
localStorage() {
|
}
|
||||||
if (!localStorage) {
|
|
||||||
localStorage = createStorage(true);
|
getItem(key) {
|
||||||
|
webidl.assertBranded(this, StoragePrototype);
|
||||||
|
const prefix = "Failed to execute 'getItem' on 'Storage'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
key = webidl.converters.DOMString(key, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
return ops.op_webstorage_get(key, this[_persistent]);
|
||||||
|
}
|
||||||
|
|
||||||
|
removeItem(key) {
|
||||||
|
webidl.assertBranded(this, StoragePrototype);
|
||||||
|
const prefix = "Failed to execute 'removeItem' on 'Storage'";
|
||||||
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
|
key = webidl.converters.DOMString(key, {
|
||||||
|
prefix,
|
||||||
|
context: "Argument 1",
|
||||||
|
});
|
||||||
|
|
||||||
|
ops.op_webstorage_remove(key, this[_persistent]);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
webidl.assertBranded(this, StoragePrototype);
|
||||||
|
ops.op_webstorage_clear(this[_persistent]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const StoragePrototype = Storage.prototype;
|
||||||
|
|
||||||
|
function createStorage(persistent) {
|
||||||
|
const storage = webidl.createBranded(Storage);
|
||||||
|
storage[_persistent] = persistent;
|
||||||
|
|
||||||
|
const proxy = new Proxy(storage, {
|
||||||
|
deleteProperty(target, key) {
|
||||||
|
if (typeof key == "symbol") {
|
||||||
|
delete target[key];
|
||||||
|
} else {
|
||||||
|
target.removeItem(key);
|
||||||
}
|
}
|
||||||
return localStorage;
|
return true;
|
||||||
},
|
},
|
||||||
sessionStorage() {
|
defineProperty(target, key, descriptor) {
|
||||||
if (!sessionStorage) {
|
if (typeof key == "symbol") {
|
||||||
sessionStorage = createStorage(false);
|
ObjectDefineProperty(target, key, descriptor);
|
||||||
|
} else {
|
||||||
|
target.setItem(key, descriptor.value);
|
||||||
}
|
}
|
||||||
return sessionStorage;
|
return true;
|
||||||
},
|
},
|
||||||
Storage,
|
get(target, key) {
|
||||||
|
if (typeof key == "symbol") return target[key];
|
||||||
|
if (ReflectHas(target, key)) {
|
||||||
|
return ReflectGet(...new SafeArrayIterator(arguments));
|
||||||
|
} else {
|
||||||
|
return target.getItem(key) ?? undefined;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
set(target, key, value) {
|
||||||
|
if (typeof key == "symbol") {
|
||||||
|
ObjectDefineProperty(target, key, {
|
||||||
|
value,
|
||||||
|
configurable: true,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
target.setItem(key, value);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
has(target, p) {
|
||||||
|
return p === SymbolFor("Deno.customInspect") ||
|
||||||
|
(typeof target.getItem(p)) === "string";
|
||||||
|
},
|
||||||
|
ownKeys() {
|
||||||
|
return ops.op_webstorage_iterate_keys(persistent);
|
||||||
|
},
|
||||||
|
getOwnPropertyDescriptor(target, key) {
|
||||||
|
if (arguments.length === 1) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
if (ReflectHas(target, key)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const value = target.getItem(key);
|
||||||
|
if (value === null) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
writable: true,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
proxy[SymbolFor("Deno.customInspect")] = function (inspect) {
|
||||||
|
return `${this.constructor.name} ${
|
||||||
|
inspect({
|
||||||
|
length: this.length,
|
||||||
|
...ObjectFromEntries(ObjectEntries(proxy)),
|
||||||
|
})
|
||||||
|
}`;
|
||||||
};
|
};
|
||||||
})(this);
|
|
||||||
|
return proxy;
|
||||||
|
}
|
||||||
|
|
||||||
|
let localStorageStorage;
|
||||||
|
function localStorage() {
|
||||||
|
if (!localStorageStorage) {
|
||||||
|
localStorageStorage = createStorage(true);
|
||||||
|
}
|
||||||
|
return localStorageStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
let sessionStorageStorage;
|
||||||
|
function sessionStorage() {
|
||||||
|
if (!sessionStorageStorage) {
|
||||||
|
sessionStorageStorage = createStorage(false);
|
||||||
|
}
|
||||||
|
return sessionStorageStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { localStorage, sessionStorage, Storage };
|
||||||
|
|
|
@ -24,7 +24,7 @@ const MAX_STORAGE_BYTES: u32 = 10 * 1024 * 1024;
|
||||||
pub fn init(origin_storage_dir: Option<PathBuf>) -> Extension {
|
pub fn init(origin_storage_dir: Option<PathBuf>) -> Extension {
|
||||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||||
.dependencies(vec!["deno_webidl"])
|
.dependencies(vec!["deno_webidl"])
|
||||||
.js(include_js_files!(
|
.esm(include_js_files!(
|
||||||
prefix "internal:ext/webstorage",
|
prefix "internal:ext/webstorage",
|
||||||
"01_webstorage.js",
|
"01_webstorage.js",
|
||||||
))
|
))
|
||||||
|
|
|
@ -5,7 +5,6 @@ use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
// This is a shim that allows to generate documentation on docs.rs
|
// This is a shim that allows to generate documentation on docs.rs
|
||||||
#[cfg(not(feature = "docsrs"))]
|
|
||||||
mod not_docs {
|
mod not_docs {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
@ -121,7 +120,7 @@ mod not_docs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_runtime_snapshot(snapshot_path: PathBuf, files: Vec<PathBuf>) {
|
fn create_runtime_snapshot(snapshot_path: PathBuf, esm_files: Vec<PathBuf>) {
|
||||||
let extensions_with_js: Vec<Extension> = vec![
|
let extensions_with_js: Vec<Extension> = vec![
|
||||||
deno_webidl::init(),
|
deno_webidl::init(),
|
||||||
deno_console::init(),
|
deno_console::init(),
|
||||||
|
@ -158,7 +157,8 @@ mod not_docs {
|
||||||
startup_snapshot: None,
|
startup_snapshot: None,
|
||||||
extensions: vec![],
|
extensions: vec![],
|
||||||
extensions_with_js,
|
extensions_with_js,
|
||||||
additional_files: files,
|
additional_files: vec![],
|
||||||
|
additional_esm_files: esm_files,
|
||||||
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
||||||
lzzzz::lz4_hc::compress_to_vec(
|
lzzzz::lz4_hc::compress_to_vec(
|
||||||
snapshot_slice,
|
snapshot_slice,
|
||||||
|
@ -172,14 +172,19 @@ mod not_docs {
|
||||||
|
|
||||||
pub fn build_snapshot(runtime_snapshot_path: PathBuf) {
|
pub fn build_snapshot(runtime_snapshot_path: PathBuf) {
|
||||||
#[allow(unused_mut)]
|
#[allow(unused_mut)]
|
||||||
let mut js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js");
|
let mut esm_files = get_js_files(
|
||||||
|
env!("CARGO_MANIFEST_DIR"),
|
||||||
|
"js",
|
||||||
|
Some(Box::new(|path| !path.ends_with("99_main.js"))),
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(not(feature = "snapshot_from_snapshot"))]
|
#[cfg(not(feature = "snapshot_from_snapshot"))]
|
||||||
{
|
{
|
||||||
let manifest = env!("CARGO_MANIFEST_DIR");
|
let manifest = env!("CARGO_MANIFEST_DIR");
|
||||||
let path = PathBuf::from(manifest);
|
let path = PathBuf::from(manifest);
|
||||||
js_files.push(path.join("js").join("99_main.js"));
|
esm_files.push(path.join("js").join("99_main.js"));
|
||||||
}
|
}
|
||||||
create_runtime_snapshot(runtime_snapshot_path, js_files);
|
create_runtime_snapshot(runtime_snapshot_path, esm_files);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,28 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const { ObjectFreeze, StringPrototypeSplit } = window.__bootstrap.primordials;
|
const { ObjectFreeze, StringPrototypeSplit } = primordials;
|
||||||
|
|
||||||
const build = {
|
const build = {
|
||||||
target: "unknown",
|
target: "unknown",
|
||||||
arch: "unknown",
|
arch: "unknown",
|
||||||
os: "unknown",
|
os: "unknown",
|
||||||
vendor: "unknown",
|
vendor: "unknown",
|
||||||
env: undefined,
|
env: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
function setBuildInfo(target) {
|
function setBuildInfo(target) {
|
||||||
const { 0: arch, 1: vendor, 2: os, 3: env } = StringPrototypeSplit(
|
const { 0: arch, 1: vendor, 2: os, 3: env } = StringPrototypeSplit(
|
||||||
target,
|
target,
|
||||||
"-",
|
"-",
|
||||||
4,
|
4,
|
||||||
);
|
);
|
||||||
build.target = target;
|
build.target = target;
|
||||||
build.arch = arch;
|
build.arch = arch;
|
||||||
build.vendor = vendor;
|
build.vendor = vendor;
|
||||||
build.os = os;
|
build.os = os;
|
||||||
build.env = env;
|
build.env = env;
|
||||||
ObjectFreeze(build);
|
ObjectFreeze(build);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.build = {
|
export { build, setBuildInfo };
|
||||||
build,
|
|
||||||
setBuildInfo,
|
|
||||||
};
|
|
||||||
})(this);
|
|
||||||
|
|
|
@ -1,153 +1,149 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const { BadResource, Interrupted } = core;
|
||||||
const { Error } = window.__bootstrap.primordials;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const { BadResource, Interrupted } = core;
|
const { Error } = primordials;
|
||||||
|
|
||||||
class NotFound extends Error {
|
class NotFound extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "NotFound";
|
this.name = "NotFound";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PermissionDenied extends Error {
|
class PermissionDenied extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "PermissionDenied";
|
this.name = "PermissionDenied";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ConnectionRefused extends Error {
|
class ConnectionRefused extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "ConnectionRefused";
|
this.name = "ConnectionRefused";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ConnectionReset extends Error {
|
class ConnectionReset extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "ConnectionReset";
|
this.name = "ConnectionReset";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class ConnectionAborted extends Error {
|
class ConnectionAborted extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "ConnectionAborted";
|
this.name = "ConnectionAborted";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NotConnected extends Error {
|
class NotConnected extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "NotConnected";
|
this.name = "NotConnected";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AddrInUse extends Error {
|
class AddrInUse extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "AddrInUse";
|
this.name = "AddrInUse";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AddrNotAvailable extends Error {
|
class AddrNotAvailable extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "AddrNotAvailable";
|
this.name = "AddrNotAvailable";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class BrokenPipe extends Error {
|
class BrokenPipe extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "BrokenPipe";
|
this.name = "BrokenPipe";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class AlreadyExists extends Error {
|
class AlreadyExists extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "AlreadyExists";
|
this.name = "AlreadyExists";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class InvalidData extends Error {
|
class InvalidData extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "InvalidData";
|
this.name = "InvalidData";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TimedOut extends Error {
|
class TimedOut extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "TimedOut";
|
this.name = "TimedOut";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class WriteZero extends Error {
|
class WriteZero extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "WriteZero";
|
this.name = "WriteZero";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class UnexpectedEof extends Error {
|
class UnexpectedEof extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "UnexpectedEof";
|
this.name = "UnexpectedEof";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Http extends Error {
|
class Http extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "Http";
|
this.name = "Http";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Busy extends Error {
|
class Busy extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "Busy";
|
this.name = "Busy";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class NotSupported extends Error {
|
class NotSupported extends Error {
|
||||||
constructor(msg) {
|
constructor(msg) {
|
||||||
super(msg);
|
super(msg);
|
||||||
this.name = "NotSupported";
|
this.name = "NotSupported";
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const errors = {
|
const errors = {
|
||||||
NotFound,
|
NotFound,
|
||||||
PermissionDenied,
|
PermissionDenied,
|
||||||
ConnectionRefused,
|
ConnectionRefused,
|
||||||
ConnectionReset,
|
ConnectionReset,
|
||||||
ConnectionAborted,
|
ConnectionAborted,
|
||||||
NotConnected,
|
NotConnected,
|
||||||
AddrInUse,
|
AddrInUse,
|
||||||
AddrNotAvailable,
|
AddrNotAvailable,
|
||||||
BrokenPipe,
|
BrokenPipe,
|
||||||
AlreadyExists,
|
AlreadyExists,
|
||||||
InvalidData,
|
InvalidData,
|
||||||
TimedOut,
|
TimedOut,
|
||||||
Interrupted,
|
Interrupted,
|
||||||
WriteZero,
|
WriteZero,
|
||||||
UnexpectedEof,
|
UnexpectedEof,
|
||||||
BadResource,
|
BadResource,
|
||||||
Http,
|
Http,
|
||||||
Busy,
|
Busy,
|
||||||
NotSupported,
|
NotSupported,
|
||||||
};
|
};
|
||||||
|
|
||||||
window.__bootstrap.errors = {
|
export { errors };
|
||||||
errors,
|
|
||||||
};
|
|
||||||
})(this);
|
|
||||||
|
|
|
@ -1,29 +1,24 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const { ObjectFreeze } = window.__bootstrap.primordials;
|
const { ObjectFreeze } = primordials;
|
||||||
|
|
||||||
const version = {
|
const version = {
|
||||||
deno: "",
|
deno: "",
|
||||||
v8: "",
|
v8: "",
|
||||||
typescript: "",
|
typescript: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
function setVersions(
|
function setVersions(
|
||||||
denoVersion,
|
denoVersion,
|
||||||
v8Version,
|
v8Version,
|
||||||
tsVersion,
|
tsVersion,
|
||||||
) {
|
) {
|
||||||
version.deno = denoVersion;
|
version.deno = denoVersion;
|
||||||
version.v8 = v8Version;
|
version.v8 = v8Version;
|
||||||
version.typescript = tsVersion;
|
version.typescript = tsVersion;
|
||||||
|
|
||||||
ObjectFreeze(version);
|
ObjectFreeze(version);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.version = {
|
export { setVersions, version };
|
||||||
version,
|
|
||||||
setVersions,
|
|
||||||
};
|
|
||||||
})(this);
|
|
||||||
|
|
|
@ -1,25 +0,0 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
|
||||||
const { TypeError, Symbol } = window.__bootstrap.primordials;
|
|
||||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
|
||||||
|
|
||||||
function requiredArguments(
|
|
||||||
name,
|
|
||||||
length,
|
|
||||||
required,
|
|
||||||
) {
|
|
||||||
if (length < required) {
|
|
||||||
const errMsg = `${name} requires at least ${required} argument${
|
|
||||||
required === 1 ? "" : "s"
|
|
||||||
}, but only ${length} present`;
|
|
||||||
throw new TypeError(errMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.webUtil = {
|
|
||||||
illegalConstructorKey,
|
|
||||||
requiredArguments,
|
|
||||||
};
|
|
||||||
})(this);
|
|
|
@ -1,150 +1,147 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const internals = globalThis.__bootstrap.internals;
|
||||||
const {
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
decodeURIComponent,
|
const {
|
||||||
ObjectPrototypeIsPrototypeOf,
|
decodeURIComponent,
|
||||||
Promise,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
SafeArrayIterator,
|
Promise,
|
||||||
StringPrototypeReplace,
|
SafeArrayIterator,
|
||||||
TypeError,
|
StringPrototypeReplace,
|
||||||
} = window.__bootstrap.primordials;
|
TypeError,
|
||||||
const { build } = window.__bootstrap.build;
|
} = primordials;
|
||||||
const { URLPrototype } = window.__bootstrap.url;
|
import { build } from "internal:runtime/js/01_build.js";
|
||||||
let logDebug = false;
|
import { URLPrototype } from "internal:ext/url/00_url.js";
|
||||||
let logSource = "JS";
|
let logDebug = false;
|
||||||
|
let logSource = "JS";
|
||||||
|
|
||||||
function setLogDebug(debug, source) {
|
function setLogDebug(debug, source) {
|
||||||
logDebug = debug;
|
logDebug = debug;
|
||||||
if (source) {
|
if (source) {
|
||||||
logSource = source;
|
logSource = source;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function log(...args) {
|
function log(...args) {
|
||||||
if (logDebug) {
|
if (logDebug) {
|
||||||
// if we destructure `console` off `globalThis` too early, we don't bind to
|
// if we destructure `console` off `globalThis` too early, we don't bind to
|
||||||
// the right console, therefore we don't log anything out.
|
// the right console, therefore we don't log anything out.
|
||||||
globalThis.console.log(
|
globalThis.console.log(
|
||||||
`DEBUG ${logSource} -`,
|
`DEBUG ${logSource} -`,
|
||||||
...new SafeArrayIterator(args),
|
...new SafeArrayIterator(args),
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function createResolvable() {
|
|
||||||
let resolve;
|
|
||||||
let reject;
|
|
||||||
const promise = new Promise((res, rej) => {
|
|
||||||
resolve = res;
|
|
||||||
reject = rej;
|
|
||||||
});
|
|
||||||
promise.resolve = resolve;
|
|
||||||
promise.reject = reject;
|
|
||||||
return promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep in sync with `fromFileUrl()` in `std/path/win32.ts`.
|
|
||||||
function pathFromURLWin32(url) {
|
|
||||||
let p = StringPrototypeReplace(
|
|
||||||
url.pathname,
|
|
||||||
/^\/*([A-Za-z]:)(\/|$)/,
|
|
||||||
"$1/",
|
|
||||||
);
|
|
||||||
p = StringPrototypeReplace(
|
|
||||||
p,
|
|
||||||
/\//g,
|
|
||||||
"\\",
|
|
||||||
);
|
|
||||||
p = StringPrototypeReplace(
|
|
||||||
p,
|
|
||||||
/%(?![0-9A-Fa-f]{2})/g,
|
|
||||||
"%25",
|
|
||||||
);
|
|
||||||
let path = decodeURIComponent(p);
|
|
||||||
if (url.hostname != "") {
|
|
||||||
// Note: The `URL` implementation guarantees that the drive letter and
|
|
||||||
// hostname are mutually exclusive. Otherwise it would not have been valid
|
|
||||||
// to append the hostname and path like this.
|
|
||||||
path = `\\\\${url.hostname}${path}`;
|
|
||||||
}
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keep in sync with `fromFileUrl()` in `std/path/posix.ts`.
|
|
||||||
function pathFromURLPosix(url) {
|
|
||||||
if (url.hostname !== "") {
|
|
||||||
throw new TypeError(`Host must be empty.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return decodeURIComponent(
|
|
||||||
StringPrototypeReplace(url.pathname, /%(?![0-9A-Fa-f]{2})/g, "%25"),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function pathFromURL(pathOrUrl) {
|
function createResolvable() {
|
||||||
if (ObjectPrototypeIsPrototypeOf(URLPrototype, pathOrUrl)) {
|
let resolve;
|
||||||
if (pathOrUrl.protocol != "file:") {
|
let reject;
|
||||||
throw new TypeError("Must be a file URL.");
|
const promise = new Promise((res, rej) => {
|
||||||
}
|
resolve = res;
|
||||||
|
reject = rej;
|
||||||
|
});
|
||||||
|
promise.resolve = resolve;
|
||||||
|
promise.reject = reject;
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
|
||||||
return build.os == "windows"
|
// Keep in sync with `fromFileUrl()` in `std/path/win32.ts`.
|
||||||
? pathFromURLWin32(pathOrUrl)
|
function pathFromURLWin32(url) {
|
||||||
: pathFromURLPosix(pathOrUrl);
|
let p = StringPrototypeReplace(
|
||||||
}
|
url.pathname,
|
||||||
return pathOrUrl;
|
/^\/*([A-Za-z]:)(\/|$)/,
|
||||||
|
"$1/",
|
||||||
|
);
|
||||||
|
p = StringPrototypeReplace(
|
||||||
|
p,
|
||||||
|
/\//g,
|
||||||
|
"\\",
|
||||||
|
);
|
||||||
|
p = StringPrototypeReplace(
|
||||||
|
p,
|
||||||
|
/%(?![0-9A-Fa-f]{2})/g,
|
||||||
|
"%25",
|
||||||
|
);
|
||||||
|
let path = decodeURIComponent(p);
|
||||||
|
if (url.hostname != "") {
|
||||||
|
// Note: The `URL` implementation guarantees that the drive letter and
|
||||||
|
// hostname are mutually exclusive. Otherwise it would not have been valid
|
||||||
|
// to append the hostname and path like this.
|
||||||
|
path = `\\\\${url.hostname}${path}`;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep in sync with `fromFileUrl()` in `std/path/posix.ts`.
|
||||||
|
function pathFromURLPosix(url) {
|
||||||
|
if (url.hostname !== "") {
|
||||||
|
throw new TypeError(`Host must be empty.`);
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__bootstrap.internals = {
|
return decodeURIComponent(
|
||||||
...window.__bootstrap.internals ?? {},
|
StringPrototypeReplace(url.pathname, /%(?![0-9A-Fa-f]{2})/g, "%25"),
|
||||||
pathFromURL,
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pathFromURL(pathOrUrl) {
|
||||||
|
if (ObjectPrototypeIsPrototypeOf(URLPrototype, pathOrUrl)) {
|
||||||
|
if (pathOrUrl.protocol != "file:") {
|
||||||
|
throw new TypeError("Must be a file URL.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return build.os == "windows"
|
||||||
|
? pathFromURLWin32(pathOrUrl)
|
||||||
|
: pathFromURLPosix(pathOrUrl);
|
||||||
|
}
|
||||||
|
return pathOrUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(bartlomieju): remove
|
||||||
|
internals.pathFromURL = pathFromURL;
|
||||||
|
|
||||||
|
function writable(value) {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
writable: true,
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function writable(value) {
|
function nonEnumerable(value) {
|
||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
writable: true,
|
writable: true,
|
||||||
enumerable: true,
|
enumerable: false,
|
||||||
configurable: true,
|
configurable: true,
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function nonEnumerable(value) {
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
writable: true,
|
|
||||||
enumerable: false,
|
|
||||||
configurable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function readOnly(value) {
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
enumerable: true,
|
|
||||||
writable: false,
|
|
||||||
configurable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getterOnly(getter) {
|
|
||||||
return {
|
|
||||||
get: getter,
|
|
||||||
set() {},
|
|
||||||
enumerable: true,
|
|
||||||
configurable: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.util = {
|
|
||||||
log,
|
|
||||||
setLogDebug,
|
|
||||||
createResolvable,
|
|
||||||
pathFromURL,
|
|
||||||
writable,
|
|
||||||
nonEnumerable,
|
|
||||||
readOnly,
|
|
||||||
getterOnly,
|
|
||||||
};
|
};
|
||||||
})(this);
|
}
|
||||||
|
|
||||||
|
function readOnly(value) {
|
||||||
|
return {
|
||||||
|
value,
|
||||||
|
enumerable: true,
|
||||||
|
writable: false,
|
||||||
|
configurable: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getterOnly(getter) {
|
||||||
|
return {
|
||||||
|
get: getter,
|
||||||
|
set() {},
|
||||||
|
enumerable: true,
|
||||||
|
configurable: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
createResolvable,
|
||||||
|
getterOnly,
|
||||||
|
log,
|
||||||
|
nonEnumerable,
|
||||||
|
pathFromURL,
|
||||||
|
readOnly,
|
||||||
|
setLogDebug,
|
||||||
|
writable,
|
||||||
|
};
|
||||||
|
|
|
@ -1,287 +1,282 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const { ops } = Deno.core;
|
const ops = core.ops;
|
||||||
const { Event } = window.__bootstrap.event;
|
import { Event, EventTarget } from "internal:ext/web/02_event.js";
|
||||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
import { pathFromURL } from "internal:runtime/js/06_util.js";
|
||||||
const { pathFromURL } = window.__bootstrap.util;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const { illegalConstructorKey } = window.__bootstrap.webUtil;
|
const {
|
||||||
const {
|
ArrayIsArray,
|
||||||
ArrayIsArray,
|
ArrayPrototypeIncludes,
|
||||||
ArrayPrototypeIncludes,
|
ArrayPrototypeMap,
|
||||||
ArrayPrototypeMap,
|
ArrayPrototypeSlice,
|
||||||
ArrayPrototypeSlice,
|
Map,
|
||||||
Map,
|
MapPrototypeGet,
|
||||||
MapPrototypeGet,
|
MapPrototypeHas,
|
||||||
MapPrototypeHas,
|
MapPrototypeSet,
|
||||||
MapPrototypeSet,
|
FunctionPrototypeCall,
|
||||||
FunctionPrototypeCall,
|
PromiseResolve,
|
||||||
PromiseResolve,
|
PromiseReject,
|
||||||
PromiseReject,
|
ReflectHas,
|
||||||
ReflectHas,
|
SafeArrayIterator,
|
||||||
SafeArrayIterator,
|
Symbol,
|
||||||
SymbolFor,
|
SymbolFor,
|
||||||
TypeError,
|
TypeError,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
|
|
||||||
/**
|
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||||
* @typedef StatusCacheValue
|
|
||||||
* @property {PermissionState} state
|
|
||||||
* @property {PermissionStatus} status
|
|
||||||
*/
|
|
||||||
|
|
||||||
/** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "sys" | "run" | "ffi" | "hrtime">} */
|
/**
|
||||||
const permissionNames = [
|
* @typedef StatusCacheValue
|
||||||
"read",
|
* @property {PermissionState} state
|
||||||
"write",
|
* @property {PermissionStatus} status
|
||||||
"net",
|
*/
|
||||||
"env",
|
|
||||||
"sys",
|
|
||||||
"run",
|
|
||||||
"ffi",
|
|
||||||
"hrtime",
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "sys" | "run" | "ffi" | "hrtime">} */
|
||||||
* @param {Deno.PermissionDescriptor} desc
|
const permissionNames = [
|
||||||
* @returns {Deno.PermissionState}
|
"read",
|
||||||
*/
|
"write",
|
||||||
function opQuery(desc) {
|
"net",
|
||||||
return ops.op_query_permission(desc);
|
"env",
|
||||||
|
"sys",
|
||||||
|
"run",
|
||||||
|
"ffi",
|
||||||
|
"hrtime",
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Deno.PermissionDescriptor} desc
|
||||||
|
* @returns {Deno.PermissionState}
|
||||||
|
*/
|
||||||
|
function opQuery(desc) {
|
||||||
|
return ops.op_query_permission(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Deno.PermissionDescriptor} desc
|
||||||
|
* @returns {Deno.PermissionState}
|
||||||
|
*/
|
||||||
|
function opRevoke(desc) {
|
||||||
|
return ops.op_revoke_permission(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Deno.PermissionDescriptor} desc
|
||||||
|
* @returns {Deno.PermissionState}
|
||||||
|
*/
|
||||||
|
function opRequest(desc) {
|
||||||
|
return ops.op_request_permission(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
class PermissionStatus extends EventTarget {
|
||||||
|
/** @type {{ state: Deno.PermissionState }} */
|
||||||
|
#state;
|
||||||
|
|
||||||
|
/** @type {((this: PermissionStatus, event: Event) => any) | null} */
|
||||||
|
onchange = null;
|
||||||
|
|
||||||
|
/** @returns {Deno.PermissionState} */
|
||||||
|
get state() {
|
||||||
|
return this.#state.state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Deno.PermissionDescriptor} desc
|
* @param {{ state: Deno.PermissionState }} state
|
||||||
* @returns {Deno.PermissionState}
|
* @param {unknown} key
|
||||||
*/
|
*/
|
||||||
function opRevoke(desc) {
|
constructor(state = null, key = null) {
|
||||||
return ops.op_revoke_permission(desc);
|
if (key != illegalConstructorKey) {
|
||||||
|
throw new TypeError("Illegal constructor.");
|
||||||
|
}
|
||||||
|
super();
|
||||||
|
this.#state = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Deno.PermissionDescriptor} desc
|
* @param {Event} event
|
||||||
* @returns {Deno.PermissionState}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
function opRequest(desc) {
|
dispatchEvent(event) {
|
||||||
return ops.op_request_permission(desc);
|
let dispatched = super.dispatchEvent(event);
|
||||||
|
if (dispatched && this.onchange) {
|
||||||
|
FunctionPrototypeCall(this.onchange, this, event);
|
||||||
|
dispatched = !event.defaultPrevented;
|
||||||
|
}
|
||||||
|
return dispatched;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PermissionStatus extends EventTarget {
|
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
||||||
/** @type {{ state: Deno.PermissionState }} */
|
return `${this.constructor.name} ${
|
||||||
#state;
|
inspect({ state: this.state, onchange: this.onchange })
|
||||||
|
}`;
|
||||||
/** @type {((this: PermissionStatus, event: Event) => any) | null} */
|
|
||||||
onchange = null;
|
|
||||||
|
|
||||||
/** @returns {Deno.PermissionState} */
|
|
||||||
get state() {
|
|
||||||
return this.#state.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {{ state: Deno.PermissionState }} state
|
|
||||||
* @param {unknown} key
|
|
||||||
*/
|
|
||||||
constructor(state = null, key = null) {
|
|
||||||
if (key != illegalConstructorKey) {
|
|
||||||
throw new TypeError("Illegal constructor.");
|
|
||||||
}
|
|
||||||
super();
|
|
||||||
this.#state = state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Event} event
|
|
||||||
* @returns {boolean}
|
|
||||||
*/
|
|
||||||
dispatchEvent(event) {
|
|
||||||
let dispatched = super.dispatchEvent(event);
|
|
||||||
if (dispatched && this.onchange) {
|
|
||||||
FunctionPrototypeCall(this.onchange, this, event);
|
|
||||||
dispatched = !event.defaultPrevented;
|
|
||||||
}
|
|
||||||
return dispatched;
|
|
||||||
}
|
|
||||||
|
|
||||||
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
|
||||||
return `${this.constructor.name} ${
|
|
||||||
inspect({ state: this.state, onchange: this.onchange })
|
|
||||||
}`;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {Map<string, StatusCacheValue>} */
|
/** @type {Map<string, StatusCacheValue>} */
|
||||||
const statusCache = new Map();
|
const statusCache = new Map();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {Deno.PermissionDescriptor} desc
|
* @param {Deno.PermissionDescriptor} desc
|
||||||
* @param {Deno.PermissionState} state
|
* @param {Deno.PermissionState} state
|
||||||
* @returns {PermissionStatus}
|
* @returns {PermissionStatus}
|
||||||
*/
|
*/
|
||||||
function cache(desc, state) {
|
function cache(desc, state) {
|
||||||
let { name: key } = desc;
|
let { name: key } = desc;
|
||||||
if (
|
if (
|
||||||
(desc.name === "read" || desc.name === "write" || desc.name === "ffi") &&
|
(desc.name === "read" || desc.name === "write" || desc.name === "ffi") &&
|
||||||
ReflectHas(desc, "path")
|
ReflectHas(desc, "path")
|
||||||
) {
|
) {
|
||||||
key += `-${desc.path}&`;
|
key += `-${desc.path}&`;
|
||||||
} else if (desc.name === "net" && desc.host) {
|
} else if (desc.name === "net" && desc.host) {
|
||||||
key += `-${desc.host}&`;
|
key += `-${desc.host}&`;
|
||||||
} else if (desc.name === "run" && desc.command) {
|
} else if (desc.name === "run" && desc.command) {
|
||||||
key += `-${desc.command}&`;
|
key += `-${desc.command}&`;
|
||||||
} else if (desc.name === "env" && desc.variable) {
|
} else if (desc.name === "env" && desc.variable) {
|
||||||
key += `-${desc.variable}&`;
|
key += `-${desc.variable}&`;
|
||||||
} else if (desc.name === "sys" && desc.kind) {
|
} else if (desc.name === "sys" && desc.kind) {
|
||||||
key += `-${desc.kind}&`;
|
key += `-${desc.kind}&`;
|
||||||
} else {
|
} else {
|
||||||
key += "$";
|
key += "$";
|
||||||
|
}
|
||||||
|
if (MapPrototypeHas(statusCache, key)) {
|
||||||
|
const status = MapPrototypeGet(statusCache, key);
|
||||||
|
if (status.state !== state) {
|
||||||
|
status.state = state;
|
||||||
|
status.status.dispatchEvent(new Event("change", { cancelable: false }));
|
||||||
}
|
}
|
||||||
if (MapPrototypeHas(statusCache, key)) {
|
|
||||||
const status = MapPrototypeGet(statusCache, key);
|
|
||||||
if (status.state !== state) {
|
|
||||||
status.state = state;
|
|
||||||
status.status.dispatchEvent(new Event("change", { cancelable: false }));
|
|
||||||
}
|
|
||||||
return status.status;
|
|
||||||
}
|
|
||||||
/** @type {{ state: Deno.PermissionState; status?: PermissionStatus }} */
|
|
||||||
const status = { state };
|
|
||||||
status.status = new PermissionStatus(status, illegalConstructorKey);
|
|
||||||
MapPrototypeSet(statusCache, key, status);
|
|
||||||
return status.status;
|
return status.status;
|
||||||
}
|
}
|
||||||
|
/** @type {{ state: Deno.PermissionState; status?: PermissionStatus }} */
|
||||||
|
const status = { state };
|
||||||
|
status.status = new PermissionStatus(status, illegalConstructorKey);
|
||||||
|
MapPrototypeSet(statusCache, key, status);
|
||||||
|
return status.status;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {unknown} desc
|
* @param {unknown} desc
|
||||||
* @returns {desc is Deno.PermissionDescriptor}
|
* @returns {desc is Deno.PermissionDescriptor}
|
||||||
*/
|
*/
|
||||||
function isValidDescriptor(desc) {
|
function isValidDescriptor(desc) {
|
||||||
return typeof desc === "object" && desc !== null &&
|
return typeof desc === "object" && desc !== null &&
|
||||||
ArrayPrototypeIncludes(permissionNames, desc.name);
|
ArrayPrototypeIncludes(permissionNames, desc.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Deno.PermissionDescriptor} desc
|
||||||
|
* @returns {desc is Deno.PermissionDescriptor}
|
||||||
|
*/
|
||||||
|
function formDescriptor(desc) {
|
||||||
|
if (
|
||||||
|
desc.name === "read" || desc.name === "write" || desc.name === "ffi"
|
||||||
|
) {
|
||||||
|
desc.path = pathFromURL(desc.path);
|
||||||
|
} else if (desc.name === "run") {
|
||||||
|
desc.command = pathFromURL(desc.command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Permissions {
|
||||||
|
constructor(key = null) {
|
||||||
|
if (key != illegalConstructorKey) {
|
||||||
|
throw new TypeError("Illegal constructor.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
query(desc) {
|
||||||
* @param {Deno.PermissionDescriptor} desc
|
try {
|
||||||
* @returns {desc is Deno.PermissionDescriptor}
|
return PromiseResolve(this.querySync(desc));
|
||||||
*/
|
} catch (error) {
|
||||||
function formDescriptor(desc) {
|
return PromiseReject(error);
|
||||||
if (
|
}
|
||||||
desc.name === "read" || desc.name === "write" || desc.name === "ffi"
|
}
|
||||||
|
|
||||||
|
querySync(desc) {
|
||||||
|
if (!isValidDescriptor(desc)) {
|
||||||
|
throw new TypeError(
|
||||||
|
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
formDescriptor(desc);
|
||||||
|
|
||||||
|
const state = opQuery(desc);
|
||||||
|
return cache(desc, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
revoke(desc) {
|
||||||
|
try {
|
||||||
|
return PromiseResolve(this.revokeSync(desc));
|
||||||
|
} catch (error) {
|
||||||
|
return PromiseReject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
revokeSync(desc) {
|
||||||
|
if (!isValidDescriptor(desc)) {
|
||||||
|
throw new TypeError(
|
||||||
|
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
formDescriptor(desc);
|
||||||
|
|
||||||
|
const state = opRevoke(desc);
|
||||||
|
return cache(desc, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
request(desc) {
|
||||||
|
try {
|
||||||
|
return PromiseResolve(this.requestSync(desc));
|
||||||
|
} catch (error) {
|
||||||
|
return PromiseReject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
requestSync(desc) {
|
||||||
|
if (!isValidDescriptor(desc)) {
|
||||||
|
throw new TypeError(
|
||||||
|
`The provided value "${desc?.name}" is not a valid permission name.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
formDescriptor(desc);
|
||||||
|
|
||||||
|
const state = opRequest(desc);
|
||||||
|
return cache(desc, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const permissions = new Permissions(illegalConstructorKey);
|
||||||
|
|
||||||
|
/** Converts all file URLs in FS allowlists to paths. */
|
||||||
|
function serializePermissions(permissions) {
|
||||||
|
if (typeof permissions == "object" && permissions != null) {
|
||||||
|
const serializedPermissions = {};
|
||||||
|
for (
|
||||||
|
const key of new SafeArrayIterator(["read", "write", "run", "ffi"])
|
||||||
) {
|
) {
|
||||||
desc.path = pathFromURL(desc.path);
|
if (ArrayIsArray(permissions[key])) {
|
||||||
} else if (desc.name === "run") {
|
serializedPermissions[key] = ArrayPrototypeMap(
|
||||||
desc.command = pathFromURL(desc.command);
|
permissions[key],
|
||||||
}
|
(path) => pathFromURL(path),
|
||||||
}
|
|
||||||
|
|
||||||
class Permissions {
|
|
||||||
constructor(key = null) {
|
|
||||||
if (key != illegalConstructorKey) {
|
|
||||||
throw new TypeError("Illegal constructor.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
query(desc) {
|
|
||||||
try {
|
|
||||||
return PromiseResolve(this.querySync(desc));
|
|
||||||
} catch (error) {
|
|
||||||
return PromiseReject(error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
querySync(desc) {
|
|
||||||
if (!isValidDescriptor(desc)) {
|
|
||||||
throw new TypeError(
|
|
||||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
|
serializedPermissions[key] = permissions[key];
|
||||||
formDescriptor(desc);
|
|
||||||
|
|
||||||
const state = opQuery(desc);
|
|
||||||
return cache(desc, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
revoke(desc) {
|
|
||||||
try {
|
|
||||||
return PromiseResolve(this.revokeSync(desc));
|
|
||||||
} catch (error) {
|
|
||||||
return PromiseReject(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (
|
||||||
revokeSync(desc) {
|
const key of new SafeArrayIterator(["env", "hrtime", "net", "sys"])
|
||||||
if (!isValidDescriptor(desc)) {
|
) {
|
||||||
throw new TypeError(
|
if (ArrayIsArray(permissions[key])) {
|
||||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
serializedPermissions[key] = ArrayPrototypeSlice(permissions[key]);
|
||||||
);
|
} else {
|
||||||
}
|
serializedPermissions[key] = permissions[key];
|
||||||
|
|
||||||
formDescriptor(desc);
|
|
||||||
|
|
||||||
const state = opRevoke(desc);
|
|
||||||
return cache(desc, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
request(desc) {
|
|
||||||
try {
|
|
||||||
return PromiseResolve(this.requestSync(desc));
|
|
||||||
} catch (error) {
|
|
||||||
return PromiseReject(error);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return serializedPermissions;
|
||||||
requestSync(desc) {
|
|
||||||
if (!isValidDescriptor(desc)) {
|
|
||||||
throw new TypeError(
|
|
||||||
`The provided value "${desc?.name}" is not a valid permission name.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
formDescriptor(desc);
|
|
||||||
|
|
||||||
const state = opRequest(desc);
|
|
||||||
return cache(desc, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return permissions;
|
||||||
|
}
|
||||||
|
|
||||||
const permissions = new Permissions(illegalConstructorKey);
|
export { Permissions, permissions, PermissionStatus, serializePermissions };
|
||||||
|
|
||||||
/** Converts all file URLs in FS allowlists to paths. */
|
|
||||||
function serializePermissions(permissions) {
|
|
||||||
if (typeof permissions == "object" && permissions != null) {
|
|
||||||
const serializedPermissions = {};
|
|
||||||
for (
|
|
||||||
const key of new SafeArrayIterator(["read", "write", "run", "ffi"])
|
|
||||||
) {
|
|
||||||
if (ArrayIsArray(permissions[key])) {
|
|
||||||
serializedPermissions[key] = ArrayPrototypeMap(
|
|
||||||
permissions[key],
|
|
||||||
(path) => pathFromURL(path),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
serializedPermissions[key] = permissions[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (
|
|
||||||
const key of new SafeArrayIterator(["env", "hrtime", "net", "sys"])
|
|
||||||
) {
|
|
||||||
if (ArrayIsArray(permissions[key])) {
|
|
||||||
serializedPermissions[key] = ArrayPrototypeSlice(permissions[key]);
|
|
||||||
} else {
|
|
||||||
serializedPermissions[key] = permissions[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return serializedPermissions;
|
|
||||||
}
|
|
||||||
return permissions;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.__bootstrap.permissions = {
|
|
||||||
serializePermissions,
|
|
||||||
permissions,
|
|
||||||
Permissions,
|
|
||||||
PermissionStatus,
|
|
||||||
};
|
|
||||||
})(this);
|
|
||||||
|
|
|
@ -1,254 +1,253 @@
|
||||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||||
"use strict";
|
|
||||||
|
|
||||||
((window) => {
|
const core = globalThis.Deno.core;
|
||||||
const core = window.Deno.core;
|
const ops = core.ops;
|
||||||
const ops = core.ops;
|
const primordials = globalThis.__bootstrap.primordials;
|
||||||
const {
|
const {
|
||||||
Error,
|
Error,
|
||||||
ObjectPrototypeIsPrototypeOf,
|
ObjectPrototypeIsPrototypeOf,
|
||||||
StringPrototypeStartsWith,
|
StringPrototypeStartsWith,
|
||||||
String,
|
String,
|
||||||
SymbolIterator,
|
SymbolIterator,
|
||||||
SymbolToStringTag,
|
SymbolToStringTag,
|
||||||
} = window.__bootstrap.primordials;
|
} = primordials;
|
||||||
const webidl = window.__bootstrap.webidl;
|
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||||
const { URL } = window.__bootstrap.url;
|
import { URL } from "internal:ext/url/00_url.js";
|
||||||
const { getLocationHref } = window.__bootstrap.location;
|
import { getLocationHref } from "internal:ext/web/12_location.js";
|
||||||
const { serializePermissions } = window.__bootstrap.permissions;
|
import { serializePermissions } from "internal:runtime/js/10_permissions.js";
|
||||||
const { log } = window.__bootstrap.util;
|
import { log } from "internal:runtime/js/06_util.js";
|
||||||
const { ErrorEvent, MessageEvent, defineEventHandler } =
|
import {
|
||||||
window.__bootstrap.event;
|
defineEventHandler,
|
||||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
ErrorEvent,
|
||||||
const {
|
EventTarget,
|
||||||
deserializeJsMessageData,
|
MessageEvent,
|
||||||
serializeJsMessageData,
|
} from "internal:ext/web/02_event.js";
|
||||||
MessagePortPrototype,
|
import {
|
||||||
} = window.__bootstrap.messagePort;
|
deserializeJsMessageData,
|
||||||
|
MessagePortPrototype,
|
||||||
|
serializeJsMessageData,
|
||||||
|
} from "internal:ext/web/13_message_port.js";
|
||||||
|
|
||||||
function createWorker(
|
function createWorker(
|
||||||
specifier,
|
specifier,
|
||||||
|
hasSourceCode,
|
||||||
|
sourceCode,
|
||||||
|
permissions,
|
||||||
|
name,
|
||||||
|
workerType,
|
||||||
|
) {
|
||||||
|
return ops.op_create_worker({
|
||||||
hasSourceCode,
|
hasSourceCode,
|
||||||
sourceCode,
|
|
||||||
permissions,
|
|
||||||
name,
|
name,
|
||||||
|
permissions: serializePermissions(permissions),
|
||||||
|
sourceCode,
|
||||||
|
specifier,
|
||||||
workerType,
|
workerType,
|
||||||
) {
|
});
|
||||||
return ops.op_create_worker({
|
}
|
||||||
hasSourceCode,
|
|
||||||
|
function hostTerminateWorker(id) {
|
||||||
|
ops.op_host_terminate_worker(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hostPostMessage(id, data) {
|
||||||
|
ops.op_host_post_message(id, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hostRecvCtrl(id) {
|
||||||
|
return core.opAsync("op_host_recv_ctrl", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hostRecvMessage(id) {
|
||||||
|
return core.opAsync("op_host_recv_message", id);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Worker extends EventTarget {
|
||||||
|
#id = 0;
|
||||||
|
#name = "";
|
||||||
|
|
||||||
|
// "RUNNING" | "CLOSED" | "TERMINATED"
|
||||||
|
// "TERMINATED" means that any controls or messages received will be
|
||||||
|
// discarded. "CLOSED" means that we have received a control
|
||||||
|
// indicating that the worker is no longer running, but there might
|
||||||
|
// still be messages left to receive.
|
||||||
|
#status = "RUNNING";
|
||||||
|
|
||||||
|
constructor(specifier, options = {}) {
|
||||||
|
super();
|
||||||
|
specifier = String(specifier);
|
||||||
|
const {
|
||||||
|
deno,
|
||||||
name,
|
name,
|
||||||
permissions: serializePermissions(permissions),
|
type = "classic",
|
||||||
sourceCode,
|
} = options;
|
||||||
|
|
||||||
|
const workerType = webidl.converters["WorkerType"](type);
|
||||||
|
|
||||||
|
if (
|
||||||
|
StringPrototypeStartsWith(specifier, "./") ||
|
||||||
|
StringPrototypeStartsWith(specifier, "../") ||
|
||||||
|
StringPrototypeStartsWith(specifier, "/") || workerType === "classic"
|
||||||
|
) {
|
||||||
|
const baseUrl = getLocationHref();
|
||||||
|
if (baseUrl != null) {
|
||||||
|
specifier = new URL(specifier, baseUrl).href;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#name = name;
|
||||||
|
let hasSourceCode, sourceCode;
|
||||||
|
if (workerType === "classic") {
|
||||||
|
hasSourceCode = true;
|
||||||
|
sourceCode = `importScripts("#");`;
|
||||||
|
} else {
|
||||||
|
hasSourceCode = false;
|
||||||
|
sourceCode = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const id = createWorker(
|
||||||
specifier,
|
specifier,
|
||||||
|
hasSourceCode,
|
||||||
|
sourceCode,
|
||||||
|
deno?.permissions,
|
||||||
|
name,
|
||||||
workerType,
|
workerType,
|
||||||
|
);
|
||||||
|
this.#id = id;
|
||||||
|
this.#pollControl();
|
||||||
|
this.#pollMessages();
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleError(e) {
|
||||||
|
const event = new ErrorEvent("error", {
|
||||||
|
cancelable: true,
|
||||||
|
message: e.message,
|
||||||
|
lineno: e.lineNumber ? e.lineNumber : undefined,
|
||||||
|
colno: e.columnNumber ? e.columnNumber : undefined,
|
||||||
|
filename: e.fileName,
|
||||||
|
error: null,
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
function hostTerminateWorker(id) {
|
this.dispatchEvent(event);
|
||||||
ops.op_host_terminate_worker(id);
|
// Don't bubble error event to window for loader errors (`!e.fileName`).
|
||||||
}
|
// TODO(nayeemrmn): It's not correct to use `e.fileName` to detect user
|
||||||
|
// errors. It won't be there for non-awaited async ops for example.
|
||||||
function hostPostMessage(id, data) {
|
if (e.fileName && !event.defaultPrevented) {
|
||||||
ops.op_host_post_message(id, data);
|
globalThis.dispatchEvent(event);
|
||||||
}
|
|
||||||
|
|
||||||
function hostRecvCtrl(id) {
|
|
||||||
return core.opAsync("op_host_recv_ctrl", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
function hostRecvMessage(id) {
|
|
||||||
return core.opAsync("op_host_recv_message", id);
|
|
||||||
}
|
|
||||||
|
|
||||||
class Worker extends EventTarget {
|
|
||||||
#id = 0;
|
|
||||||
#name = "";
|
|
||||||
|
|
||||||
// "RUNNING" | "CLOSED" | "TERMINATED"
|
|
||||||
// "TERMINATED" means that any controls or messages received will be
|
|
||||||
// discarded. "CLOSED" means that we have received a control
|
|
||||||
// indicating that the worker is no longer running, but there might
|
|
||||||
// still be messages left to receive.
|
|
||||||
#status = "RUNNING";
|
|
||||||
|
|
||||||
constructor(specifier, options = {}) {
|
|
||||||
super();
|
|
||||||
specifier = String(specifier);
|
|
||||||
const {
|
|
||||||
deno,
|
|
||||||
name,
|
|
||||||
type = "classic",
|
|
||||||
} = options;
|
|
||||||
|
|
||||||
const workerType = webidl.converters["WorkerType"](type);
|
|
||||||
|
|
||||||
if (
|
|
||||||
StringPrototypeStartsWith(specifier, "./") ||
|
|
||||||
StringPrototypeStartsWith(specifier, "../") ||
|
|
||||||
StringPrototypeStartsWith(specifier, "/") || workerType === "classic"
|
|
||||||
) {
|
|
||||||
const baseUrl = getLocationHref();
|
|
||||||
if (baseUrl != null) {
|
|
||||||
specifier = new URL(specifier, baseUrl).href;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#name = name;
|
|
||||||
let hasSourceCode, sourceCode;
|
|
||||||
if (workerType === "classic") {
|
|
||||||
hasSourceCode = true;
|
|
||||||
sourceCode = `importScripts("#");`;
|
|
||||||
} else {
|
|
||||||
hasSourceCode = false;
|
|
||||||
sourceCode = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = createWorker(
|
|
||||||
specifier,
|
|
||||||
hasSourceCode,
|
|
||||||
sourceCode,
|
|
||||||
deno?.permissions,
|
|
||||||
name,
|
|
||||||
workerType,
|
|
||||||
);
|
|
||||||
this.#id = id;
|
|
||||||
this.#pollControl();
|
|
||||||
this.#pollMessages();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleError(e) {
|
return event.defaultPrevented;
|
||||||
const event = new ErrorEvent("error", {
|
}
|
||||||
cancelable: true,
|
|
||||||
message: e.message,
|
|
||||||
lineno: e.lineNumber ? e.lineNumber : undefined,
|
|
||||||
colno: e.columnNumber ? e.columnNumber : undefined,
|
|
||||||
filename: e.fileName,
|
|
||||||
error: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
this.dispatchEvent(event);
|
#pollControl = async () => {
|
||||||
// Don't bubble error event to window for loader errors (`!e.fileName`).
|
while (this.#status === "RUNNING") {
|
||||||
// TODO(nayeemrmn): It's not correct to use `e.fileName` to detect user
|
const { 0: type, 1: data } = await hostRecvCtrl(this.#id);
|
||||||
// errors. It won't be there for non-awaited async ops for example.
|
|
||||||
if (e.fileName && !event.defaultPrevented) {
|
// If terminate was called then we ignore all messages
|
||||||
window.dispatchEvent(event);
|
if (this.#status === "TERMINATED") {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return event.defaultPrevented;
|
switch (type) {
|
||||||
|
case 1: { // TerminalError
|
||||||
|
this.#status = "CLOSED";
|
||||||
|
} /* falls through */
|
||||||
|
case 2: { // Error
|
||||||
|
if (!this.#handleError(data)) {
|
||||||
|
throw new Error("Unhandled error in child worker.");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 3: { // Close
|
||||||
|
log(`Host got "close" message from worker: ${this.#name}`);
|
||||||
|
this.#status = "CLOSED";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
throw new Error(`Unknown worker event: "${type}"`);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#pollControl = async () => {
|
#pollMessages = async () => {
|
||||||
while (this.#status === "RUNNING") {
|
while (this.#status !== "TERMINATED") {
|
||||||
const { 0: type, 1: data } = await hostRecvCtrl(this.#id);
|
const data = await hostRecvMessage(this.#id);
|
||||||
|
if (this.#status === "TERMINATED" || data === null) {
|
||||||
// If terminate was called then we ignore all messages
|
return;
|
||||||
if (this.#status === "TERMINATED") {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case 1: { // TerminalError
|
|
||||||
this.#status = "CLOSED";
|
|
||||||
} /* falls through */
|
|
||||||
case 2: { // Error
|
|
||||||
if (!this.#handleError(data)) {
|
|
||||||
throw new Error("Unhandled error in child worker.");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case 3: { // Close
|
|
||||||
log(`Host got "close" message from worker: ${this.#name}`);
|
|
||||||
this.#status = "CLOSED";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
throw new Error(`Unknown worker event: "${type}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
let message, transferables;
|
||||||
|
try {
|
||||||
#pollMessages = async () => {
|
const v = deserializeJsMessageData(data);
|
||||||
while (this.#status !== "TERMINATED") {
|
message = v[0];
|
||||||
const data = await hostRecvMessage(this.#id);
|
transferables = v[1];
|
||||||
if (this.#status === "TERMINATED" || data === null) {
|
} catch (err) {
|
||||||
return;
|
const event = new MessageEvent("messageerror", {
|
||||||
}
|
|
||||||
let message, transferables;
|
|
||||||
try {
|
|
||||||
const v = deserializeJsMessageData(data);
|
|
||||||
message = v[0];
|
|
||||||
transferables = v[1];
|
|
||||||
} catch (err) {
|
|
||||||
const event = new MessageEvent("messageerror", {
|
|
||||||
cancelable: false,
|
|
||||||
data: err,
|
|
||||||
});
|
|
||||||
this.dispatchEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const event = new MessageEvent("message", {
|
|
||||||
cancelable: false,
|
cancelable: false,
|
||||||
data: message,
|
data: err,
|
||||||
ports: transferables.filter((t) =>
|
|
||||||
ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t)
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
this.dispatchEvent(event);
|
this.dispatchEvent(event);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
const event = new MessageEvent("message", {
|
||||||
|
cancelable: false,
|
||||||
postMessage(message, transferOrOptions = {}) {
|
data: message,
|
||||||
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
|
ports: transferables.filter((t) =>
|
||||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t)
|
||||||
message = webidl.converters.any(message);
|
),
|
||||||
let options;
|
});
|
||||||
if (
|
this.dispatchEvent(event);
|
||||||
webidl.type(transferOrOptions) === "Object" &&
|
|
||||||
transferOrOptions !== undefined &&
|
|
||||||
transferOrOptions[SymbolIterator] !== undefined
|
|
||||||
) {
|
|
||||||
const transfer = webidl.converters["sequence<object>"](
|
|
||||||
transferOrOptions,
|
|
||||||
{ prefix, context: "Argument 2" },
|
|
||||||
);
|
|
||||||
options = { transfer };
|
|
||||||
} else {
|
|
||||||
options = webidl.converters.StructuredSerializeOptions(
|
|
||||||
transferOrOptions,
|
|
||||||
{
|
|
||||||
prefix,
|
|
||||||
context: "Argument 2",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
const { transfer } = options;
|
|
||||||
const data = serializeJsMessageData(message, transfer);
|
|
||||||
if (this.#status === "RUNNING") {
|
|
||||||
hostPostMessage(this.#id, data);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
terminate() {
|
postMessage(message, transferOrOptions = {}) {
|
||||||
if (this.#status !== "TERMINATED") {
|
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
|
||||||
this.#status = "TERMINATED";
|
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||||
hostTerminateWorker(this.#id);
|
message = webidl.converters.any(message);
|
||||||
}
|
let options;
|
||||||
|
if (
|
||||||
|
webidl.type(transferOrOptions) === "Object" &&
|
||||||
|
transferOrOptions !== undefined &&
|
||||||
|
transferOrOptions[SymbolIterator] !== undefined
|
||||||
|
) {
|
||||||
|
const transfer = webidl.converters["sequence<object>"](
|
||||||
|
transferOrOptions,
|
||||||
|
{ prefix, context: "Argument 2" },
|
||||||
|
);
|
||||||
|
options = { transfer };
|
||||||
|
} else {
|
||||||
|
options = webidl.converters.StructuredSerializeOptions(
|
||||||
|
transferOrOptions,
|
||||||
|
{
|
||||||
|
prefix,
|
||||||
|
context: "Argument 2",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { transfer } = options;
|
||||||
|
const data = serializeJsMessageData(message, transfer);
|
||||||
|
if (this.#status === "RUNNING") {
|
||||||
|
hostPostMessage(this.#id, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
[SymbolToStringTag] = "Worker";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
defineEventHandler(Worker.prototype, "error");
|
terminate() {
|
||||||
defineEventHandler(Worker.prototype, "message");
|
if (this.#status !== "TERMINATED") {
|
||||||
defineEventHandler(Worker.prototype, "messageerror");
|
this.#status = "TERMINATED";
|
||||||
|
hostTerminateWorker(this.#id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
|
[SymbolToStringTag] = "Worker";
|
||||||
"classic",
|
}
|
||||||
"module",
|
|
||||||
]);
|
|
||||||
|
|
||||||
window.__bootstrap.worker = {
|
defineEventHandler(Worker.prototype, "error");
|
||||||
Worker,
|
defineEventHandler(Worker.prototype, "message");
|
||||||
};
|
defineEventHandler(Worker.prototype, "messageerror");
|
||||||
})(this);
|
|
||||||
|
webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
|
||||||
|
"classic",
|
||||||
|
"module",
|
||||||
|
]);
|
||||||
|
|
||||||
|
export { Worker };
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue