mirror of
https://github.com/denoland/deno.git
synced 2025-01-11 08:33:43 -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 {
|
||||
JsRuntime::new(RuntimeOptions {
|
||||
extensions_with_js: setup(),
|
||||
module_loader: Some(std::rc::Rc::new(
|
||||
deno_core::InternalModuleLoader::new(None),
|
||||
)),
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
|
|
14
cli/build.rs
14
cli/build.rs
|
@ -275,6 +275,7 @@ mod ts {
|
|||
.build()],
|
||||
extensions_with_js: vec![],
|
||||
additional_files: files,
|
||||
additional_esm_files: vec![],
|
||||
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
||||
vec.extend_from_slice(
|
||||
&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![
|
||||
deno_webidl::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()),
|
||||
extensions,
|
||||
extensions_with_js: vec![],
|
||||
additional_files: files,
|
||||
additional_files: vec![],
|
||||
additional_esm_files: esm_files,
|
||||
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
||||
lzzzz::lz4_hc::compress_to_vec(
|
||||
snapshot_slice,
|
||||
|
@ -448,13 +450,13 @@ fn main() {
|
|||
let o = PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
||||
|
||||
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);
|
||||
|
||||
let cli_snapshot_path = o.join("CLI_SNAPSHOT.bin");
|
||||
let mut js_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js");
|
||||
js_files.push(deno_runtime::js::get_99_main());
|
||||
create_cli_snapshot(cli_snapshot_path, js_files);
|
||||
let mut esm_files = get_js_files(env!("CARGO_MANIFEST_DIR"), "js", None);
|
||||
esm_files.push(deno_runtime::js::get_99_main());
|
||||
create_cli_snapshot(cli_snapshot_path, esm_files);
|
||||
|
||||
#[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(),
|
||||
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
|
||||
queueMicrotask
|
||||
error: Uncaught 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 });
|
||||
const internals = {};
|
||||
ObjectAssign(globalThis.__bootstrap, { internals });
|
||||
ObjectAssign(globalThis.Deno, { core });
|
||||
|
||||
// Direct bindings on `globalThis`
|
||||
|
|
|
@ -267,45 +267,47 @@ pub fn host_import_module_dynamically_callback<'s>(
|
|||
.unwrap()
|
||||
.to_rust_string_lossy(scope);
|
||||
|
||||
let is_internal_module = specifier_str.starts_with("internal:");
|
||||
let resolver = v8::PromiseResolver::new(scope).unwrap();
|
||||
let promise = resolver.get_promise(scope);
|
||||
|
||||
let assertions = parse_import_assertions(
|
||||
scope,
|
||||
import_assertions,
|
||||
ImportAssertionsKind::DynamicImport,
|
||||
);
|
||||
if !is_internal_module {
|
||||
let assertions = parse_import_assertions(
|
||||
scope,
|
||||
import_assertions,
|
||||
ImportAssertionsKind::DynamicImport,
|
||||
);
|
||||
|
||||
{
|
||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||
validate_import_assertions(tc_scope, &assertions);
|
||||
if tc_scope.has_caught() {
|
||||
let e = tc_scope.exception().unwrap();
|
||||
resolver.reject(tc_scope, e);
|
||||
{
|
||||
let tc_scope = &mut v8::TryCatch::new(scope);
|
||||
validate_import_assertions(tc_scope, &assertions);
|
||||
if tc_scope.has_caught() {
|
||||
let e = tc_scope.exception().unwrap();
|
||||
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
|
||||
// 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
|
||||
|
@ -317,6 +319,14 @@ pub fn host_import_module_dynamically_callback<'s>(
|
|||
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,7 @@ impl OpDecl {
|
|||
#[derive(Default)]
|
||||
pub struct Extension {
|
||||
js_files: Option<Vec<SourcePair>>,
|
||||
esm_files: Option<Vec<SourcePair>>,
|
||||
ops: Option<Vec<OpDecl>>,
|
||||
opstate_fn: Option<Box<OpStateFn>>,
|
||||
middleware_fn: Option<Box<OpMiddlewareFn>>,
|
||||
|
@ -81,13 +82,20 @@ impl Extension {
|
|||
|
||||
/// 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.
|
||||
pub fn init_js(&self) -> &[SourcePair] {
|
||||
pub fn get_js_sources(&self) -> &[SourcePair] {
|
||||
match &self.js_files {
|
||||
Some(files) => files,
|
||||
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.
|
||||
pub fn init_ops(&mut self) -> Option<Vec<OpDecl>> {
|
||||
// TODO(@AaronO): maybe make op registration idempotent
|
||||
|
@ -145,6 +153,7 @@ impl Extension {
|
|||
#[derive(Default)]
|
||||
pub struct ExtensionBuilder {
|
||||
js: Vec<SourcePair>,
|
||||
esm: Vec<SourcePair>,
|
||||
ops: Vec<OpDecl>,
|
||||
state: Option<Box<OpStateFn>>,
|
||||
middleware: Option<Box<OpMiddlewareFn>>,
|
||||
|
@ -164,6 +173,11 @@ impl ExtensionBuilder {
|
|||
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 {
|
||||
self.ops.extend(ops);
|
||||
self
|
||||
|
@ -195,10 +209,12 @@ impl ExtensionBuilder {
|
|||
|
||||
pub fn build(&mut self) -> Extension {
|
||||
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 deps = Some(std::mem::take(&mut self.deps));
|
||||
Extension {
|
||||
js_files,
|
||||
esm_files,
|
||||
ops,
|
||||
opstate_fn: self.state.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::DUMMY_SPECIFIER;
|
||||
pub use crate::modules::FsModuleLoader;
|
||||
pub use crate::modules::InternalModuleLoader;
|
||||
pub use crate::modules::ModuleId;
|
||||
pub use crate::modules::ModuleLoader;
|
||||
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.
|
||||
///
|
||||
/// Note that this loader will **block** event loop
|
||||
|
@ -2508,4 +2571,33 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
|
|||
)
|
||||
.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::ModuleLoader;
|
||||
use crate::modules::ModuleMap;
|
||||
use crate::modules::NoopModuleLoader;
|
||||
use crate::op_void_async;
|
||||
use crate::op_void_sync;
|
||||
use crate::ops::*;
|
||||
use crate::source_map::SourceMapCache;
|
||||
use crate::source_map::SourceMapGetter;
|
||||
use crate::Extension;
|
||||
use crate::NoopModuleLoader;
|
||||
use crate::OpMiddlewareFn;
|
||||
use crate::OpResult;
|
||||
use crate::OpState;
|
||||
use crate::PromiseId;
|
||||
use anyhow::Context as AnyhowContext;
|
||||
use anyhow::Error;
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::poll_fn;
|
||||
|
@ -605,9 +606,16 @@ impl JsRuntime {
|
|||
None
|
||||
};
|
||||
|
||||
let loader = options
|
||||
.module_loader
|
||||
.unwrap_or_else(|| Rc::new(NoopModuleLoader));
|
||||
let loader = if snapshot_options != SnapshotOptions::Load {
|
||||
Rc::new(crate::modules::InternalModuleLoader::new(
|
||||
options.module_loader,
|
||||
))
|
||||
} else {
|
||||
options
|
||||
.module_loader
|
||||
.unwrap_or_else(|| Rc::new(NoopModuleLoader))
|
||||
};
|
||||
|
||||
{
|
||||
let mut state = state_rc.borrow_mut();
|
||||
state.global_realm = Some(JsRealm(global_context.clone()));
|
||||
|
@ -805,10 +813,30 @@ impl JsRuntime {
|
|||
// Take extensions to avoid double-borrow
|
||||
let extensions = std::mem::take(&mut self.extensions_with_js);
|
||||
for ext in &extensions {
|
||||
let js_files = ext.init_js();
|
||||
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)?;
|
||||
{
|
||||
let js_files = ext.get_esm_sources();
|
||||
for (filename, source) in js_files {
|
||||
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
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use anyhow::Context;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::Extension;
|
||||
use crate::JsRuntime;
|
||||
use crate::ModuleSpecifier;
|
||||
use crate::RuntimeOptions;
|
||||
use crate::Snapshot;
|
||||
|
||||
|
@ -17,6 +19,7 @@ pub struct CreateSnapshotOptions {
|
|||
pub extensions: Vec<Extension>,
|
||||
pub extensions_with_js: Vec<Extension>,
|
||||
pub additional_files: Vec<PathBuf>,
|
||||
pub additional_esm_files: Vec<PathBuf>,
|
||||
pub compression_cb: Option<Box<CompressionCb>>,
|
||||
}
|
||||
|
||||
|
@ -44,6 +47,27 @@ pub fn create_snapshot(create_snapshot_options: CreateSnapshotOptions) {
|
|||
)
|
||||
.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_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(
|
||||
cargo_manifest_dir: &'static str,
|
||||
directory: &str,
|
||||
filter: Option<FilterFn>,
|
||||
) -> Vec<PathBuf> {
|
||||
let manifest_dir = Path::new(cargo_manifest_dir);
|
||||
let mut js_files = std::fs::read_dir(directory)
|
||||
|
@ -92,7 +119,7 @@ pub fn get_js_files(
|
|||
})
|
||||
.filter(|path| {
|
||||
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>>();
|
||||
js_files.sort();
|
||||
|
|
|
@ -2,150 +2,149 @@
|
|||
|
||||
/// <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 core = window.Deno.core;
|
||||
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 _closed = Symbol("[[closed]]");
|
||||
|
||||
const _name = Symbol("[[name]]");
|
||||
const _closed = Symbol("[[closed]]");
|
||||
const channels = [];
|
||||
let rid = null;
|
||||
|
||||
const channels = [];
|
||||
let rid = null;
|
||||
async function recv() {
|
||||
while (channels.length > 0) {
|
||||
const message = await core.opAsync("op_broadcast_recv", rid);
|
||||
|
||||
async function recv() {
|
||||
while (channels.length > 0) {
|
||||
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));
|
||||
if (message === null) {
|
||||
break;
|
||||
}
|
||||
|
||||
core.close(rid);
|
||||
rid = null;
|
||||
const { 0: name, 1: data } = message;
|
||||
dispatch(null, name, new Uint8Array(data));
|
||||
}
|
||||
|
||||
function dispatch(source, name, data) {
|
||||
for (let i = 0; i < channels.length; ++i) {
|
||||
const channel = channels[i];
|
||||
core.close(rid);
|
||||
rid = null;
|
||||
}
|
||||
|
||||
if (channel === source) continue; // Don't self-send.
|
||||
if (channel[_name] !== name) continue;
|
||||
if (channel[_closed]) continue;
|
||||
function dispatch(source, name, data) {
|
||||
for (let i = 0; i < channels.length; ++i) {
|
||||
const channel = channels[i];
|
||||
|
||||
const go = () => {
|
||||
if (channel[_closed]) return;
|
||||
const event = new MessageEvent("message", {
|
||||
data: core.deserialize(data), // TODO(bnoordhuis) Cache immutables.
|
||||
origin: "http://127.0.0.1",
|
||||
});
|
||||
setTarget(event, channel);
|
||||
channel.dispatchEvent(event);
|
||||
};
|
||||
if (channel === source) continue; // Don't self-send.
|
||||
if (channel[_name] !== name) continue;
|
||||
if (channel[_closed]) continue;
|
||||
|
||||
defer(go);
|
||||
}
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
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",
|
||||
const go = () => {
|
||||
if (channel[_closed]) return;
|
||||
const event = new MessageEvent("message", {
|
||||
data: core.deserialize(data), // TODO(bnoordhuis) Cache immutables.
|
||||
origin: "http://127.0.0.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) {
|
||||
// Create the rid immediately, otherwise there is a time window (and a
|
||||
// race condition) where messages can get lost, because recv() is async.
|
||||
rid = ops.op_broadcast_subscribe();
|
||||
recv();
|
||||
}
|
||||
}
|
||||
class BroadcastChannel extends EventTarget {
|
||||
[_name];
|
||||
[_closed] = false;
|
||||
|
||||
postMessage(message) {
|
||||
webidl.assertBranded(this, BroadcastChannelPrototype);
|
||||
get name() {
|
||||
return this[_name];
|
||||
}
|
||||
|
||||
const prefix = "Failed to execute 'postMessage' on 'BroadcastChannel'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
constructor(name) {
|
||||
super();
|
||||
|
||||
if (this[_closed]) {
|
||||
throw new DOMException("Already closed", "InvalidStateError");
|
||||
}
|
||||
const prefix = "Failed to construct 'BroadcastChannel'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
|
||||
if (typeof message === "function" || typeof message === "symbol") {
|
||||
throw new DOMException("Uncloneable value", "DataCloneError");
|
||||
}
|
||||
this[_name] = webidl.converters["DOMString"](name, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
|
||||
const data = core.serialize(message);
|
||||
this[webidl.brand] = webidl.brand;
|
||||
|
||||
// Send to other listeners in this VM.
|
||||
dispatch(this, this[_name], new Uint8Array(data));
|
||||
ArrayPrototypePush(channels, this);
|
||||
|
||||
// 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);
|
||||
}
|
||||
if (rid === null) {
|
||||
// Create the rid immediately, otherwise there is a time window (and a
|
||||
// race condition) where messages can get lost, because recv() is async.
|
||||
rid = ops.op_broadcast_subscribe();
|
||||
recv();
|
||||
}
|
||||
}
|
||||
|
||||
defineEventHandler(BroadcastChannel.prototype, "message");
|
||||
defineEventHandler(BroadcastChannel.prototype, "messageerror");
|
||||
const BroadcastChannelPrototype = BroadcastChannel.prototype;
|
||||
postMessage(message) {
|
||||
webidl.assertBranded(this, BroadcastChannelPrototype);
|
||||
|
||||
window.__bootstrap.broadcastChannel = { BroadcastChannel };
|
||||
})(this);
|
||||
const prefix = "Failed to execute 'postMessage' on 'BroadcastChannel'";
|
||||
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::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_webidl", "deno_web"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/broadcast_channel",
|
||||
"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.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.__bootstrap.core;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const {
|
||||
Symbol,
|
||||
TypeError,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
} = window.__bootstrap.primordials;
|
||||
const {
|
||||
Request,
|
||||
toInnerResponse,
|
||||
toInnerRequest,
|
||||
} = window.__bootstrap.fetch;
|
||||
const { URLPrototype } = window.__bootstrap.url;
|
||||
const RequestPrototype = Request.prototype;
|
||||
const { getHeader } = window.__bootstrap.headers;
|
||||
const { readableStreamForRid } = window.__bootstrap.streams;
|
||||
const core = globalThis.Deno.core;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
Symbol,
|
||||
TypeError,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
} = primordials;
|
||||
import {
|
||||
Request,
|
||||
RequestPrototype,
|
||||
toInnerRequest,
|
||||
} from "internal:ext/fetch/23_request.js";
|
||||
import { toInnerResponse } from "internal:ext/fetch/23_response.js";
|
||||
import { URLPrototype } from "internal:ext/url/00_url.js";
|
||||
import { getHeader } from "internal:ext/fetch/20_headers.js";
|
||||
import { readableStreamForRid } from "internal:ext/web/06_streams.js";
|
||||
|
||||
class CacheStorage {
|
||||
constructor() {
|
||||
webidl.illegalConstructor();
|
||||
class CacheStorage {
|
||||
constructor() {
|
||||
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) {
|
||||
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;
|
||||
// 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();
|
||||
|
||||
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);
|
||||
// Remove fragment from request URL before put.
|
||||
reqUrl.hash = "";
|
||||
|
||||
// Step 9-11.
|
||||
const rid = await core.opAsync(
|
||||
"op_cache_put",
|
||||
{
|
||||
cacheId: this[_id],
|
||||
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) {
|
||||
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);
|
||||
/** 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;
|
||||
}
|
||||
}
|
||||
|
||||
const _matchAll = Symbol("[[matchAll]]");
|
||||
const _id = Symbol("id");
|
||||
/** 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,
|
||||
});
|
||||
}
|
||||
|
||||
class Cache {
|
||||
/** @type {number} */
|
||||
[_id];
|
||||
|
||||
constructor() {
|
||||
webidl.illegalConstructor();
|
||||
/** 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);
|
||||
}
|
||||
|
||||
/** 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 '*'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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",
|
||||
// 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],
|
||||
requestUrl: reqUrl.toString(),
|
||||
responseHeaders: innerResponse.headerList,
|
||||
requestUrl: url.toString(),
|
||||
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);
|
||||
if (matchResult) {
|
||||
const { 0: meta, 1: responseBodyRid } = matchResult;
|
||||
let body = null;
|
||||
if (responseBodyRid !== null) {
|
||||
body = readableStreamForRid(responseBodyRid);
|
||||
}
|
||||
}
|
||||
// Step 12-19: TODO(@satyarohith): do the insertion in background.
|
||||
}
|
||||
|
||||
/** 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",
|
||||
const response = new Response(
|
||||
body,
|
||||
{
|
||||
cacheId: this[_id],
|
||||
requestUrl: url.toString(),
|
||||
requestHeaders: innerRequest.headerList,
|
||||
headers: meta.responseHeaders,
|
||||
status: meta.responseStatus,
|
||||
statusText: meta.responseStatusText,
|
||||
},
|
||||
);
|
||||
if (matchResult) {
|
||||
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);
|
||||
}
|
||||
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(Cache);
|
||||
const CacheStoragePrototype = CacheStorage.prototype;
|
||||
const CachePrototype = Cache.prototype;
|
||||
webidl.configurePrototype(CacheStorage);
|
||||
webidl.configurePrototype(Cache);
|
||||
const CacheStoragePrototype = CacheStorage.prototype;
|
||||
const CachePrototype = Cache.prototype;
|
||||
|
||||
let cacheStorage;
|
||||
window.__bootstrap.caches = {
|
||||
CacheStorage,
|
||||
Cache,
|
||||
cacheStorage() {
|
||||
if (!cacheStorage) {
|
||||
cacheStorage = webidl.createBranded(CacheStorage);
|
||||
}
|
||||
return cacheStorage;
|
||||
},
|
||||
};
|
||||
})(this);
|
||||
let cacheStorageStorage;
|
||||
function cacheStorage() {
|
||||
if (!cacheStorageStorage) {
|
||||
cacheStorageStorage = webidl.createBranded(CacheStorage);
|
||||
}
|
||||
return cacheStorageStorage;
|
||||
}
|
||||
|
||||
export { Cache, CacheStorage, cacheStorage };
|
||||
|
|
2
ext/cache/lib.rs
vendored
2
ext/cache/lib.rs
vendored
|
@ -27,7 +27,7 @@ pub fn init<CA: Cache + 'static>(
|
|||
) -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_webidl", "deno_web", "deno_url", "deno_fetch"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/cache",
|
||||
"01_cache.js",
|
||||
))
|
||||
|
|
|
@ -2,110 +2,107 @@
|
|||
|
||||
/// <reference path="../../core/internal.d.ts" />
|
||||
|
||||
"use strict";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
RegExp,
|
||||
StringPrototypeReplace,
|
||||
ArrayPrototypeJoin,
|
||||
} = primordials;
|
||||
|
||||
((window) => {
|
||||
const {
|
||||
RegExp,
|
||||
StringPrototypeReplace,
|
||||
ArrayPrototypeJoin,
|
||||
} = window.__bootstrap.primordials;
|
||||
let noColor = false;
|
||||
|
||||
let noColor = false;
|
||||
function setNoColor(value) {
|
||||
noColor = value;
|
||||
}
|
||||
|
||||
function setNoColor(value) {
|
||||
noColor = value;
|
||||
}
|
||||
function getNoColor() {
|
||||
return noColor;
|
||||
}
|
||||
|
||||
function getNoColor() {
|
||||
return noColor;
|
||||
}
|
||||
|
||||
function code(open, close) {
|
||||
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,
|
||||
function code(open, close) {
|
||||
return {
|
||||
open: `\x1b[${open}m`,
|
||||
close: `\x1b[${close}m`,
|
||||
regexp: new RegExp(`\\x1b\\[${close}m`, "g"),
|
||||
};
|
||||
})(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 lib="esnext" />
|
||||
|
||||
declare namespace globalThis {
|
||||
declare namespace __bootstrap {
|
||||
declare namespace console {
|
||||
declare function createFilteredInspectProxy<TObject>(params: {
|
||||
object: TObject;
|
||||
keys: (keyof TObject)[];
|
||||
evaluate: boolean;
|
||||
}): Record<string, unknown>;
|
||||
}
|
||||
}
|
||||
declare module "internal:ext/console/02_console.js" {
|
||||
function createFilteredInspectProxy<TObject>(params: {
|
||||
object: TObject;
|
||||
keys: (keyof TObject)[];
|
||||
evaluate: boolean;
|
||||
}): Record<string, unknown>;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use std::path::PathBuf;
|
|||
|
||||
pub fn init() -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/console",
|
||||
"01_colors.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="../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) => {
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { CryptoKey } = window.__bootstrap.crypto;
|
||||
const {
|
||||
ArrayBufferIsView,
|
||||
ArrayBufferPrototype,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
SafeArrayIterator,
|
||||
} = window.__bootstrap.primordials;
|
||||
webidl.converters.AlgorithmIdentifier = (V, opts) => {
|
||||
// Union for (object or DOMString)
|
||||
if (webidl.type(V) == "Object") {
|
||||
return webidl.converters.object(V, opts);
|
||||
}
|
||||
return webidl.converters.DOMString(V, opts);
|
||||
};
|
||||
|
||||
webidl.converters.AlgorithmIdentifier = (V, opts) => {
|
||||
// Union for (object or DOMString)
|
||||
if (webidl.type(V) == "Object") {
|
||||
return webidl.converters.object(V, opts);
|
||||
}
|
||||
return webidl.converters.DOMString(V, opts);
|
||||
};
|
||||
webidl.converters["BufferSource or JsonWebKey"] = (V, opts) => {
|
||||
// Union for (BufferSource or JsonWebKey)
|
||||
if (
|
||||
ArrayBufferIsView(V) ||
|
||||
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V)
|
||||
) {
|
||||
return webidl.converters.BufferSource(V, opts);
|
||||
}
|
||||
return webidl.converters.JsonWebKey(V, opts);
|
||||
};
|
||||
|
||||
webidl.converters["BufferSource or JsonWebKey"] = (V, opts) => {
|
||||
// Union for (BufferSource or JsonWebKey)
|
||||
if (
|
||||
ArrayBufferIsView(V) ||
|
||||
ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, V)
|
||||
) {
|
||||
return webidl.converters.BufferSource(V, opts);
|
||||
}
|
||||
return webidl.converters.JsonWebKey(V, opts);
|
||||
};
|
||||
webidl.converters.KeyType = webidl.createEnumConverter("KeyType", [
|
||||
"public",
|
||||
"private",
|
||||
"secret",
|
||||
]);
|
||||
|
||||
webidl.converters.KeyType = webidl.createEnumConverter("KeyType", [
|
||||
"public",
|
||||
"private",
|
||||
"secret",
|
||||
]);
|
||||
webidl.converters.KeyFormat = webidl.createEnumConverter("KeyFormat", [
|
||||
"raw",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
"jwk",
|
||||
]);
|
||||
|
||||
webidl.converters.KeyFormat = webidl.createEnumConverter("KeyFormat", [
|
||||
"raw",
|
||||
"pkcs8",
|
||||
"spki",
|
||||
"jwk",
|
||||
]);
|
||||
webidl.converters.KeyUsage = webidl.createEnumConverter("KeyUsage", [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"sign",
|
||||
"verify",
|
||||
"deriveKey",
|
||||
"deriveBits",
|
||||
"wrapKey",
|
||||
"unwrapKey",
|
||||
]);
|
||||
|
||||
webidl.converters.KeyUsage = webidl.createEnumConverter("KeyUsage", [
|
||||
"encrypt",
|
||||
"decrypt",
|
||||
"sign",
|
||||
"verify",
|
||||
"deriveKey",
|
||||
"deriveBits",
|
||||
"wrapKey",
|
||||
"unwrapKey",
|
||||
]);
|
||||
webidl.converters["sequence<KeyUsage>"] = webidl.createSequenceConverter(
|
||||
webidl.converters.KeyUsage,
|
||||
);
|
||||
|
||||
webidl.converters["sequence<KeyUsage>"] = webidl.createSequenceConverter(
|
||||
webidl.converters.KeyUsage,
|
||||
);
|
||||
webidl.converters.HashAlgorithmIdentifier =
|
||||
webidl.converters.AlgorithmIdentifier;
|
||||
|
||||
webidl.converters.HashAlgorithmIdentifier =
|
||||
webidl.converters.AlgorithmIdentifier;
|
||||
/** @type {webidl.Dictionary} */
|
||||
const dictAlgorithm = [{
|
||||
key: "name",
|
||||
converter: webidl.converters.DOMString,
|
||||
required: true,
|
||||
}];
|
||||
|
||||
/** @type {__bootstrap.webidl.Dictionary} */
|
||||
const dictAlgorithm = [{
|
||||
key: "name",
|
||||
converter: webidl.converters.DOMString,
|
||||
webidl.converters.Algorithm = webidl
|
||||
.createDictionaryConverter("Algorithm", dictAlgorithm);
|
||||
|
||||
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,
|
||||
}];
|
||||
},
|
||||
{
|
||||
key: "publicExponent",
|
||||
converter: webidl.converters.BigInteger,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
webidl.converters.Algorithm = webidl
|
||||
.createDictionaryConverter("Algorithm", dictAlgorithm);
|
||||
webidl.converters.RsaKeyGenParams = webidl
|
||||
.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} */
|
||||
const dictRsaKeyGenParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
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.RsaHashedKeyGenParams = webidl.createDictionaryConverter(
|
||||
"RsaHashedKeyGenParams",
|
||||
dictRsaHashedKeyGenParams,
|
||||
);
|
||||
|
||||
webidl.converters.RsaKeyGenParams = webidl
|
||||
.createDictionaryConverter("RsaKeyGenParams", dictRsaKeyGenParams);
|
||||
const dictRsaHashedImportParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "hash",
|
||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
const dictRsaHashedKeyGenParams = [
|
||||
...new SafeArrayIterator(dictRsaKeyGenParams),
|
||||
{
|
||||
key: "hash",
|
||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
webidl.converters.RsaHashedImportParams = webidl.createDictionaryConverter(
|
||||
"RsaHashedImportParams",
|
||||
dictRsaHashedImportParams,
|
||||
);
|
||||
|
||||
webidl.converters.RsaHashedKeyGenParams = webidl.createDictionaryConverter(
|
||||
"RsaHashedKeyGenParams",
|
||||
dictRsaHashedKeyGenParams,
|
||||
webidl.converters.NamedCurve = webidl.converters.DOMString;
|
||||
|
||||
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 = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "hash",
|
||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
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.RsaHashedImportParams = webidl.createDictionaryConverter(
|
||||
"RsaHashedImportParams",
|
||||
dictRsaHashedImportParams,
|
||||
);
|
||||
webidl.converters.JsonWebKey = webidl.createDictionaryConverter(
|
||||
"JsonWebKey",
|
||||
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 = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "namedCurve",
|
||||
converter: webidl.converters.NamedCurve,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
webidl.converters.HkdfParams = webidl
|
||||
.createDictionaryConverter("HkdfParams", dictHkdfParams);
|
||||
|
||||
webidl.converters.EcKeyImportParams = webidl.createDictionaryConverter(
|
||||
"EcKeyImportParams",
|
||||
dictEcKeyImportParams,
|
||||
);
|
||||
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,
|
||||
},
|
||||
];
|
||||
|
||||
const dictEcKeyGenParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "namedCurve",
|
||||
converter: webidl.converters.NamedCurve,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
webidl.converters.Pbkdf2Params = webidl
|
||||
.createDictionaryConverter("Pbkdf2Params", dictPbkdf2Params);
|
||||
|
||||
webidl.converters.EcKeyGenParams = webidl
|
||||
.createDictionaryConverter("EcKeyGenParams", dictEcKeyGenParams);
|
||||
const dictAesDerivedKeyParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "length",
|
||||
converter: (V, opts) =>
|
||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
const dictAesKeyGenParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "length",
|
||||
converter: (V, opts) =>
|
||||
webidl.converters["unsigned short"](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
const dictAesCbcParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "iv",
|
||||
converter: webidl.converters["BufferSource"],
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
|
||||
webidl.converters.AesKeyGenParams = webidl
|
||||
.createDictionaryConverter("AesKeyGenParams", dictAesKeyGenParams);
|
||||
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 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 }),
|
||||
},
|
||||
];
|
||||
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.HmacKeyGenParams = webidl
|
||||
.createDictionaryConverter("HmacKeyGenParams", dictHmacKeyGenParams);
|
||||
webidl.converters.AesDerivedKeyParams = webidl
|
||||
.createDictionaryConverter("AesDerivedKeyParams", dictAesDerivedKeyParams);
|
||||
|
||||
const dictRsaPssParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "saltLength",
|
||||
converter: (V, opts) =>
|
||||
webidl.converters["unsigned long"](V, { ...opts, enforceRange: true }),
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
webidl.converters.AesCbcParams = webidl
|
||||
.createDictionaryConverter("AesCbcParams", dictAesCbcParams);
|
||||
|
||||
webidl.converters.RsaPssParams = webidl
|
||||
.createDictionaryConverter("RsaPssParams", dictRsaPssParams);
|
||||
webidl.converters.AesGcmParams = webidl
|
||||
.createDictionaryConverter("AesGcmParams", dictAesGcmParams);
|
||||
|
||||
const dictRsaOaepParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "label",
|
||||
converter: webidl.converters["BufferSource"],
|
||||
},
|
||||
];
|
||||
webidl.converters.AesCtrParams = webidl
|
||||
.createDictionaryConverter("AesCtrParams", dictAesCtrParams);
|
||||
|
||||
webidl.converters.RsaOaepParams = webidl
|
||||
.createDictionaryConverter("RsaOaepParams", dictRsaOaepParams);
|
||||
webidl.converters.CryptoKey = webidl.createInterfaceConverter(
|
||||
"CryptoKey",
|
||||
CryptoKey.prototype,
|
||||
);
|
||||
|
||||
const dictEcdsaParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "hash",
|
||||
converter: webidl.converters.HashAlgorithmIdentifier,
|
||||
required: true,
|
||||
},
|
||||
];
|
||||
const dictCryptoKeyPair = [
|
||||
{
|
||||
key: "publicKey",
|
||||
converter: webidl.converters.CryptoKey,
|
||||
},
|
||||
{
|
||||
key: "privateKey",
|
||||
converter: webidl.converters.CryptoKey,
|
||||
},
|
||||
];
|
||||
|
||||
webidl.converters["EcdsaParams"] = webidl
|
||||
.createDictionaryConverter("EcdsaParams", dictEcdsaParams);
|
||||
webidl.converters.CryptoKeyPair = webidl
|
||||
.createDictionaryConverter("CryptoKeyPair", dictCryptoKeyPair);
|
||||
|
||||
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 }),
|
||||
},
|
||||
];
|
||||
const dictEcdhKeyDeriveParams = [
|
||||
...new SafeArrayIterator(dictAlgorithm),
|
||||
{
|
||||
key: "public",
|
||||
converter: webidl.converters.CryptoKey,
|
||||
required: 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 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);
|
||||
webidl.converters.EcdhKeyDeriveParams = webidl
|
||||
.createDictionaryConverter("EcdhKeyDeriveParams", dictEcdhKeyDeriveParams);
|
||||
|
|
|
@ -75,7 +75,7 @@ use crate::shared::RawKeyData;
|
|||
pub fn init(maybe_seed: Option<u64>) -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_webidl", "deno_web"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/crypto",
|
||||
"00_crypto.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="./lib.deno_fetch.d.ts" />
|
||||
/// <reference lib="esnext" />
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const {
|
||||
HTTP_TAB_OR_SPACE_PREFIX_RE,
|
||||
HTTP_TAB_OR_SPACE_SUFFIX_RE,
|
||||
HTTP_TOKEN_CODE_POINT_RE,
|
||||
byteLowerCase,
|
||||
collectSequenceOfCodepoints,
|
||||
collectHttpQuotedString,
|
||||
httpTrim,
|
||||
} = window.__bootstrap.infra;
|
||||
const {
|
||||
ArrayIsArray,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSort,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypeSplice,
|
||||
ArrayPrototypeFilter,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
ObjectEntries,
|
||||
RegExpPrototypeTest,
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
SymbolIterator,
|
||||
StringPrototypeReplaceAll,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import {
|
||||
byteLowerCase,
|
||||
collectHttpQuotedString,
|
||||
collectSequenceOfCodepoints,
|
||||
HTTP_TAB_OR_SPACE_PREFIX_RE,
|
||||
HTTP_TAB_OR_SPACE_SUFFIX_RE,
|
||||
HTTP_TOKEN_CODE_POINT_RE,
|
||||
httpTrim,
|
||||
} from "internal:ext/web/00_infra.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayIsArray,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSort,
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypeSplice,
|
||||
ArrayPrototypeFilter,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
ObjectEntries,
|
||||
RegExpPrototypeTest,
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
SymbolIterator,
|
||||
StringPrototypeReplaceAll,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
|
||||
const _headerList = Symbol("header list");
|
||||
const _iterableHeaders = Symbol("iterable headers");
|
||||
const _guard = Symbol("guard");
|
||||
const _headerList = Symbol("header list");
|
||||
const _iterableHeaders = Symbol("iterable headers");
|
||||
const _guard = Symbol("guard");
|
||||
|
||||
/**
|
||||
* @typedef Header
|
||||
* @type {[string, string]}
|
||||
*/
|
||||
/**
|
||||
* @typedef Header
|
||||
* @type {[string, string]}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef HeaderList
|
||||
* @type {Header[]}
|
||||
*/
|
||||
/**
|
||||
* @typedef HeaderList
|
||||
* @type {Header[]}
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} potentialValue
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizeHeaderValue(potentialValue) {
|
||||
return httpTrim(potentialValue);
|
||||
/**
|
||||
* @param {string} potentialValue
|
||||
* @returns {string}
|
||||
*/
|
||||
function normalizeHeaderValue(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 {HeadersInit} object
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
*/
|
||||
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]);
|
||||
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);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Regex matching illegal chars in a header value
|
||||
// deno-lint-ignore no-control-regex
|
||||
const ILLEGAL_VALUE_CHARS = /[\x00\x0A\x0D]/;
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://fetch.spec.whatwg.org/#concept-headers-append
|
||||
* @param {Headers} headers
|
||||
* @param {string} name
|
||||
* @param {string} value
|
||||
*/
|
||||
function appendHeader(headers, name, value) {
|
||||
// 1.
|
||||
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.
|
||||
|
@ -109,371 +381,97 @@
|
|||
throw new TypeError("Header value is not valid.");
|
||||
}
|
||||
|
||||
// 3.
|
||||
if (headers[_guard] == "immutable") {
|
||||
if (this[_guard] == "immutable") {
|
||||
throw new TypeError("Headers are immutable.");
|
||||
}
|
||||
|
||||
// 7.
|
||||
const list = headers[_headerList];
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
if (!added) {
|
||||
list[i][1] = value;
|
||||
added = true;
|
||||
} 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);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)}`;
|
||||
if (!added) {
|
||||
ArrayPrototypePush(list, [name, value]);
|
||||
}
|
||||
}
|
||||
|
||||
webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
|
||||
|
||||
webidl.configurePrototype(Headers);
|
||||
const HeadersPrototype = 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);
|
||||
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
||||
const headers = {};
|
||||
// deno-lint-ignore prefer-primordials
|
||||
for (const header of this) {
|
||||
headers[header[0]] = header[1];
|
||||
}
|
||||
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,
|
||||
return `Headers ${inspect(headers)}`;
|
||||
}
|
||||
}
|
||||
|
||||
webidl.mixinPairIterable("Headers", Headers, _iterableHeaders, 0, 1);
|
||||
|
||||
webidl.configurePrototype(Headers);
|
||||
const HeadersPrototype = 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 {"immutable" | "request" | "request-no-cors" | "response" | "none"} guard
|
||||
* @returns {Headers}
|
||||
*/
|
||||
function headersFromHeaderList(list, guard) {
|
||||
const headers = webidl.createBranded(Headers);
|
||||
headers[_headerList] = list;
|
||||
headers[_guard] = guard;
|
||||
return headers;
|
||||
}
|
||||
/**
|
||||
* @param {HeaderList} list
|
||||
* @param {"immutable" | "request" | "request-no-cors" | "response" | "none"} guard
|
||||
* @returns {Headers}
|
||||
*/
|
||||
function headersFromHeaderList(list, guard) {
|
||||
const headers = webidl.createBranded(Headers);
|
||||
headers[_headerList] = list;
|
||||
headers[_guard] = guard;
|
||||
return headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Headers}
|
||||
* @returns {HeaderList}
|
||||
*/
|
||||
function headerListFromHeaders(headers) {
|
||||
return headers[_headerList];
|
||||
}
|
||||
/**
|
||||
* @param {Headers} headers
|
||||
* @returns {HeaderList}
|
||||
*/
|
||||
function headerListFromHeaders(headers) {
|
||||
return headers[_headerList];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Headers}
|
||||
* @returns {"immutable" | "request" | "request-no-cors" | "response" | "none"}
|
||||
*/
|
||||
function guardFromHeaders(headers) {
|
||||
return headers[_guard];
|
||||
}
|
||||
/**
|
||||
* @param {Headers} headers
|
||||
* @returns {"immutable" | "request" | "request-no-cors" | "response" | "none"}
|
||||
*/
|
||||
function guardFromHeaders(headers) {
|
||||
return headers[_guard];
|
||||
}
|
||||
|
||||
window.__bootstrap.headers = {
|
||||
headersFromHeaderList,
|
||||
headerListFromHeaders,
|
||||
getDecodeSplitHeader,
|
||||
guardFromHeaders,
|
||||
fillHeaders,
|
||||
getHeader,
|
||||
Headers,
|
||||
};
|
||||
})(this);
|
||||
export {
|
||||
fillHeaders,
|
||||
getDecodeSplitHeader,
|
||||
getHeader,
|
||||
guardFromHeaders,
|
||||
headerListFromHeaders,
|
||||
Headers,
|
||||
headersFromHeaderList,
|
||||
};
|
||||
|
|
|
@ -8,516 +8,518 @@
|
|||
/// <reference path="../web/06_streams_types.d.ts" />
|
||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||
/// <reference lib="esnext" />
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const webidl = globalThis.__bootstrap.webidl;
|
||||
const { Blob, BlobPrototype, File, FilePrototype } =
|
||||
globalThis.__bootstrap.file;
|
||||
const {
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSlice,
|
||||
ArrayPrototypeSplice,
|
||||
Map,
|
||||
MapPrototypeGet,
|
||||
MapPrototypeSet,
|
||||
MathRandom,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
Symbol,
|
||||
StringFromCharCode,
|
||||
StringPrototypeTrim,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeSplit,
|
||||
StringPrototypeReplace,
|
||||
StringPrototypeIndexOf,
|
||||
StringPrototypePadStart,
|
||||
StringPrototypeCodePointAt,
|
||||
StringPrototypeReplaceAll,
|
||||
TypeError,
|
||||
TypedArrayPrototypeSubarray,
|
||||
} = window.__bootstrap.primordials;
|
||||
const core = globalThis.Deno.core;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import {
|
||||
Blob,
|
||||
BlobPrototype,
|
||||
File,
|
||||
FilePrototype,
|
||||
} from "internal:ext/web/09_file.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeSlice,
|
||||
ArrayPrototypeSplice,
|
||||
Map,
|
||||
MapPrototypeGet,
|
||||
MapPrototypeSet,
|
||||
MathRandom,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
Symbol,
|
||||
StringFromCharCode,
|
||||
StringPrototypeTrim,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeSplit,
|
||||
StringPrototypeReplace,
|
||||
StringPrototypeIndexOf,
|
||||
StringPrototypePadStart,
|
||||
StringPrototypeCodePointAt,
|
||||
StringPrototypeReplaceAll,
|
||||
TypeError,
|
||||
TypedArrayPrototypeSubarray,
|
||||
} = primordials;
|
||||
|
||||
const entryList = Symbol("entry list");
|
||||
const entryList = Symbol("entry list");
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string | Blob} value
|
||||
* @param {string | undefined} filename
|
||||
* @returns {FormDataEntry}
|
||||
*/
|
||||
function createEntry(name, value, filename) {
|
||||
if (
|
||||
ObjectPrototypeIsPrototypeOf(BlobPrototype, value) &&
|
||||
!ObjectPrototypeIsPrototypeOf(FilePrototype, value)
|
||||
) {
|
||||
value = new File([value], "blob", { type: value.type });
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {string | Blob} value
|
||||
* @param {string | undefined} filename
|
||||
* @returns {FormDataEntry}
|
||||
*/
|
||||
function createEntry(name, value, filename) {
|
||||
if (
|
||||
ObjectPrototypeIsPrototypeOf(BlobPrototype, value) &&
|
||||
!ObjectPrototypeIsPrototypeOf(FilePrototype, value)
|
||||
) {
|
||||
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 (
|
||||
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,
|
||||
};
|
||||
this[webidl.brand] = webidl.brand;
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef FormDataEntry
|
||||
* @property {string} name
|
||||
* @property {FormDataEntryValue} value
|
||||
* @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 });
|
||||
|
||||
class FormData {
|
||||
/** @type {FormDataEntry[]} */
|
||||
[entryList] = [];
|
||||
|
||||
/** @param {void} form */
|
||||
constructor(form) {
|
||||
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, {
|
||||
name = webidl.converters["USVString"](name, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
|
||||
valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
context: "Argument 2",
|
||||
});
|
||||
if (ObjectPrototypeIsPrototypeOf(BlobPrototype, valueOrBlobValue)) {
|
||||
valueOrBlobValue = webidl.converters["Blob"](valueOrBlobValue, {
|
||||
if (filename !== undefined) {
|
||||
filename = webidl.converters["USVString"](filename, {
|
||||
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",
|
||||
context: "Argument 3",
|
||||
});
|
||||
}
|
||||
|
||||
const entry = createEntry(name, valueOrBlobValue, filename);
|
||||
|
||||
ArrayPrototypePush(this[entryList], entry);
|
||||
} else {
|
||||
valueOrBlobValue = webidl.converters["USVString"](valueOrBlobValue, {
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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 });
|
||||
const entry = createEntry(name, valueOrBlobValue, filename);
|
||||
|
||||
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,
|
||||
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];
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i].name === name) {
|
||||
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--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
if (!added) {
|
||||
ArrayPrototypePush(list, entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
|
||||
webidl.mixinPairIterable("FormData", FormData, entryList, "name", "value");
|
||||
|
||||
webidl.configurePrototype(FormData);
|
||||
const FormDataPrototype = FormData.prototype;
|
||||
webidl.configurePrototype(FormData);
|
||||
const FormDataPrototype = FormData.prototype;
|
||||
|
||||
const escape = (str, isFilename) => {
|
||||
const escapeMap = {
|
||||
"\n": "%0A",
|
||||
"\r": "%0D",
|
||||
'"': "%22",
|
||||
};
|
||||
|
||||
return StringPrototypeReplace(
|
||||
isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n"),
|
||||
/([\n\r"])/g,
|
||||
(c) => escapeMap[c],
|
||||
);
|
||||
const escape = (str, isFilename) => {
|
||||
const escapeMap = {
|
||||
"\n": "%0A",
|
||||
"\r": "%0D",
|
||||
'"': "%22",
|
||||
};
|
||||
|
||||
/**
|
||||
* convert FormData to a Blob synchronous without reading all of the files
|
||||
* @param {globalThis.FormData} formData
|
||||
*/
|
||||
function formDataToBlob(formData) {
|
||||
const boundary = StringPrototypePadStart(
|
||||
StringPrototypeSlice(
|
||||
StringPrototypeReplaceAll(`${MathRandom()}${MathRandom()}`, ".", ""),
|
||||
-28,
|
||||
),
|
||||
32,
|
||||
"-",
|
||||
);
|
||||
const chunks = [];
|
||||
const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="`;
|
||||
return StringPrototypeReplace(
|
||||
isFilename ? str : StringPrototypeReplace(str, /\r?\n|\r/g, "\r\n"),
|
||||
/([\n\r"])/g,
|
||||
(c) => escapeMap[c],
|
||||
);
|
||||
};
|
||||
|
||||
// deno-lint-ignore prefer-primordials
|
||||
for (const { 0: name, 1: value } of formData) {
|
||||
if (typeof value === "string") {
|
||||
ArrayPrototypePush(
|
||||
chunks,
|
||||
prefix + escape(name) + '"' + CRLF + CRLF +
|
||||
StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
|
||||
);
|
||||
} else {
|
||||
ArrayPrototypePush(
|
||||
chunks,
|
||||
prefix + escape(name) + `"; filename="${escape(value.name, true)}"` +
|
||||
CRLF +
|
||||
`Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`,
|
||||
value,
|
||||
CRLF,
|
||||
);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* convert FormData to a Blob synchronous without reading all of the files
|
||||
* @param {globalThis.FormData} formData
|
||||
*/
|
||||
function formDataToBlob(formData) {
|
||||
const boundary = StringPrototypePadStart(
|
||||
StringPrototypeSlice(
|
||||
StringPrototypeReplaceAll(`${MathRandom()}${MathRandom()}`, ".", ""),
|
||||
-28,
|
||||
),
|
||||
32,
|
||||
"-",
|
||||
);
|
||||
const chunks = [];
|
||||
const prefix = `--${boundary}\r\nContent-Disposition: form-data; name="`;
|
||||
|
||||
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 {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") ?? "",
|
||||
// deno-lint-ignore prefer-primordials
|
||||
for (const { 0: name, 1: value } of formData) {
|
||||
if (typeof value === "string") {
|
||||
ArrayPrototypePush(
|
||||
chunks,
|
||||
prefix + escape(name) + '"' + CRLF + CRLF +
|
||||
StringPrototypeReplace(value, /\r(?!\n)|(?<!\r)\n/g, CRLF) + CRLF,
|
||||
);
|
||||
} else {
|
||||
ArrayPrototypePush(
|
||||
chunks,
|
||||
prefix + escape(name) + `"; filename="${escape(value.name, true)}"` +
|
||||
CRLF +
|
||||
`Content-Type: ${value.type || "application/octet-stream"}\r\n\r\n`,
|
||||
value,
|
||||
CRLF,
|
||||
);
|
||||
|
||||
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 {string | undefined} boundary
|
||||
* @returns {FormData}
|
||||
*/
|
||||
function parseFormData(body, boundary) {
|
||||
const parser = new MultipartParser(body, boundary);
|
||||
return parser.parse();
|
||||
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 {FormDataEntry[]} entries
|
||||
* @returns {FormData}
|
||||
* @param {string} headersText
|
||||
* @returns {{ headers: Headers, disposition: Map<string, string> }}
|
||||
*/
|
||||
function formDataFromEntries(entries) {
|
||||
const fd = new FormData();
|
||||
fd[entryList] = entries;
|
||||
return fd;
|
||||
#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 };
|
||||
}
|
||||
|
||||
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 = {
|
||||
FormData,
|
||||
FormDataPrototype,
|
||||
formDataToBlob,
|
||||
parseFormData,
|
||||
formDataFromEntries,
|
||||
};
|
||||
})(globalThis);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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="./lib.deno_fetch.d.ts" />
|
||||
/// <reference lib="esnext" />
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const webidl = globalThis.__bootstrap.webidl;
|
||||
const { parseUrlEncoded } = globalThis.__bootstrap.url;
|
||||
const { URLSearchParamsPrototype } = globalThis.__bootstrap.url;
|
||||
const {
|
||||
parseFormData,
|
||||
formDataFromEntries,
|
||||
formDataToBlob,
|
||||
FormDataPrototype,
|
||||
} = globalThis.__bootstrap.formData;
|
||||
const mimesniff = globalThis.__bootstrap.mimesniff;
|
||||
const { BlobPrototype } = globalThis.__bootstrap.file;
|
||||
const {
|
||||
isReadableStreamDisturbed,
|
||||
errorReadableStream,
|
||||
readableStreamClose,
|
||||
readableStreamDisturb,
|
||||
readableStreamCollectIntoUint8Array,
|
||||
readableStreamThrowIfErrored,
|
||||
createProxy,
|
||||
ReadableStreamPrototype,
|
||||
} = globalThis.__bootstrap.streams;
|
||||
const {
|
||||
ArrayBufferPrototype,
|
||||
ArrayBufferIsView,
|
||||
ArrayPrototypeMap,
|
||||
JSONParse,
|
||||
ObjectDefineProperties,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
// TODO(lucacasonato): add SharedArrayBuffer to primordials
|
||||
// SharedArrayBufferPrototype
|
||||
TypedArrayPrototypeSlice,
|
||||
TypeError,
|
||||
Uint8Array,
|
||||
Uint8ArrayPrototype,
|
||||
} = window.__bootstrap.primordials;
|
||||
const core = globalThis.Deno.core;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import {
|
||||
parseUrlEncoded,
|
||||
URLSearchParamsPrototype,
|
||||
} from "internal:ext/url/00_url.js";
|
||||
import {
|
||||
formDataFromEntries,
|
||||
FormDataPrototype,
|
||||
formDataToBlob,
|
||||
parseFormData,
|
||||
} from "internal:ext/fetch/21_formdata.js";
|
||||
import * as mimesniff from "internal:ext/web/01_mimesniff.js";
|
||||
import { BlobPrototype } from "internal:ext/web/09_file.js";
|
||||
import {
|
||||
createProxy,
|
||||
errorReadableStream,
|
||||
isReadableStreamDisturbed,
|
||||
readableStreamClose,
|
||||
readableStreamCollectIntoUint8Array,
|
||||
readableStreamDisturb,
|
||||
ReadableStreamPrototype,
|
||||
readableStreamThrowIfErrored,
|
||||
} from "internal:ext/web/06_streams.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayBufferPrototype,
|
||||
ArrayBufferIsView,
|
||||
ArrayPrototypeMap,
|
||||
JSONParse,
|
||||
ObjectDefineProperties,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
// TODO(lucacasonato): add SharedArrayBuffer to primordials
|
||||
// SharedArrayBufferPrototype
|
||||
TypedArrayPrototypeSlice,
|
||||
TypeError,
|
||||
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
|
||||
* @returns {Uint8Array}
|
||||
* @param {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} stream
|
||||
*/
|
||||
function chunkToU8(chunk) {
|
||||
return typeof chunk === "string" ? core.encode(chunk) : chunk;
|
||||
constructor(stream) {
|
||||
/** @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array | string} chunk
|
||||
* @returns {string}
|
||||
*/
|
||||
function chunkToString(chunk) {
|
||||
return typeof chunk === "string" ? chunk : core.decode(chunk);
|
||||
}
|
||||
|
||||
class InnerBody {
|
||||
/**
|
||||
* @param {ReadableStream<Uint8Array> | { body: Uint8Array | string, consumed: boolean }} stream
|
||||
*/
|
||||
constructor(stream) {
|
||||
/** @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);
|
||||
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.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) : "",
|
||||
this.streamOrStatic = new ReadableStream({
|
||||
start(controller) {
|
||||
controller.enqueue(chunkToU8(body));
|
||||
controller.close();
|
||||
},
|
||||
});
|
||||
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.",
|
||||
);
|
||||
}
|
||||
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] }),
|
||||
),
|
||||
}
|
||||
}
|
||||
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 {
|
||||
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":
|
||||
return JSONParse(chunkToString(bytes));
|
||||
case "text":
|
||||
return chunkToString(bytes);
|
||||
throw new TypeError("Missing content type");
|
||||
}
|
||||
case "JSON":
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
// 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 };
|
||||
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 };
|
||||
}
|
||||
|
||||
webidl.converters["BodyInit_DOMString"] = (V, opts) => {
|
||||
// Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
|
||||
if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, V)) {
|
||||
return webidl.converters["ReadableStream"](V, opts);
|
||||
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
|
||||
return webidl.converters["Blob"](V, opts);
|
||||
} else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, V)) {
|
||||
return webidl.converters["FormData"](V, opts);
|
||||
} else if (ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, V)) {
|
||||
return webidl.converters["URLSearchParams"](V, opts);
|
||||
webidl.converters["BodyInit_DOMString"] = (V, opts) => {
|
||||
// Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
|
||||
if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, V)) {
|
||||
return webidl.converters["ReadableStream"](V, opts);
|
||||
} else if (ObjectPrototypeIsPrototypeOf(BlobPrototype, V)) {
|
||||
return webidl.converters["Blob"](V, opts);
|
||||
} else if (ObjectPrototypeIsPrototypeOf(FormDataPrototype, V)) {
|
||||
return webidl.converters["FormData"](V, opts);
|
||||
} else if (ObjectPrototypeIsPrototypeOf(URLSearchParamsPrototype, V)) {
|
||||
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 (
|
||||
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);
|
||||
}
|
||||
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.
|
||||
// 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"],
|
||||
);
|
||||
}
|
||||
// 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.
|
||||
// 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"],
|
||||
);
|
||||
|
||||
window.__bootstrap.fetchBody = { mixinBody, InnerBody, extractBody };
|
||||
})(globalThis);
|
||||
export { extractBody, InnerBody, mixinBody };
|
||||
|
|
|
@ -9,40 +9,34 @@
|
|||
/// <reference path="../web/06_streams_types.d.ts" />
|
||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||
/// <reference lib="esnext" />
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const core = globalThis.Deno.core;
|
||||
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
|
||||
* @returns {HttpClient}
|
||||
* @param {number} rid
|
||||
*/
|
||||
function createHttpClient(options) {
|
||||
options.caCerts ??= [];
|
||||
return new HttpClient(
|
||||
ops.op_fetch_custom_client(
|
||||
options,
|
||||
),
|
||||
);
|
||||
constructor(rid) {
|
||||
this.rid = rid;
|
||||
}
|
||||
|
||||
class HttpClient {
|
||||
/**
|
||||
* @param {number} rid
|
||||
*/
|
||||
constructor(rid) {
|
||||
this.rid = rid;
|
||||
}
|
||||
close() {
|
||||
core.close(this.rid);
|
||||
}
|
||||
close() {
|
||||
core.close(this.rid);
|
||||
}
|
||||
const HttpClientPrototype = HttpClient.prototype;
|
||||
}
|
||||
const HttpClientPrototype = HttpClient.prototype;
|
||||
|
||||
window.__bootstrap.fetch ??= {};
|
||||
window.__bootstrap.fetch.createHttpClient = createHttpClient;
|
||||
window.__bootstrap.fetch.HttpClient = HttpClient;
|
||||
window.__bootstrap.fetch.HttpClientPrototype = HttpClientPrototype;
|
||||
})(globalThis);
|
||||
export { createHttpClient, HttpClient, HttpClientPrototype };
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -9,510 +9,510 @@
|
|||
/// <reference path="../web/06_streams_types.d.ts" />
|
||||
/// <reference path="./lib.deno_fetch.d.ts" />
|
||||
/// <reference lib="esnext" />
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const { isProxy } = Deno.core;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const consoleInternal = window.__bootstrap.console;
|
||||
const {
|
||||
byteLowerCase,
|
||||
} = window.__bootstrap.infra;
|
||||
const { HTTP_TAB_OR_SPACE, regexMatcher, serializeJSValueToJSONString } =
|
||||
window.__bootstrap.infra;
|
||||
const { extractBody, mixinBody } = window.__bootstrap.fetchBody;
|
||||
const { getLocationHref } = window.__bootstrap.location;
|
||||
const { extractMimeType } = window.__bootstrap.mimesniff;
|
||||
const { URL } = window.__bootstrap.url;
|
||||
const {
|
||||
getDecodeSplitHeader,
|
||||
headerListFromHeaders,
|
||||
headersFromHeaderList,
|
||||
guardFromHeaders,
|
||||
fillHeaders,
|
||||
} = window.__bootstrap.headers;
|
||||
const {
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePush,
|
||||
ObjectDefineProperties,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
RangeError,
|
||||
RegExp,
|
||||
RegExpPrototypeTest,
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
const core = globalThis.Deno.core;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import { createFilteredInspectProxy } from "internal:ext/console/02_console.js";
|
||||
import {
|
||||
byteLowerCase,
|
||||
HTTP_TAB_OR_SPACE,
|
||||
regexMatcher,
|
||||
serializeJSValueToJSONString,
|
||||
} from "internal:ext/web/00_infra.js";
|
||||
import { extractBody, mixinBody } from "internal:ext/fetch/22_body.js";
|
||||
import { getLocationHref } from "internal:ext/web/12_location.js";
|
||||
import { extractMimeType } from "internal:ext/web/01_mimesniff.js";
|
||||
import { URL } from "internal:ext/url/00_url.js";
|
||||
import {
|
||||
fillHeaders,
|
||||
getDecodeSplitHeader,
|
||||
guardFromHeaders,
|
||||
headerListFromHeaders,
|
||||
headersFromHeaderList,
|
||||
} from "internal:ext/fetch/20_headers.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypePush,
|
||||
ObjectDefineProperties,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
RangeError,
|
||||
RegExp,
|
||||
RegExpPrototypeTest,
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
|
||||
const VCHAR = ["\x21-\x7E"];
|
||||
const OBS_TEXT = ["\x80-\xFF"];
|
||||
const VCHAR = ["\x21-\x7E"];
|
||||
const OBS_TEXT = ["\x80-\xFF"];
|
||||
|
||||
const REASON_PHRASE = [
|
||||
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
||||
...new SafeArrayIterator(VCHAR),
|
||||
...new SafeArrayIterator(OBS_TEXT),
|
||||
];
|
||||
const REASON_PHRASE_MATCHER = regexMatcher(REASON_PHRASE);
|
||||
const REASON_PHRASE_RE = new RegExp(`^[${REASON_PHRASE_MATCHER}]*$`);
|
||||
const REASON_PHRASE = [
|
||||
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
||||
...new SafeArrayIterator(VCHAR),
|
||||
...new SafeArrayIterator(OBS_TEXT),
|
||||
];
|
||||
const REASON_PHRASE_MATCHER = regexMatcher(REASON_PHRASE);
|
||||
const REASON_PHRASE_RE = new RegExp(`^[${REASON_PHRASE_MATCHER}]*$`);
|
||||
|
||||
const _response = Symbol("response");
|
||||
const _headers = Symbol("headers");
|
||||
const _mimeType = Symbol("mime type");
|
||||
const _body = Symbol("body");
|
||||
const _response = Symbol("response");
|
||||
const _headers = Symbol("headers");
|
||||
const _mimeType = Symbol("mime type");
|
||||
const _body = Symbol("body");
|
||||
|
||||
/**
|
||||
* @typedef InnerResponse
|
||||
* @property {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"} type
|
||||
* @property {() => string | null} url
|
||||
* @property {string[]} urlList
|
||||
* @property {number} status
|
||||
* @property {string} statusMessage
|
||||
* @property {[string, string][]} headerList
|
||||
* @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body
|
||||
* @property {boolean} aborted
|
||||
* @property {string} [error]
|
||||
*/
|
||||
/**
|
||||
* @typedef InnerResponse
|
||||
* @property {"basic" | "cors" | "default" | "error" | "opaque" | "opaqueredirect"} type
|
||||
* @property {() => string | null} url
|
||||
* @property {string[]} urlList
|
||||
* @property {number} status
|
||||
* @property {string} statusMessage
|
||||
* @property {[string, string][]} headerList
|
||||
* @property {null | typeof __window.bootstrap.fetchBody.InnerBody} body
|
||||
* @property {boolean} aborted
|
||||
* @property {string} [error]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {number} status
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function nullBodyStatus(status) {
|
||||
return status === 101 || status === 204 || status === 205 || status === 304;
|
||||
}
|
||||
/**
|
||||
* @param {number} status
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function nullBodyStatus(status) {
|
||||
return status === 101 || status === 204 || status === 205 || status === 304;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} status
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function redirectStatus(status) {
|
||||
return status === 301 || status === 302 || status === 303 ||
|
||||
status === 307 || status === 308;
|
||||
}
|
||||
/**
|
||||
* @param {number} status
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function redirectStatus(status) {
|
||||
return status === 301 || status === 302 || status === 303 ||
|
||||
status === 307 || status === 308;
|
||||
}
|
||||
|
||||
/**
|
||||
* https://fetch.spec.whatwg.org/#concept-response-clone
|
||||
* @param {InnerResponse} response
|
||||
* @returns {InnerResponse}
|
||||
*/
|
||||
function cloneInnerResponse(response) {
|
||||
const urlList = [...new SafeArrayIterator(response.urlList)];
|
||||
const headerList = ArrayPrototypeMap(
|
||||
response.headerList,
|
||||
(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,
|
||||
/**
|
||||
* https://fetch.spec.whatwg.org/#concept-response-clone
|
||||
* @param {InnerResponse} response
|
||||
* @returns {InnerResponse}
|
||||
*/
|
||||
function cloneInnerResponse(response) {
|
||||
const urlList = [...new SafeArrayIterator(response.urlList)];
|
||||
const headerList = ArrayPrototypeMap(
|
||||
response.headerList,
|
||||
(x) => [x[0], x[1]],
|
||||
);
|
||||
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" && !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);
|
||||
|
||||
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];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Response} response
|
||||
* @returns {InnerResponse}
|
||||
*/
|
||||
function toInnerResponse(response) {
|
||||
return response[_response];
|
||||
/**
|
||||
* @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: 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}
|
||||
*/
|
||||
function fromInnerResponse(inner, guard) {
|
||||
static error() {
|
||||
const inner = newInnerResponse(0);
|
||||
inner.type = "error";
|
||||
const response = webidl.createBranded(Response);
|
||||
response[_response] = inner;
|
||||
response[_headers] = headersFromHeaderList(inner.headerList, guard);
|
||||
response[_headers] = headersFromHeaderList(
|
||||
response[_response].headerList,
|
||||
"immutable",
|
||||
);
|
||||
return response;
|
||||
}
|
||||
|
||||
window.__bootstrap.fetch ??= {};
|
||||
window.__bootstrap.fetch.Response = Response;
|
||||
window.__bootstrap.fetch.ResponsePrototype = ResponsePrototype;
|
||||
window.__bootstrap.fetch.newInnerResponse = newInnerResponse;
|
||||
window.__bootstrap.fetch.toInnerResponse = toInnerResponse;
|
||||
window.__bootstrap.fetch.fromInnerResponse = fromInnerResponse;
|
||||
window.__bootstrap.fetch.redirectStatus = redirectStatus;
|
||||
window.__bootstrap.fetch.nullBodyStatus = nullBodyStatus;
|
||||
window.__bootstrap.fetch.networkError = networkError;
|
||||
window.__bootstrap.fetch.abortedNetworkError = abortedNetworkError;
|
||||
})(globalThis);
|
||||
/**
|
||||
* @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(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 lib="esnext" />
|
||||
|
||||
declare namespace globalThis {
|
||||
declare namespace __bootstrap {
|
||||
declare var fetchUtil: {
|
||||
requiredArguments(name: string, length: number, required: number): void;
|
||||
};
|
||||
declare var domIterable: {
|
||||
DomIterableMixin(base: any, dataSymbol: symbol): any;
|
||||
};
|
||||
|
||||
declare var domIterable: {
|
||||
DomIterableMixin(base: any, dataSymbol: symbol): any;
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
declare module "internal:ext/fetch/20_headers.js" {
|
||||
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 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"))
|
||||
.dependencies(vec!["deno_webidl", "deno_web", "deno_url", "deno_console"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/fetch",
|
||||
"01_fetch_util.js",
|
||||
"20_headers.js",
|
||||
"21_formdata.js",
|
||||
"22_body.js",
|
||||
|
|
|
@ -1,510 +1,509 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const __bootstrap = window.__bootstrap;
|
||||
const {
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypeJoin,
|
||||
ObjectDefineProperty,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
Number,
|
||||
NumberIsSafeInteger,
|
||||
TypeError,
|
||||
Uint8Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
BigInt64Array,
|
||||
BigUint64Array,
|
||||
Function,
|
||||
ReflectHas,
|
||||
PromisePrototypeThen,
|
||||
MathMax,
|
||||
MathCeil,
|
||||
SafeMap,
|
||||
SafeArrayIterator,
|
||||
} = window.__bootstrap.primordials;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
const internals = globalThis.__bootstrap.internals;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypeJoin,
|
||||
ObjectDefineProperty,
|
||||
ObjectPrototypeHasOwnProperty,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
Number,
|
||||
NumberIsSafeInteger,
|
||||
TypeError,
|
||||
Uint8Array,
|
||||
Int32Array,
|
||||
Uint32Array,
|
||||
BigInt64Array,
|
||||
BigUint64Array,
|
||||
Function,
|
||||
ReflectHas,
|
||||
PromisePrototypeThen,
|
||||
MathMax,
|
||||
MathCeil,
|
||||
SafeMap,
|
||||
SafeArrayIterator,
|
||||
} = primordials;
|
||||
|
||||
const U32_BUFFER = new Uint32Array(2);
|
||||
const U64_BUFFER = new BigUint64Array(U32_BUFFER.buffer);
|
||||
const I64_BUFFER = new BigInt64Array(U32_BUFFER.buffer);
|
||||
class UnsafePointerView {
|
||||
pointer;
|
||||
const U32_BUFFER = new Uint32Array(2);
|
||||
const U64_BUFFER = new BigUint64Array(U32_BUFFER.buffer);
|
||||
const I64_BUFFER = new BigInt64Array(U32_BUFFER.buffer);
|
||||
class UnsafePointerView {
|
||||
pointer;
|
||||
|
||||
constructor(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,
|
||||
);
|
||||
}
|
||||
constructor(pointer) {
|
||||
this.pointer = pointer;
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
getBool(offset = 0) {
|
||||
return ops.op_ffi_read_bool(
|
||||
this.pointer,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
|
||||
class UnsafeFnPointer {
|
||||
pointer;
|
||||
definition;
|
||||
#structSize;
|
||||
getUint8(offset = 0) {
|
||||
return ops.op_ffi_read_u8(
|
||||
this.pointer,
|
||||
offset,
|
||||
);
|
||||
}
|
||||
|
||||
constructor(pointer, definition) {
|
||||
this.pointer = pointer;
|
||||
this.definition = definition;
|
||||
this.#structSize = isStruct(definition.result)
|
||||
? getTypeSizeAndAlignment(definition.result)[0]
|
||||
: null;
|
||||
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);
|
||||
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) {
|
||||
if (this.definition.nonblocking) {
|
||||
if (this.#structSize === null) {
|
||||
return core.opAsync(
|
||||
class UnsafeFnPointer {
|
||||
pointer;
|
||||
definition;
|
||||
#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",
|
||||
this.pointer,
|
||||
this.definition,
|
||||
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,
|
||||
);
|
||||
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,
|
||||
);
|
||||
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",
|
||||
),
|
||||
() => buffer,
|
||||
);
|
||||
}
|
||||
const { 0: rid, 1: pointer } = ops.op_ffi_unsafe_callback_create(
|
||||
definition,
|
||||
callback,
|
||||
} 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,
|
||||
);
|
||||
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;
|
||||
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);
|
||||
alignment = MathMax(alignment, fieldAlign);
|
||||
size = MathCeil(size / fieldAlign) * fieldAlign;
|
||||
size += fieldSize;
|
||||
}
|
||||
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 {
|
||||
#rid;
|
||||
symbols = {};
|
||||
class UnsafeCallback {
|
||||
#refcount;
|
||||
// Internal promise only meant to keep Deno from exiting
|
||||
#refpromise;
|
||||
#rid;
|
||||
definition;
|
||||
callback;
|
||||
pointer;
|
||||
|
||||
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;
|
||||
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(
|
||||
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 type = symbols[symbol].type;
|
||||
if (type === "void") {
|
||||
throw new TypeError(
|
||||
"Foreign symbol of type 'void' is not supported.",
|
||||
);
|
||||
}
|
||||
const name = symbols[symbol].name || symbol;
|
||||
const value = ops.op_ffi_get_static(
|
||||
this.#rid,
|
||||
name,
|
||||
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 value = ops.op_ffi_get_static(
|
||||
this.#rid,
|
||||
name,
|
||||
type,
|
||||
);
|
||||
ObjectDefineProperty(
|
||||
this.symbols,
|
||||
symbol,
|
||||
{
|
||||
configurable: false,
|
||||
enumerable: true,
|
||||
value,
|
||||
writable: false,
|
||||
const isNonBlocking = symbols[symbol].nonblocking;
|
||||
if (isNonBlocking) {
|
||||
ObjectDefineProperty(
|
||||
this.symbols,
|
||||
symbol,
|
||||
{
|
||||
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,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const resultType = symbols[symbol].result;
|
||||
const isStructResult = isStruct(resultType);
|
||||
const structSize = isStructResult
|
||||
? getTypeSizeAndAlignment(resultType)[0]
|
||||
: 0;
|
||||
const needsUnpacking = isReturnedAsBigInt(resultType);
|
||||
writable: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const isNonBlocking = symbols[symbol].nonblocking;
|
||||
if (isNonBlocking) {
|
||||
ObjectDefineProperty(
|
||||
this.symbols,
|
||||
symbol,
|
||||
{
|
||||
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 call = this.symbols[symbol];
|
||||
const parameters = symbols[symbol].parameters;
|
||||
const vi = new Int32Array(2);
|
||||
const vui = new Uint32Array(vi.buffer);
|
||||
const b = new BigInt64Array(vi.buffer);
|
||||
|
||||
if (needsUnpacking && !isNonBlocking) {
|
||||
const call = this.symbols[symbol];
|
||||
const parameters = symbols[symbol].parameters;
|
||||
const vi = new Int32Array(2);
|
||||
const vui = new Uint32Array(vi.buffer);
|
||||
const b = new BigInt64Array(vi.buffer);
|
||||
|
||||
const params = ArrayPrototypeJoin(
|
||||
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
||||
", ",
|
||||
);
|
||||
// Make sure V8 has no excuse to not optimize this function.
|
||||
this.symbols[symbol] = new Function(
|
||||
"vi",
|
||||
"vui",
|
||||
"b",
|
||||
"call",
|
||||
"NumberIsSafeInteger",
|
||||
"Number",
|
||||
`return function (${params}) {
|
||||
const params = ArrayPrototypeJoin(
|
||||
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
||||
", ",
|
||||
);
|
||||
// Make sure V8 has no excuse to not optimize this function.
|
||||
this.symbols[symbol] = new Function(
|
||||
"vi",
|
||||
"vui",
|
||||
"b",
|
||||
"call",
|
||||
"NumberIsSafeInteger",
|
||||
"Number",
|
||||
`return function (${params}) {
|
||||
call(${params}${parameters.length > 0 ? ", " : ""}vi);
|
||||
${
|
||||
isI64(resultType)
|
||||
? `const n1 = Number(b[0])`
|
||||
: `const n1 = vui[0] + 2 ** 32 * vui[1]` // Faster path for u64
|
||||
};
|
||||
isI64(resultType)
|
||||
? `const n1 = Number(b[0])`
|
||||
: `const n1 = vui[0] + 2 ** 32 * vui[1]` // Faster path for u64
|
||||
};
|
||||
if (NumberIsSafeInteger(n1)) return n1;
|
||||
return b[0];
|
||||
}`,
|
||||
)(vi, vui, b, call, NumberIsSafeInteger, Number);
|
||||
} else if (isStructResult && !isNonBlocking) {
|
||||
const call = this.symbols[symbol];
|
||||
const parameters = symbols[symbol].parameters;
|
||||
const params = ArrayPrototypeJoin(
|
||||
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
||||
", ",
|
||||
);
|
||||
this.symbols[symbol] = new Function(
|
||||
"call",
|
||||
`return function (${params}) {
|
||||
)(vi, vui, b, call, NumberIsSafeInteger, Number);
|
||||
} else if (isStructResult && !isNonBlocking) {
|
||||
const call = this.symbols[symbol];
|
||||
const parameters = symbols[symbol].parameters;
|
||||
const params = ArrayPrototypeJoin(
|
||||
ArrayPrototypeMap(parameters, (_, index) => `p${index}`),
|
||||
", ",
|
||||
);
|
||||
this.symbols[symbol] = new Function(
|
||||
"call",
|
||||
`return function (${params}) {
|
||||
const buffer = new Uint8Array(${structSize});
|
||||
call(${params}${parameters.length > 0 ? ", " : ""}buffer);
|
||||
return buffer;
|
||||
}`,
|
||||
)(call);
|
||||
}
|
||||
)(call);
|
||||
}
|
||||
}
|
||||
|
||||
close() {
|
||||
core.close(this.#rid);
|
||||
}
|
||||
}
|
||||
|
||||
function dlopen(path, symbols) {
|
||||
// URL support is progressively enhanced by util in `runtime/js`.
|
||||
const pathFromURL = __bootstrap.util.pathFromURL ?? ((p) => p);
|
||||
return new DynamicLibrary(pathFromURL(path), symbols);
|
||||
close() {
|
||||
core.close(this.#rid);
|
||||
}
|
||||
}
|
||||
|
||||
window.__bootstrap.ffi = {
|
||||
dlopen,
|
||||
UnsafeCallback,
|
||||
UnsafePointer,
|
||||
UnsafePointerView,
|
||||
UnsafeFnPointer,
|
||||
};
|
||||
})(this);
|
||||
function dlopen(path, symbols) {
|
||||
// TODO(@crowlKats): remove me
|
||||
// URL support is progressively enhanced by util in `runtime/js`.
|
||||
const pathFromURL = internals.pathFromURL ?? ((p) => p);
|
||||
return new DynamicLibrary(pathFromURL(path), symbols);
|
||||
}
|
||||
|
||||
export {
|
||||
dlopen,
|
||||
UnsafeCallback,
|
||||
UnsafeFnPointer,
|
||||
UnsafePointer,
|
||||
UnsafePointerView,
|
||||
};
|
||||
|
|
|
@ -84,7 +84,7 @@ pub(crate) struct FfiState {
|
|||
|
||||
pub fn init<P: FfiPermissions + 'static>(unstable: bool) -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/ffi",
|
||||
"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_http",
|
||||
])
|
||||
.js(deno_core::include_js_files!(
|
||||
.esm(deno_core::include_js_files!(
|
||||
prefix "internal:ext/flash",
|
||||
"01_http.js",
|
||||
))
|
||||
|
|
|
@ -1,296 +1,318 @@
|
|||
// 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 webidl = window.__bootstrap.webidl;
|
||||
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");
|
||||
const _deferred = Symbol("upgradeHttpDeferred");
|
||||
|
||||
const connErrorSymbol = Symbol("connError");
|
||||
const _deferred = Symbol("upgradeHttpDeferred");
|
||||
class HttpConn {
|
||||
#rid = 0;
|
||||
#closed = false;
|
||||
#remoteAddr;
|
||||
#localAddr;
|
||||
|
||||
class HttpConn {
|
||||
#rid = 0;
|
||||
#closed = false;
|
||||
#remoteAddr;
|
||||
#localAddr;
|
||||
// This set holds resource ids of resources
|
||||
// that were created during lifecycle of this request.
|
||||
// When the connection is closed these resources should be closed
|
||||
// as well.
|
||||
managedResources = new Set();
|
||||
|
||||
// This set holds resource ids of resources
|
||||
// that were created during lifecycle of this request.
|
||||
// When the connection is closed these resources should be closed
|
||||
// as well.
|
||||
managedResources = new Set();
|
||||
constructor(rid, remoteAddr, localAddr) {
|
||||
this.#rid = rid;
|
||||
this.#remoteAddr = remoteAddr;
|
||||
this.#localAddr = localAddr;
|
||||
}
|
||||
|
||||
constructor(rid, remoteAddr, localAddr) {
|
||||
this.#rid = rid;
|
||||
this.#remoteAddr = remoteAddr;
|
||||
this.#localAddr = localAddr;
|
||||
}
|
||||
/** @returns {number} */
|
||||
get rid() {
|
||||
return this.#rid;
|
||||
}
|
||||
|
||||
/** @returns {number} */
|
||||
get rid() {
|
||||
return this.#rid;
|
||||
}
|
||||
|
||||
/** @returns {Promise<RequestEvent | null>} */
|
||||
async nextRequest() {
|
||||
let nextRequest;
|
||||
try {
|
||||
nextRequest = await core.opAsync("op_http_accept", this.#rid);
|
||||
} catch (error) {
|
||||
this.close();
|
||||
// A connection error seen here would cause disrupted responses to throw
|
||||
// a generic `BadResource` error. Instead store this error and replace
|
||||
// those with it.
|
||||
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();
|
||||
/** @returns {Promise<RequestEvent | null>} */
|
||||
async nextRequest() {
|
||||
let nextRequest;
|
||||
try {
|
||||
nextRequest = await core.opAsync("op_http_accept", this.#rid);
|
||||
} catch (error) {
|
||||
this.close();
|
||||
// A connection error seen here would cause disrupted responses to throw
|
||||
// a generic `BadResource` error. Instead store this error and replace
|
||||
// those with it.
|
||||
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;
|
||||
|
||||
const { 0: streamRid, 1: method, 2: url } = nextRequest;
|
||||
SetPrototypeAdd(this.managedResources, streamRid);
|
||||
|
||||
/** @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 };
|
||||
this.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @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);
|
||||
}
|
||||
}
|
||||
const { 0: streamRid, 1: method, 2: url } = nextRequest;
|
||||
SetPrototypeAdd(this.managedResources, streamRid);
|
||||
|
||||
/** @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);
|
||||
}
|
||||
|
||||
[SymbolAsyncIterator]() {
|
||||
// deno-lint-ignore no-this-alias
|
||||
const httpConn = this;
|
||||
return {
|
||||
async next() {
|
||||
const reqEvt = await httpConn.nextRequest();
|
||||
// Change with caution, current form avoids a v8 deopt
|
||||
return { value: reqEvt ?? undefined, done: reqEvt === null };
|
||||
},
|
||||
};
|
||||
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} */
|
||||
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(
|
||||
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.",
|
||||
);
|
||||
[SymbolAsyncIterator]() {
|
||||
// deno-lint-ignore no-this-alias
|
||||
const httpConn = this;
|
||||
return {
|
||||
async next() {
|
||||
const reqEvt = await httpConn.nextRequest();
|
||||
// Change with caution, current form avoids a v8 deopt
|
||||
return { value: reqEvt ?? undefined, done: reqEvt === null };
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
|
||||
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 (
|
||||
ObjectPrototypeIsPrototypeOf(
|
||||
ReadableStreamPrototype,
|
||||
innerResp.body.streamOrStatic,
|
||||
)
|
||||
) {
|
||||
if (
|
||||
innerResp.body.length === null ||
|
||||
ObjectPrototypeIsPrototypeOf(
|
||||
ReadableStreamPrototype,
|
||||
innerResp.body.streamOrStatic,
|
||||
BlobPrototype,
|
||||
innerResp.body.source,
|
||||
)
|
||||
) {
|
||||
if (
|
||||
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");
|
||||
}
|
||||
}
|
||||
respBody = innerResp.body.stream;
|
||||
} else {
|
||||
innerResp.body.streamOrStatic.consumed = true;
|
||||
respBody = innerResp.body.streamOrStatic.body;
|
||||
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 {
|
||||
respBody = new Uint8Array(0);
|
||||
innerResp.body.streamOrStatic.consumed = true;
|
||||
respBody = innerResp.body.streamOrStatic.body;
|
||||
}
|
||||
const isStreamingResponseBody = !(
|
||||
typeof respBody === "string" ||
|
||||
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, respBody)
|
||||
} else {
|
||||
respBody = new Uint8Array(0);
|
||||
}
|
||||
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 {
|
||||
await core.opAsync(
|
||||
"op_http_write_headers",
|
||||
streamRid,
|
||||
innerResp.status ?? 200,
|
||||
innerResp.headerList,
|
||||
isStreamingResponseBody ? null : respBody,
|
||||
);
|
||||
} 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;
|
||||
} 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 (isStreamingResponseBody) {
|
||||
let success = false;
|
||||
if (
|
||||
respBody === null ||
|
||||
!ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
|
||||
) {
|
||||
throw new TypeError("Unreachable");
|
||||
if (isStreamingResponseBody) {
|
||||
let success = false;
|
||||
if (
|
||||
respBody === null ||
|
||||
!ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, respBody)
|
||||
) {
|
||||
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);
|
||||
let reader;
|
||||
if (resourceBacking) {
|
||||
if (respBody.locked) {
|
||||
throw new TypeError("ReadableStream is locked.");
|
||||
reader = respBody.getReader(); // Aquire JS lock.
|
||||
try {
|
||||
await core.opAsync(
|
||||
"op_http_write_resource",
|
||||
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 {
|
||||
await core.opAsync(
|
||||
"op_http_write_resource",
|
||||
streamRid,
|
||||
resourceBacking.rid,
|
||||
);
|
||||
if (resourceBacking.autoClose) core.tryClose(resourceBacking.rid);
|
||||
readableStreamClose(respBody); // Release JS lock.
|
||||
success = true;
|
||||
await core.opAsync("op_http_write", streamRid, value);
|
||||
} catch (error) {
|
||||
const connError = httpConn[connErrorSymbol];
|
||||
if (
|
||||
|
@ -303,176 +325,147 @@
|
|||
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;
|
||||
}
|
||||
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 (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");
|
||||
if (success) {
|
||||
try {
|
||||
await core.opAsync("op_http_shutdown", streamRid);
|
||||
} catch (error) {
|
||||
await reader.cancel(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
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 = {}) {
|
||||
const upgrade = request.headers.get("upgrade");
|
||||
const upgradeHasWebSocketOption = upgrade !== null &&
|
||||
ArrayPrototypeSome(
|
||||
StringPrototypeSplit(upgrade, /\s*,\s*/),
|
||||
(option) => StringPrototypeToLowerCase(option) === "websocket",
|
||||
);
|
||||
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)`,
|
||||
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 response = fromInnerResponse(r, "immutable");
|
||||
const _ws = Symbol("[[associated_ws]]");
|
||||
|
||||
const socket = webidl.createBranded(WebSocket);
|
||||
setEventTargetData(socket);
|
||||
socket[_server] = true;
|
||||
response[_ws] = socket;
|
||||
socket[_idleTimeoutDuration] = options.idleTimeout ?? 120;
|
||||
socket[_idleTimeoutTimeout] = null;
|
||||
|
||||
return { response, socket };
|
||||
function upgradeWebSocket(request, options = {}) {
|
||||
const upgrade = request.headers.get("upgrade");
|
||||
const upgradeHasWebSocketOption = upgrade !== null &&
|
||||
ArrayPrototypeSome(
|
||||
StringPrototypeSplit(upgrade, /\s*,\s*/),
|
||||
(option) => StringPrototypeToLowerCase(option) === "websocket",
|
||||
);
|
||||
if (!upgradeHasWebSocketOption) {
|
||||
throw new TypeError(
|
||||
"Invalid Header: 'upgrade' header must contain 'websocket'",
|
||||
);
|
||||
}
|
||||
|
||||
function upgradeHttp(req) {
|
||||
if (req[_flash]) {
|
||||
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(
|
||||
"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 = {
|
||||
HttpConn,
|
||||
upgradeWebSocket,
|
||||
upgradeHttp,
|
||||
_ws,
|
||||
};
|
||||
})(this);
|
||||
const response = fromInnerResponse(r, "immutable");
|
||||
|
||||
const socket = webidl.createBranded(WebSocket);
|
||||
setEventTargetData(socket);
|
||||
socket[_server] = true;
|
||||
response[_ws] = socket;
|
||||
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 {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_web", "deno_net", "deno_fetch", "deno_websocket"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/http",
|
||||
"01_http.js",
|
||||
))
|
||||
|
|
|
@ -1,423 +1,421 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const { BadResourcePrototype, InterruptedPrototype, ops } = core;
|
||||
const {
|
||||
readableStreamForRidUnrefable,
|
||||
readableStreamForRidUnrefableRef,
|
||||
readableStreamForRidUnrefableUnref,
|
||||
writableStreamForRid,
|
||||
} = window.__bootstrap.streams;
|
||||
const {
|
||||
Error,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
PromiseResolve,
|
||||
SymbolAsyncIterator,
|
||||
SymbolFor,
|
||||
TypedArrayPrototypeSubarray,
|
||||
TypeError,
|
||||
Uint8Array,
|
||||
} = window.__bootstrap.primordials;
|
||||
const core = globalThis.Deno.core;
|
||||
const { BadResourcePrototype, InterruptedPrototype, ops } = core;
|
||||
import {
|
||||
readableStreamForRidUnrefable,
|
||||
readableStreamForRidUnrefableRef,
|
||||
readableStreamForRidUnrefableUnref,
|
||||
writableStreamForRid,
|
||||
} from "internal:ext/web/06_streams.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
Error,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
PromiseResolve,
|
||||
SymbolAsyncIterator,
|
||||
SymbolFor,
|
||||
TypedArrayPrototypeSubarray,
|
||||
TypeError,
|
||||
Uint8Array,
|
||||
} = primordials;
|
||||
|
||||
const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
|
||||
const promiseIdSymbol = SymbolFor("Deno.core.internalPromiseId");
|
||||
|
||||
async function write(rid, data) {
|
||||
return await core.write(rid, data);
|
||||
async function 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) {
|
||||
return core.shutdown(rid);
|
||||
get rid() {
|
||||
return this.#rid;
|
||||
}
|
||||
|
||||
function resolveDns(query, recordType, options) {
|
||||
return core.opAsync("op_dns_resolve", { query, recordType, options });
|
||||
get remoteAddr() {
|
||||
return this.#remoteAddr;
|
||||
}
|
||||
|
||||
class Conn {
|
||||
#rid = 0;
|
||||
#remoteAddr = null;
|
||||
#localAddr = null;
|
||||
#unref = false;
|
||||
#pendingReadPromiseIds = [];
|
||||
get localAddr() {
|
||||
return this.#localAddr;
|
||||
}
|
||||
|
||||
#readable;
|
||||
#writable;
|
||||
write(p) {
|
||||
return write(this.rid, p);
|
||||
}
|
||||
|
||||
constructor(rid, remoteAddr, localAddr) {
|
||||
this.#rid = rid;
|
||||
this.#remoteAddr = remoteAddr;
|
||||
this.#localAddr = localAddr;
|
||||
async read(buffer) {
|
||||
if (buffer.length === 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
get rid() {
|
||||
return this.#rid;
|
||||
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;
|
||||
}
|
||||
|
||||
get remoteAddr() {
|
||||
return this.#remoteAddr;
|
||||
}
|
||||
close() {
|
||||
core.close(this.rid);
|
||||
}
|
||||
|
||||
get localAddr() {
|
||||
return this.#localAddr;
|
||||
}
|
||||
closeWrite() {
|
||||
return shutdown(this.rid);
|
||||
}
|
||||
|
||||
write(p) {
|
||||
return write(this.rid, p);
|
||||
}
|
||||
|
||||
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) {
|
||||
get readable() {
|
||||
if (this.#readable === undefined) {
|
||||
this.#readable = readableStreamForRidUnrefable(this.rid);
|
||||
if (this.#unref) {
|
||||
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 {
|
||||
setNoDelay(noDelay = true) {
|
||||
return ops.op_set_nodelay(this.rid, noDelay);
|
||||
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 };
|
||||
}
|
||||
|
||||
setKeepAlive(keepAlive = true) {
|
||||
return ops.op_set_keepalive(this.rid, keepAlive);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
unref() {
|
||||
this.#unref = true;
|
||||
if (typeof this.#promiseId === "number") {
|
||||
core.unrefOp(this.#promiseId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Datagram {
|
||||
#rid = 0;
|
||||
#addr = null;
|
||||
class Datagram {
|
||||
#rid = 0;
|
||||
#addr = null;
|
||||
|
||||
constructor(rid, addr, bufSize = 1024) {
|
||||
this.#rid = rid;
|
||||
this.#addr = addr;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
constructor(rid, addr, bufSize = 1024) {
|
||||
this.#rid = rid;
|
||||
this.#addr = addr;
|
||||
this.bufSize = bufSize;
|
||||
}
|
||||
|
||||
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);
|
||||
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 "unix": {
|
||||
const { 0: rid, 1: path } = ops.op_net_listen_unix(args.path);
|
||||
const addr = {
|
||||
transport: "unix",
|
||||
path,
|
||||
};
|
||||
return new Listener(rid, addr);
|
||||
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 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) {
|
||||
return function listenDatagram(args) {
|
||||
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}'`);
|
||||
}
|
||||
};
|
||||
close() {
|
||||
core.close(this.rid);
|
||||
}
|
||||
|
||||
async function connect(args) {
|
||||
switch (args.transport ?? "tcp") {
|
||||
case "tcp": {
|
||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
||||
"op_net_connect_tcp",
|
||||
async *[SymbolAsyncIterator]() {
|
||||
while (true) {
|
||||
try {
|
||||
yield await this.receive();
|
||||
} 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",
|
||||
port: args.port,
|
||||
},
|
||||
args.reuseAddress ?? false,
|
||||
);
|
||||
localAddr.transport = "tcp";
|
||||
remoteAddr.transport = "tcp";
|
||||
return new TcpConn(rid, remoteAddr, localAddr);
|
||||
addr.transport = "udp";
|
||||
return new Datagram(rid, addr);
|
||||
}
|
||||
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 },
|
||||
);
|
||||
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}'`);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const { Listener, Conn } = window.__bootstrap.net;
|
||||
const { TypeError } = window.__bootstrap.primordials;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
import { Conn, Listener } from "internal:ext/net/01_net.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const { TypeError } = primordials;
|
||||
|
||||
function opStartTls(args) {
|
||||
return core.opAsync("op_tls_start", args);
|
||||
function opStartTls(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) {
|
||||
return core.opAsync("op_tls_handshake", 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(
|
||||
"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 {
|
||||
handshake() {
|
||||
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}'`);
|
||||
}
|
||||
class TlsListener extends Listener {
|
||||
async accept() {
|
||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
||||
"op_net_connect_tls",
|
||||
{ hostname, port },
|
||||
{ certFile, caCerts, certChain, privateKey, alpnProtocols },
|
||||
"op_net_accept_tls",
|
||||
this.rid,
|
||||
);
|
||||
localAddr.transport = "tcp";
|
||||
remoteAddr.transport = "tcp";
|
||||
return new TlsConn(rid, remoteAddr, localAddr);
|
||||
}
|
||||
}
|
||||
|
||||
class TlsListener extends Listener {
|
||||
async accept() {
|
||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await core.opAsync(
|
||||
"op_net_accept_tls",
|
||||
this.rid,
|
||||
);
|
||||
localAddr.transport = "tcp";
|
||||
remoteAddr.transport = "tcp";
|
||||
return new TlsConn(rid, remoteAddr, localAddr);
|
||||
}
|
||||
function listenTls({
|
||||
port,
|
||||
cert,
|
||||
certFile,
|
||||
key,
|
||||
keyFile,
|
||||
hostname = "0.0.0.0",
|
||||
transport = "tcp",
|
||||
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({
|
||||
port,
|
||||
cert,
|
||||
certFile,
|
||||
key,
|
||||
keyFile,
|
||||
hostname = "0.0.0.0",
|
||||
transport = "tcp",
|
||||
async function startTls(
|
||||
conn,
|
||||
{
|
||||
hostname = "127.0.0.1",
|
||||
certFile = undefined,
|
||||
caCerts = [],
|
||||
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);
|
||||
}
|
||||
} = {},
|
||||
) {
|
||||
const { 0: rid, 1: localAddr, 2: remoteAddr } = await opStartTls({
|
||||
rid: conn.rid,
|
||||
hostname,
|
||||
certFile,
|
||||
caCerts,
|
||||
alpnProtocols,
|
||||
});
|
||||
return new TlsConn(rid, remoteAddr, localAddr);
|
||||
}
|
||||
|
||||
async function startTls(
|
||||
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);
|
||||
export { connectTls, listenTls, startTls, TlsConn, TlsListener };
|
||||
|
|
|
@ -86,7 +86,7 @@ pub fn init<P: NetPermissions + 'static>(
|
|||
ops.extend(ops_tls::init::<P>());
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_web"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/net",
|
||||
"01_net.js",
|
||||
"02_tls.js",
|
||||
|
|
|
@ -2,127 +2,122 @@
|
|||
|
||||
// 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) => {
|
||||
const {
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeFilter,
|
||||
ObjectEntries,
|
||||
ObjectCreate,
|
||||
ObjectDefineProperty,
|
||||
Proxy,
|
||||
ReflectDefineProperty,
|
||||
ReflectGetOwnPropertyDescriptor,
|
||||
ReflectOwnKeys,
|
||||
Set,
|
||||
SetPrototypeHas,
|
||||
} = window.__bootstrap.primordials;
|
||||
|
||||
function assert(cond) {
|
||||
if (!cond) {
|
||||
throw Error("assert");
|
||||
}
|
||||
function assert(cond) {
|
||||
if (!cond) {
|
||||
throw Error("assert");
|
||||
}
|
||||
}
|
||||
|
||||
let initialized = false;
|
||||
const nodeGlobals = {};
|
||||
const nodeGlobalThis = new Proxy(globalThis, {
|
||||
get(_target, prop, _receiver) {
|
||||
if (prop in nodeGlobals) {
|
||||
return nodeGlobals[prop];
|
||||
} else {
|
||||
return globalThis[prop];
|
||||
}
|
||||
},
|
||||
set(_target, prop, value) {
|
||||
if (prop in nodeGlobals) {
|
||||
nodeGlobals[prop] = value;
|
||||
} else {
|
||||
globalThis[prop] = value;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
deleteProperty(_target, prop) {
|
||||
let success = false;
|
||||
if (prop in nodeGlobals) {
|
||||
delete nodeGlobals[prop];
|
||||
success = true;
|
||||
}
|
||||
if (prop in globalThis) {
|
||||
delete globalThis[prop];
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
},
|
||||
ownKeys(_target) {
|
||||
const globalThisKeys = ReflectOwnKeys(globalThis);
|
||||
const nodeGlobalsKeys = ReflectOwnKeys(nodeGlobals);
|
||||
const nodeGlobalsKeySet = new Set(nodeGlobalsKeys);
|
||||
return [
|
||||
...ArrayPrototypeFilter(
|
||||
globalThisKeys,
|
||||
(k) => !SetPrototypeHas(nodeGlobalsKeySet, k),
|
||||
),
|
||||
...nodeGlobalsKeys,
|
||||
];
|
||||
},
|
||||
defineProperty(_target, prop, desc) {
|
||||
if (prop in nodeGlobals) {
|
||||
return ReflectDefineProperty(nodeGlobals, prop, desc);
|
||||
} else {
|
||||
return ReflectDefineProperty(globalThis, prop, desc);
|
||||
}
|
||||
},
|
||||
getOwnPropertyDescriptor(_target, prop) {
|
||||
if (prop in nodeGlobals) {
|
||||
return ReflectGetOwnPropertyDescriptor(nodeGlobals, prop);
|
||||
} else {
|
||||
return ReflectGetOwnPropertyDescriptor(globalThis, prop);
|
||||
}
|
||||
},
|
||||
has(_target, prop) {
|
||||
return prop in nodeGlobals || prop in globalThis;
|
||||
},
|
||||
let initialized = false;
|
||||
const nodeGlobals = {};
|
||||
const nodeGlobalThis = new Proxy(globalThis, {
|
||||
get(_target, prop, _receiver) {
|
||||
if (prop in nodeGlobals) {
|
||||
return nodeGlobals[prop];
|
||||
} else {
|
||||
return globalThis[prop];
|
||||
}
|
||||
},
|
||||
set(_target, prop, value) {
|
||||
if (prop in nodeGlobals) {
|
||||
nodeGlobals[prop] = value;
|
||||
} else {
|
||||
globalThis[prop] = value;
|
||||
}
|
||||
return true;
|
||||
},
|
||||
deleteProperty(_target, prop) {
|
||||
let success = false;
|
||||
if (prop in nodeGlobals) {
|
||||
delete nodeGlobals[prop];
|
||||
success = true;
|
||||
}
|
||||
if (prop in globalThis) {
|
||||
delete globalThis[prop];
|
||||
success = true;
|
||||
}
|
||||
return success;
|
||||
},
|
||||
ownKeys(_target) {
|
||||
const globalThisKeys = ReflectOwnKeys(globalThis);
|
||||
const nodeGlobalsKeys = ReflectOwnKeys(nodeGlobals);
|
||||
const nodeGlobalsKeySet = new Set(nodeGlobalsKeys);
|
||||
return [
|
||||
...ArrayPrototypeFilter(
|
||||
globalThisKeys,
|
||||
(k) => !SetPrototypeHas(nodeGlobalsKeySet, k),
|
||||
),
|
||||
...nodeGlobalsKeys,
|
||||
];
|
||||
},
|
||||
defineProperty(_target, prop, desc) {
|
||||
if (prop in nodeGlobals) {
|
||||
return ReflectDefineProperty(nodeGlobals, prop, desc);
|
||||
} else {
|
||||
return ReflectDefineProperty(globalThis, prop, desc);
|
||||
}
|
||||
},
|
||||
getOwnPropertyDescriptor(_target, prop) {
|
||||
if (prop in nodeGlobals) {
|
||||
return ReflectGetOwnPropertyDescriptor(nodeGlobals, prop);
|
||||
} else {
|
||||
return ReflectGetOwnPropertyDescriptor(globalThis, prop);
|
||||
}
|
||||
},
|
||||
has(_target, prop) {
|
||||
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);
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
window.__bootstrap.internals = {
|
||||
...window.__bootstrap.internals ?? {},
|
||||
node: {
|
||||
globalThis: nodeGlobalThis,
|
||||
initialize,
|
||||
nativeModuleExports,
|
||||
builtinModules,
|
||||
},
|
||||
};
|
||||
})(globalThis);
|
||||
internals.node = {
|
||||
globalThis: nodeGlobalThis,
|
||||
initialize,
|
||||
nativeModuleExports,
|
||||
builtinModules,
|
||||
};
|
||||
|
|
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>>,
|
||||
) -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/node",
|
||||
"01_node.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="./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 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");
|
||||
|
||||
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
|
||||
* @property {Component} protocol
|
||||
* @property {Component} username
|
||||
* @property {Component} password
|
||||
* @property {Component} hostname
|
||||
* @property {Component} port
|
||||
* @property {Component} pathname
|
||||
* @property {Component} search
|
||||
* @property {Component} hash
|
||||
* @param {URLPatternInput} input
|
||||
* @param {string} [baseURL]
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef Component
|
||||
* @property {string} patternString
|
||||
* @property {RegExp} regexp
|
||||
* @property {string[]} groupNameList
|
||||
*/
|
||||
|
||||
class URLPattern {
|
||||
/** @type {Components} */
|
||||
[_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, {
|
||||
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,
|
||||
context: "Argument 1",
|
||||
});
|
||||
if (baseURL !== undefined) {
|
||||
baseURL = webidl.converters.USVString(baseURL, {
|
||||
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() {
|
||||
webidl.assertBranded(this, URLPatternPrototype);
|
||||
return this[_components].protocol.patternString;
|
||||
}
|
||||
const components = ops.op_urlpattern_parse(input, baseURL);
|
||||
|
||||
get username() {
|
||||
webidl.assertBranded(this, URLPatternPrototype);
|
||||
return this[_components].username.patternString;
|
||||
}
|
||||
|
||||
get password() {
|
||||
webidl.assertBranded(this, URLPatternPrototype);
|
||||
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 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",
|
||||
);
|
||||
const groups = ObjectFromEntries(groupEntries);
|
||||
result[key] = {
|
||||
input,
|
||||
groups,
|
||||
};
|
||||
} catch (e) {
|
||||
throw new TypeError(`${prefix}: ${key} is invalid; ${e.message}`);
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}`;
|
||||
}
|
||||
this[_components] = components;
|
||||
}
|
||||
|
||||
webidl.configurePrototype(URLPattern);
|
||||
const URLPatternPrototype = URLPattern.prototype;
|
||||
get protocol() {
|
||||
webidl.assertBranded(this, URLPatternPrototype);
|
||||
return this[_components].protocol.patternString;
|
||||
}
|
||||
|
||||
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 },
|
||||
]);
|
||||
get username() {
|
||||
webidl.assertBranded(this, URLPatternPrototype);
|
||||
return this[_components].username.patternString;
|
||||
}
|
||||
|
||||
webidl.converters["URLPatternInput"] = (V, opts) => {
|
||||
// Union for (URLPatternInit or USVString)
|
||||
if (typeof V == "object") {
|
||||
return webidl.converters.URLPatternInit(V, opts);
|
||||
get password() {
|
||||
webidl.assertBranded(this, URLPatternPrototype);
|
||||
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",
|
||||
});
|
||||
}
|
||||
return webidl.converters.USVString(V, opts);
|
||||
};
|
||||
|
||||
window.__bootstrap.urlPattern = {
|
||||
URLPattern,
|
||||
};
|
||||
})(globalThis);
|
||||
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);
|
||||
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_url::init(),
|
||||
Extension::builder("bench_setup")
|
||||
.js(vec![(
|
||||
"setup",
|
||||
"const { URL } = globalThis.__bootstrap.url;",
|
||||
.esm(vec![(
|
||||
"internal:setup",
|
||||
r#"import { URL } from "internal:ext/url/00_url.js";
|
||||
globalThis.URL = URL;
|
||||
"#,
|
||||
)])
|
||||
.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.
|
||||
|
||||
// deno-lint-ignore-file no-var
|
||||
|
||||
/// <reference no-default-lib="true" />
|
||||
/// <reference lib="esnext" />
|
||||
|
||||
declare namespace globalThis {
|
||||
declare namespace __bootstrap {
|
||||
declare var url: {
|
||||
URL: typeof URL;
|
||||
URLSearchParams: typeof URLSearchParams;
|
||||
parseUrlEncoded(bytes: Uint8Array): [string, string][];
|
||||
};
|
||||
|
||||
declare var urlPattern: {
|
||||
URLPattern: typeof URLPattern;
|
||||
};
|
||||
}
|
||||
declare module "internal:ext/url/00_url.js" {
|
||||
const URL: typeof URL;
|
||||
const URLSearchParams: typeof URLSearchParams;
|
||||
function parseUrlEncoded(bytes: Uint8Array): [string, string][];
|
||||
}
|
||||
|
||||
declare module "internal:ext/url/01_urlpattern.js" {
|
||||
const URLPattern: typeof URLPattern;
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ use crate::urlpattern::op_urlpattern_process_match_input;
|
|||
pub fn init() -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_webidl"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/url",
|
||||
"00_url.js",
|
||||
"01_urlpattern.js",
|
||||
|
|
|
@ -6,334 +6,331 @@
|
|||
/// <reference path="../web/internal.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 core = Deno.core;
|
||||
const ops = core.ops;
|
||||
const {
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypeMap,
|
||||
Error,
|
||||
JSONStringify,
|
||||
NumberPrototypeToString,
|
||||
RegExp,
|
||||
SafeArrayIterator,
|
||||
String,
|
||||
StringPrototypeCharAt,
|
||||
StringPrototypeCharCodeAt,
|
||||
StringPrototypeMatch,
|
||||
StringPrototypePadStart,
|
||||
StringPrototypeReplace,
|
||||
StringPrototypeSlice,
|
||||
StringPrototypeSubstring,
|
||||
StringPrototypeToLowerCase,
|
||||
StringPrototypeToUpperCase,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
const ASCII_DIGIT = ["\u0030-\u0039"];
|
||||
const ASCII_UPPER_ALPHA = ["\u0041-\u005A"];
|
||||
const ASCII_LOWER_ALPHA = ["\u0061-\u007A"];
|
||||
const ASCII_ALPHA = [
|
||||
...new SafeArrayIterator(ASCII_UPPER_ALPHA),
|
||||
...new SafeArrayIterator(ASCII_LOWER_ALPHA),
|
||||
];
|
||||
const ASCII_ALPHANUMERIC = [
|
||||
...new SafeArrayIterator(ASCII_DIGIT),
|
||||
...new SafeArrayIterator(ASCII_ALPHA),
|
||||
];
|
||||
|
||||
const ASCII_DIGIT = ["\u0030-\u0039"];
|
||||
const ASCII_UPPER_ALPHA = ["\u0041-\u005A"];
|
||||
const ASCII_LOWER_ALPHA = ["\u0061-\u007A"];
|
||||
const ASCII_ALPHA = [
|
||||
...new SafeArrayIterator(ASCII_UPPER_ALPHA),
|
||||
...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_WHITESPACE = [
|
||||
"\u000A",
|
||||
"\u000D",
|
||||
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
||||
];
|
||||
|
||||
const HTTP_TAB_OR_SPACE = ["\u0009", "\u0020"];
|
||||
const HTTP_WHITESPACE = [
|
||||
"\u000A",
|
||||
"\u000D",
|
||||
...new SafeArrayIterator(HTTP_TAB_OR_SPACE),
|
||||
];
|
||||
const HTTP_TOKEN_CODE_POINT = [
|
||||
"\u0021",
|
||||
"\u0023",
|
||||
"\u0024",
|
||||
"\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",
|
||||
"\u0023",
|
||||
"\u0024",
|
||||
"\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",
|
||||
);
|
||||
|
||||
/**
|
||||
* 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",
|
||||
/**
|
||||
* 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",
|
||||
);
|
||||
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;
|
||||
}
|
||||
// 5.5.2.
|
||||
value += input[position];
|
||||
// 5.5.3.
|
||||
position++;
|
||||
} else { // 5.6.
|
||||
// 5.6.1
|
||||
if (quoteOrBackslash !== "\u0022") throw new TypeError('must be "');
|
||||
// 5.6.2
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
// 6.
|
||||
if (extractValue) return { result: value, position };
|
||||
// 7.
|
||||
return {
|
||||
result: StringPrototypeSubstring(input, positionStart, position + 1),
|
||||
position,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
// 5.5.2.
|
||||
value += input[position];
|
||||
// 5.5.3.
|
||||
position++;
|
||||
} else { // 5.6.
|
||||
// 5.6.1
|
||||
if (quoteOrBackslash !== "\u0022") throw new TypeError('must be "');
|
||||
// 5.6.2
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
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,
|
||||
// 6.
|
||||
if (extractValue) return { result: value, position };
|
||||
// 7.
|
||||
return {
|
||||
result: StringPrototypeSubstring(input, positionStart, position + 1),
|
||||
position,
|
||||
};
|
||||
})(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/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 {
|
||||
ArrayPrototypeSlice,
|
||||
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");
|
||||
const _message = Symbol("message");
|
||||
const _code = Symbol("code");
|
||||
|
||||
const _name = Symbol("name");
|
||||
const _message = Symbol("message");
|
||||
const _code = Symbol("code");
|
||||
// Defined in WebIDL 4.3.
|
||||
// https://webidl.spec.whatwg.org/#idl-DOMException
|
||||
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.
|
||||
// https://webidl.spec.whatwg.org/#idl-DOMException
|
||||
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 2.8.1.
|
||||
// https://webidl.spec.whatwg.org/#dfn-error-names-table
|
||||
/** @type {Record<string, number>} */
|
||||
// the prototype should be null, to prevent user code from looking
|
||||
// up Object.prototype properties, such as "toString"
|
||||
const nameToCodeMapping = ObjectCreate(null, {
|
||||
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 2.8.1.
|
||||
// https://webidl.spec.whatwg.org/#dfn-error-names-table
|
||||
/** @type {Record<string, number>} */
|
||||
// the prototype should be null, to prevent user code from looking
|
||||
// up Object.prototype properties, such as "toString"
|
||||
const nameToCodeMapping = ObjectCreate(null, {
|
||||
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/#idl-DOMException
|
||||
class DOMException {
|
||||
[_message];
|
||||
[_name];
|
||||
[_code];
|
||||
|
||||
// Defined in WebIDL 4.3.
|
||||
// https://webidl.spec.whatwg.org/#idl-DOMException
|
||||
class DOMException {
|
||||
[_message];
|
||||
[_name];
|
||||
[_code];
|
||||
// https://webidl.spec.whatwg.org/#dom-domexception-domexception
|
||||
constructor(message = "", name = "Error") {
|
||||
message = webidl.converters.DOMString(message, {
|
||||
prefix: "Failed to construct 'DOMException'",
|
||||
context: "Argument 1",
|
||||
});
|
||||
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
|
||||
constructor(message = "", name = "Error") {
|
||||
message = webidl.converters.DOMString(message, {
|
||||
prefix: "Failed to construct 'DOMException'",
|
||||
context: "Argument 1",
|
||||
});
|
||||
name = webidl.converters.DOMString(name, {
|
||||
prefix: "Failed to construct 'DOMException'",
|
||||
context: "Argument 2",
|
||||
});
|
||||
const code = nameToCodeMapping[name] ?? 0;
|
||||
this[_message] = message;
|
||||
this[_name] = name;
|
||||
this[_code] = code;
|
||||
this[webidl.brand] = webidl.brand;
|
||||
|
||||
this[_message] = message;
|
||||
this[_name] = name;
|
||||
this[_code] = code;
|
||||
this[webidl.brand] = webidl.brand;
|
||||
const error = new Error(message);
|
||||
error.name = "DOMException";
|
||||
ObjectDefineProperty(this, "stack", {
|
||||
value: error.stack,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
|
||||
const error = new Error(message);
|
||||
error.name = "DOMException";
|
||||
ObjectDefineProperty(this, "stack", {
|
||||
value: error.stack,
|
||||
writable: 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",
|
||||
],
|
||||
}));
|
||||
}
|
||||
}
|
||||
// `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,
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
get message() {
|
||||
webidl.assertBranded(this, DOMExceptionPrototype);
|
||||
return this[_message];
|
||||
}
|
||||
|
||||
window.__bootstrap.domException = { DOMException };
|
||||
})(this);
|
||||
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(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/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 {
|
||||
ArrayPrototypeIncludes,
|
||||
Map,
|
||||
MapPrototypeGet,
|
||||
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
|
||||
* @property {string} type
|
||||
* @property {string} subtype
|
||||
* @property {Map<string,string>} parameters
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef MimeType
|
||||
* @property {string} type
|
||||
* @property {string} subtype
|
||||
* @property {Map<string,string>} parameters
|
||||
*/
|
||||
/**
|
||||
* @param {string} input
|
||||
* @returns {MimeType | null}
|
||||
*/
|
||||
function parseMimeType(input) {
|
||||
// 1.
|
||||
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_PREFIX_RE, "");
|
||||
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_SUFFIX_RE, "");
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @returns {MimeType | null}
|
||||
*/
|
||||
function parseMimeType(input) {
|
||||
// 1.
|
||||
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_PREFIX_RE, "");
|
||||
input = StringPrototypeReplaceAll(input, HTTP_WHITESPACE_SUFFIX_RE, "");
|
||||
// 2.
|
||||
let position = 0;
|
||||
const endOfInput = input.length;
|
||||
|
||||
// 2.
|
||||
let position = 0;
|
||||
const endOfInput = input.length;
|
||||
// 3.
|
||||
const res1 = collectSequenceOfCodepoints(
|
||||
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(
|
||||
input,
|
||||
position,
|
||||
(c) => c != "\u002F",
|
||||
(c) => ArrayPrototypeIncludes(HTTP_WHITESPACE, c),
|
||||
);
|
||||
const type = res1.result;
|
||||
position = res1.position;
|
||||
|
||||
// 4.
|
||||
if (type === "" || !RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, type)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 5.
|
||||
if (position >= endOfInput) return null;
|
||||
|
||||
// 6.
|
||||
position++;
|
||||
|
||||
// 7.
|
||||
// 11.3.
|
||||
const res2 = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => c != "\u003B",
|
||||
(c) => c !== "\u003B" && c !== "\u003D",
|
||||
);
|
||||
let subtype = res2.result;
|
||||
let parameterName = res2.result;
|
||||
position = res2.position;
|
||||
|
||||
// 8.
|
||||
subtype = StringPrototypeReplaceAll(subtype, HTTP_WHITESPACE_SUFFIX_RE, "");
|
||||
// 11.4.
|
||||
parameterName = StringPrototypeToLowerCase(parameterName);
|
||||
|
||||
// 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.
|
||||
// 11.5.
|
||||
if (position < endOfInput) {
|
||||
if (input[position] == "\u003B") continue;
|
||||
position++;
|
||||
}
|
||||
|
||||
// 11.2.
|
||||
const res1 = collectSequenceOfCodepoints(
|
||||
// 11.6.
|
||||
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) => ArrayPrototypeIncludes(HTTP_WHITESPACE, c),
|
||||
(c) => c !== "\u003B",
|
||||
);
|
||||
position = res1.position;
|
||||
parameterValue = res.result;
|
||||
position = res.position;
|
||||
|
||||
// 11.3.
|
||||
const res2 = collectSequenceOfCodepoints(
|
||||
input,
|
||||
position,
|
||||
(c) => c !== "\u003B" && c !== "\u003D",
|
||||
// 11.9.2.
|
||||
parameterValue = StringPrototypeReplaceAll(
|
||||
parameterValue,
|
||||
HTTP_WHITESPACE_SUFFIX_RE,
|
||||
"",
|
||||
);
|
||||
let parameterName = res2.result;
|
||||
position = res2.position;
|
||||
|
||||
// 11.4.
|
||||
parameterName = StringPrototypeToLowerCase(parameterName);
|
||||
// 11.9.3.
|
||||
if (parameterValue === "") continue;
|
||||
}
|
||||
|
||||
// 11.5.
|
||||
if (position < endOfInput) {
|
||||
if (input[position] == "\u003B") continue;
|
||||
position++;
|
||||
// 11.10.
|
||||
if (
|
||||
parameterName !== "" &&
|
||||
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;
|
||||
}
|
||||
|
||||
// 11.6.
|
||||
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.
|
||||
essence_ = essence(mimeType);
|
||||
} else {
|
||||
if (
|
||||
parameterName !== "" &&
|
||||
RegExpPrototypeTest(HTTP_TOKEN_CODE_POINT_RE, parameterName) &&
|
||||
RegExpPrototypeTest(
|
||||
HTTP_QUOTED_STRING_TOKEN_POINT_RE,
|
||||
parameterValue,
|
||||
) &&
|
||||
!MapPrototypeHas(mimeType.parameters, parameterName)
|
||||
!MapPrototypeHas(mimeType.parameters, "charset") &&
|
||||
charset !== null
|
||||
) {
|
||||
MapPrototypeSet(mimeType.parameters, parameterName, parameterValue);
|
||||
MapPrototypeSet(mimeType.parameters, "charset", charset);
|
||||
}
|
||||
}
|
||||
|
||||
// 12.
|
||||
return mimeType;
|
||||
}
|
||||
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);
|
||||
} else {
|
||||
if (
|
||||
!MapPrototypeHas(mimeType.parameters, "charset") &&
|
||||
charset !== null
|
||||
) {
|
||||
MapPrototypeSet(mimeType.parameters, "charset", charset);
|
||||
}
|
||||
}
|
||||
}
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
window.__bootstrap.mimesniff = {
|
||||
parseMimeType,
|
||||
essence,
|
||||
serializeMimeType,
|
||||
extractMimeType,
|
||||
};
|
||||
})(this);
|
||||
export { essence, extractMimeType, parseMimeType, serializeMimeType };
|
||||
|
|
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/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 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();
|
||||
|
||||
const objectCloneMemo = new WeakMap();
|
||||
|
||||
function cloneArrayBuffer(
|
||||
function cloneArrayBuffer(
|
||||
srcBuffer,
|
||||
srcByteOffset,
|
||||
srcLength,
|
||||
_cloneConstructor,
|
||||
) {
|
||||
// this function fudges the return type but SharedArrayBuffer is disabled for a while anyway
|
||||
return ArrayBufferPrototypeSlice(
|
||||
srcBuffer,
|
||||
srcByteOffset,
|
||||
srcLength,
|
||||
_cloneConstructor,
|
||||
) {
|
||||
// this function fudges the return type but SharedArrayBuffer is disabled for a while anyway
|
||||
return ArrayBufferPrototypeSlice(
|
||||
srcBuffer,
|
||||
srcByteOffset,
|
||||
srcByteOffset + srcLength,
|
||||
srcByteOffset + srcLength,
|
||||
);
|
||||
}
|
||||
|
||||
// TODO(petamoriken): Resizable ArrayBuffer support in the future
|
||||
/** Clone a value in a similar way to structured cloning. It is similar to a
|
||||
* StructureDeserialize(StructuredSerialize(...)). */
|
||||
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
|
||||
/** Clone a value in a similar way to structured cloning. It is similar to a
|
||||
* StructureDeserialize(StructuredSerialize(...)). */
|
||||
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),
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
return core.deserialize(core.serialize(value));
|
||||
} catch (e) {
|
||||
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
||||
throw new DOMException(e.message, "DataCloneError");
|
||||
}
|
||||
throw e;
|
||||
try {
|
||||
return core.deserialize(core.serialize(value));
|
||||
} catch (e) {
|
||||
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
||||
throw new DOMException(e.message, "DataCloneError");
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
window.__bootstrap.structuredClone = structuredClone;
|
||||
})(globalThis);
|
||||
export { structuredClone };
|
||||
|
|
|
@ -1,375 +1,372 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const {
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeShift,
|
||||
FunctionPrototypeCall,
|
||||
Map,
|
||||
MapPrototypeDelete,
|
||||
MapPrototypeGet,
|
||||
MapPrototypeHas,
|
||||
MapPrototypeSet,
|
||||
Uint8Array,
|
||||
Uint32Array,
|
||||
// deno-lint-ignore camelcase
|
||||
NumberPOSITIVE_INFINITY,
|
||||
PromisePrototypeThen,
|
||||
SafeArrayIterator,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
indirectEval,
|
||||
} = window.__bootstrap.primordials;
|
||||
const { webidl } = window.__bootstrap;
|
||||
const { reportException } = window.__bootstrap.event;
|
||||
const { assert } = window.__bootstrap.infra;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeShift,
|
||||
FunctionPrototypeCall,
|
||||
Map,
|
||||
MapPrototypeDelete,
|
||||
MapPrototypeGet,
|
||||
MapPrototypeHas,
|
||||
MapPrototypeSet,
|
||||
Uint8Array,
|
||||
Uint32Array,
|
||||
// deno-lint-ignore camelcase
|
||||
NumberPOSITIVE_INFINITY,
|
||||
PromisePrototypeThen,
|
||||
SafeArrayIterator,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
indirectEval,
|
||||
} = primordials;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import { reportException } from "internal:ext/web/02_event.js";
|
||||
import { assert } from "internal:ext/web/00_infra.js";
|
||||
|
||||
const hrU8 = new Uint8Array(8);
|
||||
const hr = new Uint32Array(hrU8.buffer);
|
||||
function opNow() {
|
||||
ops.op_now(hrU8);
|
||||
return (hr[0] * 1000 + hr[1] / 1e6);
|
||||
const hrU8 = new Uint8Array(8);
|
||||
const hr = new Uint32Array(hrU8.buffer);
|
||||
function opNow() {
|
||||
ops.op_now(hrU8);
|
||||
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);
|
||||
|
||||
/**
|
||||
* The task queue corresponding to the timer task source.
|
||||
*
|
||||
* @type { {action: () => void, nestingLevel: number}[] }
|
||||
*/
|
||||
const timerTasks = [];
|
||||
timerNestingLevel = task.nestingLevel;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
try {
|
||||
task.action();
|
||||
} finally {
|
||||
timerNestingLevel = 0;
|
||||
}
|
||||
return timerTasks.length === 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();
|
||||
} finally {
|
||||
timerNestingLevel = 0;
|
||||
}
|
||||
return timerTasks.length === 0;
|
||||
/**
|
||||
* @param {Function | string} callback
|
||||
* @param {number} timeout
|
||||
* @param {Array<any>} args
|
||||
* @param {boolean} repeat
|
||||
* @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;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
// 9. Let task be a task that runs the following steps:
|
||||
const task = {
|
||||
action: () => {
|
||||
// 1. If id does not exist in global's map of active timers, then abort
|
||||
// these steps.
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Function | string} callback
|
||||
* @param {number} timeout
|
||||
* @param {Array<any>} args
|
||||
* @param {boolean} repeat
|
||||
* @param {number | undefined} prevId
|
||||
* @returns {number} The timer ID
|
||||
*/
|
||||
function initializeTimer(
|
||||
callback,
|
||||
if (repeat) {
|
||||
if (MapPrototypeHas(activeTimers, id)) {
|
||||
// 4. If id does not exist in global's map of active timers, then
|
||||
// abort these steps.
|
||||
// NOTE: If might have been removed via the author code in handler
|
||||
// calling clearTimeout() or clearInterval().
|
||||
// 5. If repeat is true, then perform the timer initialization steps
|
||||
// again, given global, handler, timeout, arguments, true, and id.
|
||||
initializeTimer(callback, timeout, args, true, id);
|
||||
}
|
||||
} 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,
|
||||
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 };
|
||||
timerInfo,
|
||||
);
|
||||
|
||||
// Step 4 in "run steps after a timeout".
|
||||
MapPrototypeSet(activeTimers, id, timerInfo);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
// 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 = {
|
||||
action: () => {
|
||||
// 1. If id does not exist in global's map of active timers, then abort
|
||||
// these steps.
|
||||
//
|
||||
// This is relevant if the timer has been canceled after the sleep op
|
||||
// resolves but before this task runs.
|
||||
if (!MapPrototypeHas(activeTimers, id)) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* @typedef ScheduledTimer
|
||||
* @property {number} millis
|
||||
* @property {() => void} cb
|
||||
* @property {boolean} resolved
|
||||
* @property {ScheduledTimer | null} prev
|
||||
* @property {ScheduledTimer | null} next
|
||||
*/
|
||||
|
||||
// 2.
|
||||
// 3.
|
||||
if (typeof callback === "function") {
|
||||
try {
|
||||
FunctionPrototypeCall(
|
||||
callback,
|
||||
globalThis,
|
||||
...new SafeArrayIterator(args),
|
||||
);
|
||||
} catch (error) {
|
||||
reportException(error);
|
||||
}
|
||||
} else {
|
||||
indirectEval(callback);
|
||||
}
|
||||
/**
|
||||
* A doubly linked list of timers.
|
||||
* @type { { head: ScheduledTimer | null, tail: ScheduledTimer | null } }
|
||||
*/
|
||||
const scheduledTimers = { head: null, tail: null };
|
||||
|
||||
if (repeat) {
|
||||
if (MapPrototypeHas(activeTimers, id)) {
|
||||
// 4. If id does not exist in global's map of active timers, then
|
||||
// abort these steps.
|
||||
// NOTE: If might have been removed via the author code in handler
|
||||
// calling clearTimeout() or clearInterval().
|
||||
// 5. If repeat is true, then perform the timer initialization steps
|
||||
// again, given global, handler, timeout, arguments, true, and id.
|
||||
initializeTimer(callback, timeout, args, true, id);
|
||||
}
|
||||
} 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,
|
||||
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;
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
window.__bootstrap.timers = {
|
||||
setTimeout,
|
||||
setInterval,
|
||||
clearTimeout,
|
||||
clearInterval,
|
||||
handleTimerMacrotask,
|
||||
opNow,
|
||||
refTimer,
|
||||
unrefTimer,
|
||||
/** @type {ScheduledTimer} */
|
||||
const timerObject = {
|
||||
millis,
|
||||
cb,
|
||||
resolved: false,
|
||||
prev: scheduledTimers.tail,
|
||||
next: null,
|
||||
};
|
||||
})(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.
|
||||
"use strict";
|
||||
|
||||
// @ts-check
|
||||
/// <reference path="../../core/internal.d.ts" />
|
||||
|
||||
((window) => {
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { Event, setIsTrusted, defineEventHandler } = window.__bootstrap.event;
|
||||
const { EventTarget, listenerCount } = window.__bootstrap.eventTarget;
|
||||
const {
|
||||
SafeArrayIterator,
|
||||
SafeSetIterator,
|
||||
Set,
|
||||
SetPrototypeAdd,
|
||||
SetPrototypeDelete,
|
||||
Symbol,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
const { setTimeout, refTimer, unrefTimer } = window.__bootstrap.timers;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import {
|
||||
defineEventHandler,
|
||||
Event,
|
||||
EventTarget,
|
||||
listenerCount,
|
||||
setIsTrusted,
|
||||
} from "internal:ext/web/02_event.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
SafeArrayIterator,
|
||||
SafeSetIterator,
|
||||
Set,
|
||||
SetPrototypeAdd,
|
||||
SetPrototypeDelete,
|
||||
Symbol,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
import {
|
||||
refTimer,
|
||||
setTimeout,
|
||||
unrefTimer,
|
||||
} from "internal:ext/web/02_timers.js";
|
||||
|
||||
const add = Symbol("[[add]]");
|
||||
const signalAbort = Symbol("[[signalAbort]]");
|
||||
const remove = Symbol("[[remove]]");
|
||||
const abortReason = Symbol("[[abortReason]]");
|
||||
const abortAlgos = Symbol("[[abortAlgos]]");
|
||||
const signal = Symbol("[[signal]]");
|
||||
const timerId = Symbol("[[timerId]]");
|
||||
const add = Symbol("[[add]]");
|
||||
const signalAbort = Symbol("[[signalAbort]]");
|
||||
const remove = Symbol("[[remove]]");
|
||||
const abortReason = Symbol("[[abortReason]]");
|
||||
const abortAlgos = Symbol("[[abortAlgos]]");
|
||||
const signal = Symbol("[[signal]]");
|
||||
const timerId = Symbol("[[timerId]]");
|
||||
|
||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||
|
||||
class AbortSignal extends EventTarget {
|
||||
static abort(reason = undefined) {
|
||||
if (reason !== undefined) {
|
||||
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);
|
||||
class AbortSignal extends EventTarget {
|
||||
static abort(reason = undefined) {
|
||||
if (reason !== undefined) {
|
||||
reason = webidl.converters.any(reason);
|
||||
}
|
||||
const signal = new AbortSignal(illegalConstructorKey);
|
||||
signal[signalAbort](reason);
|
||||
return signal;
|
||||
}
|
||||
|
||||
webidl.configurePrototype(AbortController);
|
||||
const AbortControllerPrototype = AbortController.prototype;
|
||||
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,
|
||||
});
|
||||
|
||||
webidl.converters["AbortSignal"] = webidl.createInterfaceConverter(
|
||||
"AbortSignal",
|
||||
AbortSignal.prototype,
|
||||
);
|
||||
|
||||
function newSignal() {
|
||||
return new AbortSignal(illegalConstructorKey);
|
||||
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;
|
||||
}
|
||||
|
||||
function follow(followingSignal, parentSignal) {
|
||||
if (followingSignal.aborted) {
|
||||
[add](algorithm) {
|
||||
if (this.aborted) {
|
||||
return;
|
||||
}
|
||||
if (parentSignal.aborted) {
|
||||
followingSignal[signalAbort](parentSignal.reason);
|
||||
} else {
|
||||
parentSignal[add](() =>
|
||||
followingSignal[signalAbort](parentSignal.reason)
|
||||
);
|
||||
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];
|
||||
}
|
||||
}
|
||||
|
||||
window.__bootstrap.abortSignal = {
|
||||
AbortSignal,
|
||||
AbortController,
|
||||
AbortSignalPrototype,
|
||||
add,
|
||||
signalAbort,
|
||||
remove,
|
||||
follow,
|
||||
newSignal,
|
||||
};
|
||||
})(this);
|
||||
// `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);
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
"use strict";
|
||||
|
||||
// @ts-check
|
||||
/// <reference path="../../core/internal.d.ts" />
|
||||
|
||||
((window) => {
|
||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
||||
const {
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
import { EventTarget } from "internal:ext/web/02_event.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
Symbol,
|
||||
SymbolToStringTag,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
|
||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||
|
||||
class Window extends EventTarget {
|
||||
constructor(key = null) {
|
||||
if (key !== illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
}
|
||||
|
||||
get [SymbolToStringTag]() {
|
||||
return "Window";
|
||||
class Window extends EventTarget {
|
||||
constructor(key = null) {
|
||||
if (key !== illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
}
|
||||
|
||||
class WorkerGlobalScope extends EventTarget {
|
||||
constructor(key = null) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
}
|
||||
get [SymbolToStringTag]() {
|
||||
return "Window";
|
||||
}
|
||||
}
|
||||
|
||||
get [SymbolToStringTag]() {
|
||||
return "WorkerGlobalScope";
|
||||
class WorkerGlobalScope extends EventTarget {
|
||||
constructor(key = null) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
}
|
||||
|
||||
class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
||||
constructor(key = null) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
}
|
||||
get [SymbolToStringTag]() {
|
||||
return "WorkerGlobalScope";
|
||||
}
|
||||
}
|
||||
|
||||
get [SymbolToStringTag]() {
|
||||
return "DedicatedWorkerGlobalScope";
|
||||
class DedicatedWorkerGlobalScope extends WorkerGlobalScope {
|
||||
constructor(key = null) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
}
|
||||
|
||||
window.__bootstrap.globalInterfaces = {
|
||||
DedicatedWorkerGlobalScope,
|
||||
Window,
|
||||
WorkerGlobalScope,
|
||||
dedicatedWorkerGlobalScopeConstructorDescriptor: {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: DedicatedWorkerGlobalScope,
|
||||
writable: true,
|
||||
},
|
||||
windowConstructorDescriptor: {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: Window,
|
||||
writable: true,
|
||||
},
|
||||
workerGlobalScopeConstructorDescriptor: {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: WorkerGlobalScope,
|
||||
writable: true,
|
||||
},
|
||||
};
|
||||
})(this);
|
||||
get [SymbolToStringTag]() {
|
||||
return "DedicatedWorkerGlobalScope";
|
||||
}
|
||||
}
|
||||
|
||||
const dedicatedWorkerGlobalScopeConstructorDescriptor = {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: DedicatedWorkerGlobalScope,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
const windowConstructorDescriptor = {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: Window,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
const workerGlobalScopeConstructorDescriptor = {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: WorkerGlobalScope,
|
||||
writable: true,
|
||||
};
|
||||
|
||||
export {
|
||||
DedicatedWorkerGlobalScope,
|
||||
dedicatedWorkerGlobalScopeConstructorDescriptor,
|
||||
Window,
|
||||
windowConstructorDescriptor,
|
||||
WorkerGlobalScope,
|
||||
workerGlobalScopeConstructorDescriptor,
|
||||
};
|
||||
|
|
|
@ -6,68 +6,62 @@
|
|||
/// <reference path="../web/internal.d.ts" />
|
||||
/// <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;
|
||||
const ops = core.ops;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { DOMException } = window.__bootstrap.domException;
|
||||
const {
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
TypeErrorPrototype,
|
||||
} = window.__bootstrap.primordials;
|
||||
|
||||
/**
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
function atob(data) {
|
||||
const prefix = "Failed to execute 'atob'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
data = webidl.converters.DOMString(data, {
|
||||
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;
|
||||
/**
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
function atob(data) {
|
||||
const prefix = "Failed to execute 'atob'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
data = webidl.converters.DOMString(data, {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
function btoa(data) {
|
||||
const prefix = "Failed to execute 'btoa'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
data = webidl.converters.DOMString(data, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
try {
|
||||
return ops.op_base64_btoa(data);
|
||||
} catch (e) {
|
||||
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
||||
throw new DOMException(
|
||||
"The string to be encoded contains characters outside of the Latin1 range.",
|
||||
"InvalidCharacterError",
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
/**
|
||||
* @param {string} data
|
||||
* @returns {string}
|
||||
*/
|
||||
function btoa(data) {
|
||||
const prefix = "Failed to execute 'btoa'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
data = webidl.converters.DOMString(data, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
try {
|
||||
return ops.op_base64_btoa(data);
|
||||
} catch (e) {
|
||||
if (ObjectPrototypeIsPrototypeOf(TypeErrorPrototype, e)) {
|
||||
throw new DOMException(
|
||||
"The string to be encoded contains characters outside of the Latin1 range.",
|
||||
"InvalidCharacterError",
|
||||
);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
window.__bootstrap.base64 = {
|
||||
atob,
|
||||
btoa,
|
||||
};
|
||||
})(globalThis);
|
||||
export { atob, btoa };
|
||||
|
|
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 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) => {
|
||||
const core = Deno.core;
|
||||
const ops = core.ops;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const {
|
||||
PromiseReject,
|
||||
PromiseResolve,
|
||||
// TODO(lucacasonato): add SharedArrayBuffer to primordials
|
||||
// SharedArrayBufferPrototype
|
||||
StringPrototypeCharCodeAt,
|
||||
StringPrototypeSlice,
|
||||
TypedArrayPrototypeSubarray,
|
||||
Uint8Array,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
ArrayBufferIsView,
|
||||
Uint32Array,
|
||||
} = window.__bootstrap.primordials;
|
||||
class TextDecoder {
|
||||
/** @type {string} */
|
||||
#encoding;
|
||||
/** @type {boolean} */
|
||||
#fatal;
|
||||
/** @type {boolean} */
|
||||
#ignoreBOM;
|
||||
/** @type {boolean} */
|
||||
#utf8SinglePass;
|
||||
|
||||
class TextDecoder {
|
||||
/** @type {string} */
|
||||
#encoding;
|
||||
/** @type {boolean} */
|
||||
#fatal;
|
||||
/** @type {boolean} */
|
||||
#ignoreBOM;
|
||||
/** @type {boolean} */
|
||||
#utf8SinglePass;
|
||||
/** @type {number | null} */
|
||||
#rid = null;
|
||||
|
||||
/** @type {number | null} */
|
||||
#rid = null;
|
||||
|
||||
/**
|
||||
* @param {string} label
|
||||
* @param {TextDecoderOptions} options
|
||||
*/
|
||||
constructor(label = "utf-8", options = {}) {
|
||||
const prefix = "Failed to construct 'TextDecoder'";
|
||||
label = webidl.converters.DOMString(label, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
options = webidl.converters.TextDecoderOptions(options, {
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
const encoding = ops.op_encoding_normalize_label(label);
|
||||
this.#encoding = encoding;
|
||||
this.#fatal = options.fatal;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param {string} label
|
||||
* @param {TextDecoderOptions} options
|
||||
*/
|
||||
constructor(label = "utf-8", options = {}) {
|
||||
const prefix = "Failed to construct 'TextDecoder'";
|
||||
label = webidl.converters.DOMString(label, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
options = webidl.converters.TextDecoderOptions(options, {
|
||||
prefix,
|
||||
context: "Argument 2",
|
||||
});
|
||||
const encoding = ops.op_encoding_normalize_label(label);
|
||||
this.#encoding = encoding;
|
||||
this.#fatal = options.fatal;
|
||||
this.#ignoreBOM = options.ignoreBOM;
|
||||
this.#utf8SinglePass = encoding === "utf-8" && !options.fatal;
|
||||
this[webidl.brand] = webidl.brand;
|
||||
}
|
||||
|
||||
webidl.configurePrototype(TextDecoder);
|
||||
const TextDecoderPrototype = TextDecoder.prototype;
|
||||
/** @returns {string} */
|
||||
get encoding() {
|
||||
webidl.assertBranded(this, TextDecoderPrototype);
|
||||
return this.#encoding;
|
||||
}
|
||||
|
||||
class TextEncoder {
|
||||
constructor() {
|
||||
this[webidl.brand] = webidl.brand;
|
||||
}
|
||||
/** @returns {boolean} */
|
||||
get fatal() {
|
||||
webidl.assertBranded(this, TextDecoderPrototype);
|
||||
return this.#fatal;
|
||||
}
|
||||
|
||||
/** @returns {string} */
|
||||
get encoding() {
|
||||
webidl.assertBranded(this, TextEncoderPrototype);
|
||||
return "utf-8";
|
||||
}
|
||||
/** @returns {boolean} */
|
||||
get ignoreBOM() {
|
||||
webidl.assertBranded(this, TextDecoderPrototype);
|
||||
return this.#ignoreBOM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} input
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
encode(input = "") {
|
||||
webidl.assertBranded(this, TextEncoderPrototype);
|
||||
const prefix = "Failed to execute 'encode' on 'TextEncoder'";
|
||||
// The WebIDL type of `input` is `USVString`, but `core.encode` already
|
||||
// converts lone surrogates to the replacement character.
|
||||
input = webidl.converters.DOMString(input, {
|
||||
/**
|
||||
* @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",
|
||||
});
|
||||
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,
|
||||
});
|
||||
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, {
|
||||
let stream = false;
|
||||
if (options !== undefined) {
|
||||
options = webidl.converters.TextDecodeOptions(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;
|
||||
stream = options.stream;
|
||||
}
|
||||
|
||||
/** @returns {string} */
|
||||
get encoding() {
|
||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||
return this.#decoder.encoding;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
get fatal() {
|
||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||
return this.#decoder.fatal;
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
/** @returns {boolean} */
|
||||
get ignoreBOM() {
|
||||
webidl.assertBranded(this, TextDecoderStreamPrototype);
|
||||
return this.#decoder.ignoreBOM;
|
||||
}
|
||||
return ops.op_encoding_decode_single(
|
||||
input,
|
||||
this.#encoding,
|
||||
this.#fatal,
|
||||
this.#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;
|
||||
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(TextDecoderStream);
|
||||
const TextDecoderStreamPrototype = TextDecoderStream.prototype;
|
||||
webidl.configurePrototype(TextDecoder);
|
||||
const TextDecoderPrototype = TextDecoder.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;
|
||||
}
|
||||
class TextEncoder {
|
||||
constructor() {
|
||||
this[webidl.brand] = webidl.brand;
|
||||
}
|
||||
|
||||
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);
|
||||
/** @returns {string} */
|
||||
get encoding() {
|
||||
webidl.assertBranded(this, TextEncoderPrototype);
|
||||
return "utf-8";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Uint8Array} bytes
|
||||
* @param {string} input
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
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;
|
||||
encode(input = "") {
|
||||
webidl.assertBranded(this, TextEncoderPrototype);
|
||||
const prefix = "Failed to execute 'encode' on 'TextEncoder'";
|
||||
// The WebIDL type of `input` is `USVString`, but `core.encode` already
|
||||
// converts lone surrogates to the replacement character.
|
||||
input = webidl.converters.DOMString(input, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
return core.encode(input);
|
||||
}
|
||||
|
||||
window.__bootstrap.encoding = {
|
||||
TextEncoder,
|
||||
TextDecoder,
|
||||
TextEncoderStream,
|
||||
TextDecoderStream,
|
||||
decode,
|
||||
};
|
||||
})(this);
|
||||
/**
|
||||
* @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,
|
||||
});
|
||||
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 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 core = window.Deno.core;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { forgivingBase64Encode } = window.__bootstrap.infra;
|
||||
const { ProgressEvent } = window.__bootstrap.event;
|
||||
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]]");
|
||||
const result = Symbol("[[result]]");
|
||||
const error = Symbol("[[error]]");
|
||||
const aborted = Symbol("[[aborted]]");
|
||||
const handlerSymbol = Symbol("eventHandlers");
|
||||
|
||||
const state = Symbol("[[state]]");
|
||||
const result = Symbol("[[result]]");
|
||||
const error = Symbol("[[error]]");
|
||||
const aborted = Symbol("[[aborted]]");
|
||||
const handlerSymbol = Symbol("eventHandlers");
|
||||
class FileReader extends EventTarget {
|
||||
/** @type {"empty" | "loading" | "done"} */
|
||||
[state] = "empty";
|
||||
/** @type {null | string | ArrayBuffer} */
|
||||
[result] = null;
|
||||
/** @type {null | DOMException} */
|
||||
[error] = null;
|
||||
/** @type {null | {aborted: boolean}} */
|
||||
[aborted] = null;
|
||||
|
||||
class FileReader extends EventTarget {
|
||||
/** @type {"empty" | "loading" | "done"} */
|
||||
[state] = "empty";
|
||||
/** @type {null | string | ArrayBuffer} */
|
||||
[result] = null;
|
||||
/** @type {null | DOMException} */
|
||||
[error] = null;
|
||||
/** @type {null | {aborted: boolean}} */
|
||||
[aborted] = null;
|
||||
/**
|
||||
* @param {Blob} blob
|
||||
* @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype
|
||||
*/
|
||||
#readOperation(blob, readtype) {
|
||||
// 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;
|
||||
|
||||
/**
|
||||
* @param {Blob} blob
|
||||
* @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype
|
||||
*/
|
||||
#readOperation(blob, readtype) {
|
||||
// 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
|
||||
// separate variable, so if a new read operation starts while there are
|
||||
// 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 };
|
||||
|
||||
// We set this[aborted] to a new object, and keep track of it in a
|
||||
// separate variable, so if a new read operation starts while there are
|
||||
// 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.
|
||||
const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream();
|
||||
|
||||
// 5. Let stream be the result of calling get stream on blob.
|
||||
const stream /*: ReadableStream<ArrayBufferView>*/ = blob.stream();
|
||||
// 6. Let reader be the result of getting a reader from stream.
|
||||
const reader = stream.getReader();
|
||||
|
||||
// 6. Let reader be the result of getting a reader from stream.
|
||||
const reader = stream.getReader();
|
||||
// 7. Let bytes be an empty byte sequence.
|
||||
/** @type {Uint8Array[]} */
|
||||
const chunks = [];
|
||||
|
||||
// 7. Let bytes be an empty byte sequence.
|
||||
/** @type {Uint8Array[]} */
|
||||
const chunks = [];
|
||||
// 8. Let chunkPromise be the result of reading a chunk from stream with reader.
|
||||
let chunkPromise = reader.read();
|
||||
|
||||
// 8. Let chunkPromise be the result of reading a chunk from stream with reader.
|
||||
let chunkPromise = reader.read();
|
||||
// 9. Let isFirstChunk be true.
|
||||
let isFirstChunk = true;
|
||||
|
||||
// 9. Let isFirstChunk be true.
|
||||
let isFirstChunk = true;
|
||||
// 10 in parallel while 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
|
||||
(async () => {
|
||||
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) {
|
||||
// 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;
|
||||
|
||||
// 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[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);
|
||||
}
|
||||
|
||||
//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.
|
||||
// 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", {});
|
||||
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"
|
||||
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() {
|
||||
super();
|
||||
this[webidl.brand] = webidl.brand;
|
||||
MapPrototypeSet(this[handlerSymbol], name, handlerWrapper);
|
||||
}
|
||||
|
||||
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} */
|
||||
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");
|
||||
}
|
||||
}
|
||||
// Fire a progress event called abort at the context object.
|
||||
const ev = new ProgressEvent("abort", {});
|
||||
this.dispatchEvent(ev);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Fire a progress event called abort at the context object.
|
||||
const ev = new ProgressEvent("abort", {});
|
||||
// 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);
|
||||
|
||||
// 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);
|
||||
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;
|
||||
/** @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" });
|
||||
}
|
||||
|
||||
window.__bootstrap.fileReader = {
|
||||
FileReader,
|
||||
};
|
||||
})(this);
|
||||
/** @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);
|
||||
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="./internal.d.ts" />
|
||||
/// <reference lib="esnext" />
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = Deno.core;
|
||||
const ops = core.ops;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { getParts } = window.__bootstrap.file;
|
||||
const { URL } = window.__bootstrap.url;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import { getParts } from "internal:ext/web/09_file.js";
|
||||
import { URL } from "internal:ext/url/00_url.js";
|
||||
|
||||
/**
|
||||
* @param {Blob} blob
|
||||
* @returns {string}
|
||||
*/
|
||||
function createObjectURL(blob) {
|
||||
const prefix = "Failed to execute 'createObjectURL' on 'URL'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
blob = webidl.converters["Blob"](blob, {
|
||||
context: "Argument 1",
|
||||
prefix,
|
||||
});
|
||||
/**
|
||||
* @param {Blob} blob
|
||||
* @returns {string}
|
||||
*/
|
||||
function createObjectURL(blob) {
|
||||
const prefix = "Failed to execute 'createObjectURL' on 'URL'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
blob = webidl.converters["Blob"](blob, {
|
||||
context: "Argument 1",
|
||||
prefix,
|
||||
});
|
||||
|
||||
const url = ops.op_blob_create_object_url(
|
||||
blob.type,
|
||||
getParts(blob),
|
||||
);
|
||||
return ops.op_blob_create_object_url(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,
|
||||
});
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
ops.op_blob_revoke_object_url(url);
|
||||
}
|
||||
|
||||
URL.createObjectURL = createObjectURL;
|
||||
URL.revokeObjectURL = revokeObjectURL;
|
||||
})(globalThis);
|
||||
URL.createObjectURL = createObjectURL;
|
||||
URL.revokeObjectURL = revokeObjectURL;
|
||||
|
|
|
@ -1,403 +1,410 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
/// <reference path="../../core/internal.d.ts" />
|
||||
|
||||
((window) => {
|
||||
const { URL } = window.__bootstrap.url;
|
||||
const { DOMException } = window.__bootstrap.domException;
|
||||
const {
|
||||
Error,
|
||||
ObjectDefineProperties,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
SymbolToStringTag,
|
||||
TypeError,
|
||||
WeakMap,
|
||||
WeakMapPrototypeGet,
|
||||
WeakMapPrototypeSet,
|
||||
} = window.__bootstrap.primordials;
|
||||
import { URL } from "internal:ext/url/00_url.js";
|
||||
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
Error,
|
||||
ObjectDefineProperties,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
SymbolToStringTag,
|
||||
TypeError,
|
||||
WeakMap,
|
||||
WeakMapPrototypeGet,
|
||||
WeakMapPrototypeSet,
|
||||
} = primordials;
|
||||
|
||||
const locationConstructorKey = Symbol("locationConstuctorKey");
|
||||
const locationConstructorKey = Symbol("locationConstuctorKey");
|
||||
|
||||
// The differences between the definitions of `Location` and `WorkerLocation`
|
||||
// are because of the `LegacyUnforgeable` attribute only specified upon
|
||||
// `Location`'s properties. See:
|
||||
// - https://html.spec.whatwg.org/multipage/history.html#the-location-interface
|
||||
// - https://heycam.github.io/webidl/#LegacyUnforgeable
|
||||
class Location {
|
||||
constructor(href = null, key = null) {
|
||||
if (key != locationConstructorKey) {
|
||||
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)}`;
|
||||
},
|
||||
},
|
||||
});
|
||||
// The differences between the definitions of `Location` and `WorkerLocation`
|
||||
// are because of the `LegacyUnforgeable` attribute only specified upon
|
||||
// `Location`'s properties. See:
|
||||
// - https://html.spec.whatwg.org/multipage/history.html#the-location-interface
|
||||
// - https://heycam.github.io/webidl/#LegacyUnforgeable
|
||||
class Location {
|
||||
constructor(href = null, key = null) {
|
||||
if (key != locationConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
},
|
||||
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.`,
|
||||
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",
|
||||
);
|
||||
}
|
||||
return workerLocation;
|
||||
},
|
||||
enumerable: true,
|
||||
},
|
||||
configurable: true,
|
||||
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)}`;
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
getLocationHref() {
|
||||
return location?.href;
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
},
|
||||
host: {
|
||||
get() {
|
||||
const url = WeakMapPrototypeGet(workerLocationUrls, this);
|
||||
if (url == null) {
|
||||
throw new TypeError("Illegal invocation.");
|
||||
}
|
||||
return url.host;
|
||||
},
|
||||
};
|
||||
})(this);
|
||||
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);
|
||||
}
|
||||
|
||||
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="./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) => {
|
||||
const core = window.Deno.core;
|
||||
const { InterruptedPrototype, ops } = core;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { EventTarget, setEventTargetData } = window.__bootstrap.eventTarget;
|
||||
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 {
|
||||
/** @type {MessagePort} */
|
||||
#port1;
|
||||
/** @type {MessagePort} */
|
||||
#port2;
|
||||
|
||||
class MessageChannel {
|
||||
/** @type {MessagePort} */
|
||||
#port1;
|
||||
/** @type {MessagePort} */
|
||||
#port2;
|
||||
|
||||
constructor() {
|
||||
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 })
|
||||
}`;
|
||||
}
|
||||
constructor() {
|
||||
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;
|
||||
}
|
||||
|
||||
webidl.configurePrototype(MessageChannel);
|
||||
const MessageChannelPrototype = MessageChannel.prototype;
|
||||
|
||||
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;
|
||||
get port1() {
|
||||
webidl.assertBranded(this, MessageChannelPrototype);
|
||||
return this.#port1;
|
||||
}
|
||||
|
||||
class MessagePort extends EventTarget {
|
||||
/** @type {number | null} */
|
||||
[_id] = null;
|
||||
/** @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;
|
||||
}
|
||||
}
|
||||
get port2() {
|
||||
webidl.assertBranded(this, MessageChannelPrototype);
|
||||
return this.#port2;
|
||||
}
|
||||
|
||||
defineEventHandler(MessagePort.prototype, "message", function (self) {
|
||||
self.start();
|
||||
});
|
||||
defineEventHandler(MessagePort.prototype, "messageerror");
|
||||
[SymbolFor("Deno.inspect")](inspect) {
|
||||
return `MessageChannel ${
|
||||
inspect({ port1: this.port1, port2: this.port2 })
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
webidl.configurePrototype(MessagePort);
|
||||
const MessagePortPrototype = MessagePort.prototype;
|
||||
webidl.configurePrototype(MessageChannel);
|
||||
const MessageChannelPrototype = MessageChannel.prototype;
|
||||
|
||||
/**
|
||||
* @returns {[number, number]}
|
||||
*/
|
||||
function opCreateEntangledMessagePort() {
|
||||
return ops.op_message_port_create_entangled();
|
||||
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 {
|
||||
/** @type {number | null} */
|
||||
[_id] = null;
|
||||
/** @type {boolean} */
|
||||
[_enabled] = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
webidl.illegalConstructor();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {globalThis.__bootstrap.messagePort.MessageData} messageData
|
||||
* @returns {[any, object[]]}
|
||||
* @param {any} message
|
||||
* @param {object[] | StructuredSerializeOptions} transferOrOptions
|
||||
*/
|
||||
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 {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'";
|
||||
postMessage(message, transferOrOptions = {}) {
|
||||
webidl.assertBranded(this, MessagePortPrototype);
|
||||
const prefix = "Failed to execute 'postMessage' on 'MessagePort'";
|
||||
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];
|
||||
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);
|
||||
}
|
||||
|
||||
window.__bootstrap.messagePort = {
|
||||
MessageChannel,
|
||||
MessagePort,
|
||||
MessagePortPrototype,
|
||||
deserializeJsMessageData,
|
||||
serializeJsMessageData,
|
||||
structuredClone,
|
||||
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) {
|
||||
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="./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) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { TransformStream } = window.__bootstrap.streams;
|
||||
webidl.converters.CompressionFormat = webidl.createEnumConverter(
|
||||
"CompressionFormat",
|
||||
[
|
||||
"deflate",
|
||||
"deflate-raw",
|
||||
"gzip",
|
||||
],
|
||||
);
|
||||
|
||||
webidl.converters.CompressionFormat = webidl.createEnumConverter(
|
||||
"CompressionFormat",
|
||||
[
|
||||
"deflate",
|
||||
"deflate-raw",
|
||||
"gzip",
|
||||
],
|
||||
);
|
||||
class CompressionStream {
|
||||
#transform;
|
||||
|
||||
class CompressionStream {
|
||||
#transform;
|
||||
constructor(format) {
|
||||
const prefix = "Failed to construct 'CompressionStream'";
|
||||
webidl.requiredArguments(arguments.length, 1, { prefix });
|
||||
format = webidl.converters.CompressionFormat(format, {
|
||||
prefix,
|
||||
context: "Argument 1",
|
||||
});
|
||||
|
||||
constructor(format) {
|
||||
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);
|
||||
|
||||
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({
|
||||
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;
|
||||
}
|
||||
this[webidl.brand] = webidl.brand;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
get readable() {
|
||||
webidl.assertBranded(this, DecompressionStreamPrototype);
|
||||
return this.#transform.readable;
|
||||
}
|
||||
|
||||
get writable() {
|
||||
webidl.assertBranded(this, DecompressionStreamPrototype);
|
||||
return this.#transform.writable;
|
||||
}
|
||||
get readable() {
|
||||
webidl.assertBranded(this, CompressionStreamPrototype);
|
||||
return this.#transform.readable;
|
||||
}
|
||||
|
||||
function maybeEnqueue(controller, output) {
|
||||
if (output && output.byteLength > 0) {
|
||||
controller.enqueue(output);
|
||||
}
|
||||
get writable() {
|
||||
webidl.assertBranded(this, CompressionStreamPrototype);
|
||||
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);
|
||||
const DecompressionStreamPrototype = DecompressionStream.prototype;
|
||||
get readable() {
|
||||
webidl.assertBranded(this, DecompressionStreamPrototype);
|
||||
return this.#transform.readable;
|
||||
}
|
||||
|
||||
window.__bootstrap.compression = {
|
||||
CompressionStream,
|
||||
DecompressionStream,
|
||||
};
|
||||
})(globalThis);
|
||||
get writable() {
|
||||
webidl.assertBranded(this, DecompressionStreamPrototype);
|
||||
return this.#transform.writable;
|
||||
}
|
||||
}
|
||||
|
||||
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_web::init::<Permissions>(BlobStore::default(), None),
|
||||
Extension::builder("bench_setup")
|
||||
.js(vec![(
|
||||
"setup",
|
||||
.esm(vec![(
|
||||
"internal:setup",
|
||||
r#"
|
||||
const { TextDecoder } = globalThis.__bootstrap.encoding;
|
||||
const hello12k = Deno.core.encode("hello world\n".repeat(1e3));
|
||||
import { TextDecoder } from "internal:ext/web/08_text_encoding.js";
|
||||
globalThis.TextDecoder = TextDecoder;
|
||||
globalThis.hello12k = Deno.core.encode("hello world\n".repeat(1e3));
|
||||
"#,
|
||||
)])
|
||||
.state(|state| {
|
||||
|
|
|
@ -28,9 +28,10 @@ fn setup() -> Vec<Extension> {
|
|||
deno_console::init(),
|
||||
deno_web::init::<Permissions>(BlobStore::default(), None),
|
||||
Extension::builder("bench_setup")
|
||||
.js(vec![
|
||||
("setup", r#"
|
||||
const { setTimeout, handleTimerMacrotask } = globalThis.__bootstrap.timers;
|
||||
.esm(vec![
|
||||
("internal:setup", r#"
|
||||
import { setTimeout, handleTimerMacrotask } from "internal:ext/web/02_timers.js";
|
||||
globalThis.setTimeout = setTimeout;
|
||||
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.
|
||||
|
||||
// deno-lint-ignore-file no-var
|
||||
|
||||
/// <reference no-default-lib="true" />
|
||||
/// <reference lib="esnext" />
|
||||
|
||||
declare namespace globalThis {
|
||||
declare namespace __bootstrap {
|
||||
declare var infra: {
|
||||
collectSequenceOfCodepoints(
|
||||
input: string,
|
||||
position: number,
|
||||
condition: (char: string) => boolean,
|
||||
): {
|
||||
result: string;
|
||||
position: number;
|
||||
};
|
||||
ASCII_DIGIT: string[];
|
||||
ASCII_UPPER_ALPHA: string[];
|
||||
ASCII_LOWER_ALPHA: string[];
|
||||
ASCII_ALPHA: string[];
|
||||
ASCII_ALPHANUMERIC: string[];
|
||||
HTTP_TAB_OR_SPACE: string[];
|
||||
HTTP_WHITESPACE: string[];
|
||||
HTTP_TOKEN_CODE_POINT: string[];
|
||||
HTTP_TOKEN_CODE_POINT_RE: RegExp;
|
||||
HTTP_QUOTED_STRING_TOKEN_POINT: string[];
|
||||
HTTP_QUOTED_STRING_TOKEN_POINT_RE: RegExp;
|
||||
HTTP_TAB_OR_SPACE_PREFIX_RE: RegExp;
|
||||
HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp;
|
||||
HTTP_WHITESPACE_PREFIX_RE: RegExp;
|
||||
HTTP_WHITESPACE_SUFFIX_RE: RegExp;
|
||||
httpTrim(s: string): string;
|
||||
regexMatcher(chars: string[]): string;
|
||||
byteUpperCase(s: string): string;
|
||||
byteLowerCase(s: string): string;
|
||||
collectHttpQuotedString(
|
||||
input: string,
|
||||
position: number,
|
||||
extractValue: boolean,
|
||||
): {
|
||||
result: string;
|
||||
position: number;
|
||||
};
|
||||
forgivingBase64Encode(data: Uint8Array): string;
|
||||
forgivingBase64Decode(data: string): Uint8Array;
|
||||
serializeJSValueToJSONString(value: unknown): string;
|
||||
};
|
||||
declare module "internal:ext/web/00_infra.js" {
|
||||
function collectSequenceOfCodepoints(
|
||||
input: string,
|
||||
position: number,
|
||||
condition: (char: string) => boolean,
|
||||
): {
|
||||
result: string;
|
||||
position: number;
|
||||
};
|
||||
const ASCII_DIGIT: string[];
|
||||
const ASCII_UPPER_ALPHA: string[];
|
||||
const ASCII_LOWER_ALPHA: string[];
|
||||
const ASCII_ALPHA: string[];
|
||||
const ASCII_ALPHANUMERIC: string[];
|
||||
const HTTP_TAB_OR_SPACE: string[];
|
||||
const HTTP_WHITESPACE: string[];
|
||||
const HTTP_TOKEN_CODE_POINT: string[];
|
||||
const HTTP_TOKEN_CODE_POINT_RE: RegExp;
|
||||
const HTTP_QUOTED_STRING_TOKEN_POINT: string[];
|
||||
const HTTP_QUOTED_STRING_TOKEN_POINT_RE: RegExp;
|
||||
const HTTP_TAB_OR_SPACE_PREFIX_RE: RegExp;
|
||||
const HTTP_TAB_OR_SPACE_SUFFIX_RE: RegExp;
|
||||
const HTTP_WHITESPACE_PREFIX_RE: RegExp;
|
||||
const HTTP_WHITESPACE_SUFFIX_RE: RegExp;
|
||||
function httpTrim(s: string): string;
|
||||
function regexMatcher(chars: string[]): string;
|
||||
function byteUpperCase(s: string): string;
|
||||
function byteLowerCase(s: string): string;
|
||||
function collectHttpQuotedString(
|
||||
input: string,
|
||||
position: number,
|
||||
extractValue: boolean,
|
||||
): {
|
||||
result: string;
|
||||
position: number;
|
||||
};
|
||||
function forgivingBase64Encode(data: Uint8Array): string;
|
||||
function forgivingBase64Decode(data: string): Uint8Array;
|
||||
function serializeJSValueToJSONString(value: unknown): string;
|
||||
}
|
||||
|
||||
declare var domException: {
|
||||
DOMException: typeof DOMException;
|
||||
};
|
||||
declare module "internal:ext/web/01_dom_exception.js" {
|
||||
export = DOMException;
|
||||
}
|
||||
|
||||
declare namespace mimesniff {
|
||||
declare interface MimeType {
|
||||
type: string;
|
||||
subtype: string;
|
||||
parameters: Map<string, string>;
|
||||
}
|
||||
declare function parseMimeType(input: string): MimeType | null;
|
||||
declare function essence(mimeType: MimeType): string;
|
||||
declare function serializeMimeType(mimeType: MimeType): string;
|
||||
declare function extractMimeType(
|
||||
headerValues: string[] | null,
|
||||
): MimeType | null;
|
||||
}
|
||||
declare module "internal:ext/web/01_mimesniff.js" {
|
||||
interface MimeType {
|
||||
type: string;
|
||||
subtype: string;
|
||||
parameters: Map<string, string>;
|
||||
}
|
||||
function parseMimeType(input: string): MimeType | null;
|
||||
function essence(mimeType: MimeType): string;
|
||||
function serializeMimeType(mimeType: MimeType): string;
|
||||
function extractMimeType(
|
||||
headerValues: string[] | null,
|
||||
): MimeType | null;
|
||||
}
|
||||
|
||||
declare var eventTarget: {
|
||||
EventTarget: typeof EventTarget;
|
||||
};
|
||||
declare module "internal:ext/web/02_event.js" {
|
||||
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: {
|
||||
Event: typeof event;
|
||||
ErrorEvent: typeof ErrorEvent;
|
||||
CloseEvent: typeof CloseEvent;
|
||||
MessageEvent: typeof MessageEvent;
|
||||
CustomEvent: typeof CustomEvent;
|
||||
ProgressEvent: typeof ProgressEvent;
|
||||
PromiseRejectionEvent: typeof PromiseRejectionEvent;
|
||||
reportError: typeof reportError;
|
||||
};
|
||||
declare module "internal:ext/web/12_location.js" {
|
||||
function getLocationHref(): string | undefined;
|
||||
}
|
||||
|
||||
declare var location: {
|
||||
getLocationHref(): string | undefined;
|
||||
};
|
||||
declare module "internal:ext/web/05_base64.js" {
|
||||
function atob(data: string): string;
|
||||
function btoa(data: string): string;
|
||||
}
|
||||
|
||||
declare var base64: {
|
||||
atob(data: string): string;
|
||||
btoa(data: string): string;
|
||||
};
|
||||
declare module "internal:ext/web/09_file.js" {
|
||||
function blobFromObjectUrl(url: string): Blob | null;
|
||||
function getParts(blob: Blob): string[];
|
||||
const Blob: typeof Blob;
|
||||
const File: typeof File;
|
||||
}
|
||||
|
||||
declare var file: {
|
||||
blobFromObjectUrl(url: string): Blob | null;
|
||||
getParts(blob: Blob): string[];
|
||||
Blob: typeof Blob;
|
||||
File: typeof File;
|
||||
};
|
||||
declare module "internal:ext/web/06_streams.js" {
|
||||
const ReadableStream: typeof ReadableStream;
|
||||
function isReadableStreamDisturbed(stream: ReadableStream): boolean;
|
||||
function createProxy<T>(stream: ReadableStream<T>): ReadableStream<T>;
|
||||
}
|
||||
|
||||
declare var streams: {
|
||||
ReadableStream: typeof ReadableStream;
|
||||
isReadableStreamDisturbed(stream: ReadableStream): boolean;
|
||||
createProxy<T>(stream: ReadableStream<T>): ReadableStream<T>;
|
||||
};
|
||||
|
||||
declare namespace messagePort {
|
||||
declare type Transferable = {
|
||||
kind: "messagePort";
|
||||
data: number;
|
||||
} | {
|
||||
kind: "arrayBuffer";
|
||||
data: number;
|
||||
};
|
||||
declare interface MessageData {
|
||||
data: Uint8Array;
|
||||
transferables: Transferable[];
|
||||
}
|
||||
}
|
||||
declare module "internal:ext/web/13_message_port.js" {
|
||||
type Transferable = {
|
||||
kind: "messagePort";
|
||||
data: number;
|
||||
} | {
|
||||
kind: "arrayBuffer";
|
||||
data: number;
|
||||
};
|
||||
interface MessageData {
|
||||
data: Uint8Array;
|
||||
transferables: Transferable[];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,7 +64,7 @@ pub fn init<P: TimersPermission + 'static>(
|
|||
) -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_webidl", "deno_console", "deno_url"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/web",
|
||||
"00_infra.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="./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 core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { Symbol } = window.__bootstrap.primordials;
|
||||
const { _device, assertDevice, createGPUTexture } = window.__bootstrap.webgpu;
|
||||
const _surfaceRid = Symbol("[[surfaceRid]]");
|
||||
const _configuration = Symbol("[[configuration]]");
|
||||
const _canvas = Symbol("[[canvas]]");
|
||||
const _currentTexture = Symbol("[[currentTexture]]");
|
||||
class GPUCanvasContext {
|
||||
/** @type {number} */
|
||||
[_surfaceRid];
|
||||
/** @type {InnerGPUDevice} */
|
||||
[_device];
|
||||
[_configuration];
|
||||
[_canvas];
|
||||
/** @type {GPUTexture | undefined} */
|
||||
[_currentTexture];
|
||||
|
||||
const _surfaceRid = Symbol("[[surfaceRid]]");
|
||||
const _configuration = Symbol("[[configuration]]");
|
||||
const _canvas = Symbol("[[canvas]]");
|
||||
const _currentTexture = Symbol("[[currentTexture]]");
|
||||
class GPUCanvasContext {
|
||||
/** @type {number} */
|
||||
[_surfaceRid];
|
||||
/** @type {InnerGPUDevice} */
|
||||
[_device];
|
||||
[_configuration];
|
||||
[_canvas];
|
||||
/** @type {GPUTexture | undefined} */
|
||||
[_currentTexture];
|
||||
get canvas() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
return this[_canvas];
|
||||
}
|
||||
|
||||
get canvas() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
return this[_canvas];
|
||||
}
|
||||
constructor() {
|
||||
webidl.illegalConstructor();
|
||||
}
|
||||
|
||||
constructor() {
|
||||
webidl.illegalConstructor();
|
||||
}
|
||||
configure(configuration) {
|
||||
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) {
|
||||
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",
|
||||
});
|
||||
this[_device] = configuration.device[_device];
|
||||
this[_configuration] = configuration;
|
||||
const device = assertDevice(this, {
|
||||
prefix,
|
||||
context: "configuration.device",
|
||||
});
|
||||
|
||||
this[_device] = configuration.device[_device];
|
||||
this[_configuration] = configuration;
|
||||
const device = assertDevice(this, {
|
||||
prefix,
|
||||
context: "configuration.device",
|
||||
});
|
||||
const { err } = ops.op_webgpu_surface_configure({
|
||||
surfaceRid: this[_surfaceRid],
|
||||
deviceRid: device.rid,
|
||||
format: configuration.format,
|
||||
viewFormats: configuration.viewFormats,
|
||||
usage: configuration.usage,
|
||||
width: configuration.width,
|
||||
height: configuration.height,
|
||||
alphaMode: configuration.alphaMode,
|
||||
});
|
||||
|
||||
const { err } = ops.op_webgpu_surface_configure({
|
||||
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);
|
||||
}
|
||||
|
||||
device.pushError(err);
|
||||
}
|
||||
unconfigure() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
|
||||
unconfigure() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
this[_configuration] = null;
|
||||
this[_device] = null;
|
||||
}
|
||||
|
||||
this[_configuration] = null;
|
||||
this[_device] = null;
|
||||
}
|
||||
getCurrentTexture() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
const prefix =
|
||||
"Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
|
||||
|
||||
getCurrentTexture() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
const prefix =
|
||||
"Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
|
||||
|
||||
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],
|
||||
if (this[_configuration] === null) {
|
||||
throw new DOMException(
|
||||
"context is not configured.",
|
||||
"InvalidStateError",
|
||||
);
|
||||
}
|
||||
|
||||
const texture = createGPUTexture(
|
||||
{
|
||||
size: {
|
||||
width: this[_configuration].width,
|
||||
height: this[_configuration].height,
|
||||
depthOrArrayLayers: 1,
|
||||
},
|
||||
mipLevelCount: 1,
|
||||
sampleCount: 1,
|
||||
dimension: "2d",
|
||||
format: this[_configuration].format,
|
||||
usage: this[_configuration].usage,
|
||||
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(
|
||||
{
|
||||
size: {
|
||||
width: this[_configuration].width,
|
||||
height: this[_configuration].height,
|
||||
depthOrArrayLayers: 1,
|
||||
},
|
||||
device,
|
||||
rid,
|
||||
);
|
||||
device.trackResource(texture);
|
||||
this[_currentTexture] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
// Extended from spec. Required to present the texture; browser don't need this.
|
||||
present() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
|
||||
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;
|
||||
mipLevelCount: 1,
|
||||
sampleCount: 1,
|
||||
dimension: "2d",
|
||||
format: this[_configuration].format,
|
||||
usage: this[_configuration].usage,
|
||||
},
|
||||
device,
|
||||
rid,
|
||||
);
|
||||
device.trackResource(texture);
|
||||
this[_currentTexture] = texture;
|
||||
return texture;
|
||||
}
|
||||
|
||||
window.__bootstrap.webgpu = {
|
||||
...window.__bootstrap.webgpu,
|
||||
GPUCanvasContext,
|
||||
createCanvasContext,
|
||||
};
|
||||
})(this);
|
||||
// Extended from spec. Required to present the texture; browser don't need this.
|
||||
present() {
|
||||
webidl.assertBranded(this, GPUCanvasContextPrototype);
|
||||
const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
|
||||
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;
|
||||
}
|
||||
|
||||
export { createCanvasContext, GPUCanvasContext };
|
||||
|
|
|
@ -6,81 +6,77 @@
|
|||
/// <reference path="../web/lib.deno_web.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) => {
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { GPUTextureUsage } = window.__bootstrap.webgpu;
|
||||
// ENUM: GPUCanvasAlphaMode
|
||||
webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
|
||||
"GPUCanvasAlphaMode",
|
||||
[
|
||||
"opaque",
|
||||
"premultiplied",
|
||||
],
|
||||
);
|
||||
|
||||
// ENUM: GPUCanvasAlphaMode
|
||||
webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
|
||||
"GPUCanvasAlphaMode",
|
||||
[
|
||||
"opaque",
|
||||
"premultiplied",
|
||||
],
|
||||
// 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,
|
||||
);
|
||||
|
||||
// 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 {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_webidl", "deno_web"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/webgpu",
|
||||
"01_webgpu.js",
|
||||
"02_idl_types.js",
|
||||
|
|
|
@ -15,8 +15,8 @@ use wgpu_types::SurfaceStatus;
|
|||
pub fn init_surface(unstable: bool) -> Extension {
|
||||
Extension::builder("deno_webgpu_surface")
|
||||
.dependencies(vec!["deno_webidl", "deno_web", "deno_webgpu"])
|
||||
.js(include_js_files!(
|
||||
prefix "internal:deno_webgpu",
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/webgpu",
|
||||
"03_surface.js",
|
||||
"04_surface_idl_types.js",
|
||||
))
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -2,7 +2,10 @@
|
|||
|
||||
// deno-lint-ignore-file
|
||||
|
||||
const { createDictionaryConverter, converters } = globalThis.__bootstrap.webidl;
|
||||
import {
|
||||
converters,
|
||||
createDictionaryConverter,
|
||||
} from "internal:ext/webidl/00_webidl.js";
|
||||
|
||||
const TextDecodeOptions = createDictionaryConverter(
|
||||
"TextDecodeOptions",
|
||||
|
@ -14,6 +17,7 @@ const TextDecodeOptions = createDictionaryConverter(
|
|||
},
|
||||
],
|
||||
);
|
||||
globalThis.TextDecodeOptions = TextDecodeOptions;
|
||||
|
||||
// Sanity check
|
||||
{
|
||||
|
@ -33,3 +37,4 @@ function handwrittenConverter(V) {
|
|||
}
|
||||
return defaultValue;
|
||||
}
|
||||
globalThis.handwrittenConverter = handwrittenConverter;
|
||||
|
|
|
@ -11,7 +11,7 @@ fn setup() -> Vec<Extension> {
|
|||
vec![
|
||||
deno_webidl::init(),
|
||||
Extension::builder("deno_webidl_bench")
|
||||
.js(vec![("setup", include_str!("dict.js"))])
|
||||
.esm(vec![("internal:setup", include_str!("dict.js"))])
|
||||
.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 lib="esnext" />
|
||||
|
||||
declare namespace globalThis {
|
||||
declare namespace __bootstrap {
|
||||
declare namespace webidl {
|
||||
declare interface ConverterOpts {
|
||||
/**
|
||||
* The prefix for error messages created by this converter.
|
||||
* Examples:
|
||||
* - `Failed to construct 'Event'`
|
||||
* - `Failed to execute 'removeEventListener' on 'EventTarget'`
|
||||
*/
|
||||
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";
|
||||
}
|
||||
declare module "internal:ext/webidl/00_webidl.js" {
|
||||
interface ConverterOpts {
|
||||
/**
|
||||
* The prefix for error messages created by this converter.
|
||||
* Examples:
|
||||
* - `Failed to construct 'Event'`
|
||||
* - `Failed to execute 'removeEventListener' on 'EventTarget'`
|
||||
*/
|
||||
prefix: string;
|
||||
}
|
||||
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.
|
||||
pub fn init() -> Extension {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/webidl",
|
||||
"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.
|
||||
"use strict";
|
||||
|
||||
/// <reference path="../../core/internal.d.ts" />
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { writableStreamClose, Deferred } = window.__bootstrap.streams;
|
||||
const { DOMException } = window.__bootstrap.domException;
|
||||
const { add, remove } = window.__bootstrap.abortSignal;
|
||||
const { headersFromHeaderList, headerListFromHeaders, fillHeaders } =
|
||||
window.__bootstrap.headers;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import { Deferred, writableStreamClose } from "internal:ext/web/06_streams.js";
|
||||
import DOMException from "internal:ext/web/01_dom_exception.js";
|
||||
import { add, remove } from "internal:ext/web/03_abort_signal.js";
|
||||
import {
|
||||
fillHeaders,
|
||||
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 {
|
||||
ArrayPrototypeJoin,
|
||||
ArrayPrototypeMap,
|
||||
Error,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
PromisePrototypeCatch,
|
||||
PromisePrototypeThen,
|
||||
Set,
|
||||
StringPrototypeEndsWith,
|
||||
StringPrototypeToLowerCase,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
Uint8ArrayPrototype,
|
||||
} = window.__bootstrap.primordials;
|
||||
webidl.converters.WebSocketStreamOptions = webidl.createDictionaryConverter(
|
||||
"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: "",
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
webidl.converters.WebSocketStreamOptions = webidl.createDictionaryConverter(
|
||||
"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 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]]");
|
||||
const _url = Symbol("[[url]]");
|
||||
const _connection = Symbol("[[connection]]");
|
||||
const _closed = Symbol("[[closed]]");
|
||||
const _earlyClose = Symbol("[[earlyClose]]");
|
||||
const _closeSent = Symbol("[[closeSent]]");
|
||||
class WebSocketStream {
|
||||
[_rid];
|
||||
[_url];
|
||||
get url() {
|
||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||
return this[_url];
|
||||
}
|
||||
|
||||
[_url];
|
||||
get url() {
|
||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||
return this[_url];
|
||||
constructor(url, options) {
|
||||
this[webidl.brand] = webidl.brand;
|
||||
const prefix = "Failed to construct 'WebSocketStream'";
|
||||
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) {
|
||||
this[webidl.brand] = webidl.brand;
|
||||
const prefix = "Failed to construct 'WebSocketStream'";
|
||||
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",
|
||||
);
|
||||
}
|
||||
|
||||
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 (wsURL.hash !== "" || StringPrototypeEndsWith(wsURL.href, "#")) {
|
||||
throw new DOMException(
|
||||
"Fragments are not allowed in a WebSocket URL.",
|
||||
"SyntaxError",
|
||||
);
|
||||
}
|
||||
|
||||
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),
|
||||
this[_url] = wsURL.href;
|
||||
|
||||
if (
|
||||
options.protocols.length !==
|
||||
new Set(
|
||||
ArrayPrototypeMap(
|
||||
options.protocols,
|
||||
(p) => StringPrototypeToLowerCase(p),
|
||||
),
|
||||
(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,
|
||||
);
|
||||
).size
|
||||
) {
|
||||
throw new DOMException(
|
||||
"Can't supply multiple times the same protocol.",
|
||||
"SyntaxError",
|
||||
);
|
||||
}
|
||||
|
||||
if (kind === "close") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
})(),
|
||||
() => {
|
||||
const err = new DOMException(
|
||||
"Closed while connecting",
|
||||
"NetworkError",
|
||||
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) {
|
||||
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({
|
||||
write: async (chunk) => {
|
||||
if (typeof chunk === "string") {
|
||||
await core.opAsync("op_ws_send", this[_rid], {
|
||||
kind: "text",
|
||||
value: chunk,
|
||||
});
|
||||
} else if (
|
||||
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)
|
||||
) {
|
||||
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",
|
||||
if (kind === "close") {
|
||||
break;
|
||||
}
|
||||
}
|
||||
})(),
|
||||
() => {
|
||||
const err = new DOMException(
|
||||
"Closed while connecting",
|
||||
"NetworkError",
|
||||
);
|
||||
}
|
||||
},
|
||||
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[_connection].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 (
|
||||
this[_closeSent].state === "fulfilled" &&
|
||||
this[_closed].state === "pending"
|
||||
const writable = new WritableStream({
|
||||
write: async (chunk) => {
|
||||
if (typeof chunk === "string") {
|
||||
await core.opAsync("op_ws_send", this[_rid], {
|
||||
kind: "text",
|
||||
value: chunk,
|
||||
});
|
||||
} else if (
|
||||
ObjectPrototypeIsPrototypeOf(Uint8ArrayPrototype, chunk)
|
||||
) {
|
||||
if (
|
||||
new Date().getTime() - await this[_closeSent].promise <=
|
||||
CLOSE_RESPONSE_TIMEOUT
|
||||
) {
|
||||
return pull(controller);
|
||||
}
|
||||
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",
|
||||
);
|
||||
}
|
||||
},
|
||||
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;
|
||||
}
|
||||
};
|
||||
const readable = new ReadableStream({
|
||||
start: (controller) => {
|
||||
PromisePrototypeThen(this.closed, () => {
|
||||
try {
|
||||
controller.close();
|
||||
} catch (_) {
|
||||
// needed to ignore warnings & assertions
|
||||
}
|
||||
try {
|
||||
PromisePrototypeCatch(
|
||||
writableStreamClose(writable),
|
||||
() => {},
|
||||
);
|
||||
} catch (_) {
|
||||
// needed to ignore warnings & assertions
|
||||
}
|
||||
});
|
||||
case "error": {
|
||||
const err = new Error(value);
|
||||
this[_closed].reject(err);
|
||||
controller.error(err);
|
||||
core.tryClose(this[_rid]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
PromisePrototypeThen(this[_closeSent].promise, () => {
|
||||
if (this[_closed].state === "pending") {
|
||||
return pull(controller);
|
||||
}
|
||||
});
|
||||
},
|
||||
pull,
|
||||
cancel: async (reason) => {
|
||||
if (
|
||||
this[_closeSent].state === "fulfilled" &&
|
||||
this[_closed].state === "pending"
|
||||
) {
|
||||
if (
|
||||
new Date().getTime() - await this[_closeSent].promise <=
|
||||
CLOSE_RESPONSE_TIMEOUT
|
||||
) {
|
||||
return pull(controller);
|
||||
}
|
||||
|
||||
this[_closed].resolve(value);
|
||||
core.tryClose(this[_rid]);
|
||||
}
|
||||
};
|
||||
const readable = new ReadableStream({
|
||||
start: (controller) => {
|
||||
PromisePrototypeThen(this.closed, () => {
|
||||
try {
|
||||
this.close(reason?.code !== undefined ? reason : {});
|
||||
controller.close();
|
||||
} catch (_) {
|
||||
this.close();
|
||||
// needed to ignore warnings & assertions
|
||||
}
|
||||
await this.closed;
|
||||
},
|
||||
});
|
||||
try {
|
||||
PromisePrototypeCatch(
|
||||
writableStreamClose(writable),
|
||||
() => {},
|
||||
);
|
||||
} catch (_) {
|
||||
// needed to ignore warnings & assertions
|
||||
}
|
||||
});
|
||||
|
||||
this[_connection].resolve({
|
||||
readable,
|
||||
writable,
|
||||
extensions: create.extensions ?? "",
|
||||
protocol: create.protocol ?? "",
|
||||
});
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
if (ObjectPrototypeIsPrototypeOf(core.InterruptedPrototype, err)) {
|
||||
// The signal was aborted.
|
||||
err = options.signal.reason;
|
||||
} else {
|
||||
core.tryClose(cancelRid);
|
||||
}
|
||||
this[_connection].reject(err);
|
||||
this[_closed].reject(err);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
PromisePrototypeThen(this[_closeSent].promise, () => {
|
||||
if (this[_closed].state === "pending") {
|
||||
return pull(controller);
|
||||
}
|
||||
});
|
||||
},
|
||||
pull,
|
||||
cancel: async (reason) => {
|
||||
try {
|
||||
this.close(reason?.code !== undefined ? reason : {});
|
||||
} catch (_) {
|
||||
this.close();
|
||||
}
|
||||
await this.closed;
|
||||
},
|
||||
});
|
||||
|
||||
[_connection] = new Deferred();
|
||||
get connection() {
|
||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||
return this[_connection].promise;
|
||||
}
|
||||
|
||||
[_earlyClose] = false;
|
||||
[_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,
|
||||
})
|
||||
}`;
|
||||
this[_connection].resolve({
|
||||
readable,
|
||||
writable,
|
||||
extensions: create.extensions ?? "",
|
||||
protocol: create.protocol ?? "",
|
||||
});
|
||||
}
|
||||
},
|
||||
(err) => {
|
||||
if (ObjectPrototypeIsPrototypeOf(core.InterruptedPrototype, err)) {
|
||||
// The signal was aborted.
|
||||
err = options.signal.reason;
|
||||
} else {
|
||||
core.tryClose(cancelRid);
|
||||
}
|
||||
this[_connection].reject(err);
|
||||
this[_closed].reject(err);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const WebSocketStreamPrototype = WebSocketStream.prototype;
|
||||
[_connection] = new Deferred();
|
||||
get connection() {
|
||||
webidl.assertBranded(this, WebSocketStreamPrototype);
|
||||
return this[_connection].promise;
|
||||
}
|
||||
|
||||
window.__bootstrap.webSocket.WebSocketStream = WebSocketStream;
|
||||
})(this);
|
||||
[_earlyClose] = false;
|
||||
[_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::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_url", "deno_webidl"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/websocket",
|
||||
"01_websocket.js",
|
||||
"02_websocketstream.js",
|
||||
|
|
|
@ -2,191 +2,189 @@
|
|||
|
||||
/// <reference path="../../core/internal.d.ts" />
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const {
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
ObjectDefineProperty,
|
||||
ObjectFromEntries,
|
||||
ObjectEntries,
|
||||
ReflectGet,
|
||||
ReflectHas,
|
||||
Proxy,
|
||||
} = window.__bootstrap.primordials;
|
||||
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 {
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
ObjectDefineProperty,
|
||||
ObjectFromEntries,
|
||||
ObjectEntries,
|
||||
ReflectGet,
|
||||
ReflectHas,
|
||||
Proxy,
|
||||
} = primordials;
|
||||
|
||||
const _persistent = Symbol("[[persistent]]");
|
||||
const _persistent = Symbol("[[persistent]]");
|
||||
|
||||
class Storage {
|
||||
[_persistent];
|
||||
class Storage {
|
||||
[_persistent];
|
||||
|
||||
constructor() {
|
||||
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]);
|
||||
}
|
||||
constructor() {
|
||||
webidl.illegalConstructor();
|
||||
}
|
||||
|
||||
const StoragePrototype = Storage.prototype;
|
||||
get length() {
|
||||
webidl.assertBranded(this, StoragePrototype);
|
||||
return ops.op_webstorage_length(this[_persistent]);
|
||||
}
|
||||
|
||||
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 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,
|
||||
};
|
||||
},
|
||||
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",
|
||||
});
|
||||
|
||||
proxy[SymbolFor("Deno.customInspect")] = function (inspect) {
|
||||
return `${this.constructor.name} ${
|
||||
inspect({
|
||||
length: this.length,
|
||||
...ObjectFromEntries(ObjectEntries(proxy)),
|
||||
})
|
||||
}`;
|
||||
};
|
||||
|
||||
return proxy;
|
||||
return ops.op_webstorage_key(index, this[_persistent]);
|
||||
}
|
||||
|
||||
let localStorage;
|
||||
let sessionStorage;
|
||||
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",
|
||||
});
|
||||
|
||||
window.__bootstrap.webStorage = {
|
||||
localStorage() {
|
||||
if (!localStorage) {
|
||||
localStorage = createStorage(true);
|
||||
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;
|
||||
|
||||
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() {
|
||||
if (!sessionStorage) {
|
||||
sessionStorage = createStorage(false);
|
||||
defineProperty(target, key, descriptor) {
|
||||
if (typeof key == "symbol") {
|
||||
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 {
|
||||
Extension::builder(env!("CARGO_PKG_NAME"))
|
||||
.dependencies(vec!["deno_webidl"])
|
||||
.js(include_js_files!(
|
||||
.esm(include_js_files!(
|
||||
prefix "internal:ext/webstorage",
|
||||
"01_webstorage.js",
|
||||
))
|
||||
|
|
|
@ -5,7 +5,6 @@ use std::env;
|
|||
use std::path::PathBuf;
|
||||
|
||||
// This is a shim that allows to generate documentation on docs.rs
|
||||
#[cfg(not(feature = "docsrs"))]
|
||||
mod not_docs {
|
||||
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![
|
||||
deno_webidl::init(),
|
||||
deno_console::init(),
|
||||
|
@ -158,7 +157,8 @@ mod not_docs {
|
|||
startup_snapshot: None,
|
||||
extensions: vec![],
|
||||
extensions_with_js,
|
||||
additional_files: files,
|
||||
additional_files: vec![],
|
||||
additional_esm_files: esm_files,
|
||||
compression_cb: Some(Box::new(|vec, snapshot_slice| {
|
||||
lzzzz::lz4_hc::compress_to_vec(
|
||||
snapshot_slice,
|
||||
|
@ -172,14 +172,19 @@ mod not_docs {
|
|||
|
||||
pub fn build_snapshot(runtime_snapshot_path: PathBuf) {
|
||||
#[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"))]
|
||||
{
|
||||
let manifest = env!("CARGO_MANIFEST_DIR");
|
||||
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.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const { ObjectFreeze, StringPrototypeSplit } = window.__bootstrap.primordials;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const { ObjectFreeze, StringPrototypeSplit } = primordials;
|
||||
|
||||
const build = {
|
||||
target: "unknown",
|
||||
arch: "unknown",
|
||||
os: "unknown",
|
||||
vendor: "unknown",
|
||||
env: undefined,
|
||||
};
|
||||
const build = {
|
||||
target: "unknown",
|
||||
arch: "unknown",
|
||||
os: "unknown",
|
||||
vendor: "unknown",
|
||||
env: undefined,
|
||||
};
|
||||
|
||||
function setBuildInfo(target) {
|
||||
const { 0: arch, 1: vendor, 2: os, 3: env } = StringPrototypeSplit(
|
||||
target,
|
||||
"-",
|
||||
4,
|
||||
);
|
||||
build.target = target;
|
||||
build.arch = arch;
|
||||
build.vendor = vendor;
|
||||
build.os = os;
|
||||
build.env = env;
|
||||
ObjectFreeze(build);
|
||||
}
|
||||
function setBuildInfo(target) {
|
||||
const { 0: arch, 1: vendor, 2: os, 3: env } = StringPrototypeSplit(
|
||||
target,
|
||||
"-",
|
||||
4,
|
||||
);
|
||||
build.target = target;
|
||||
build.arch = arch;
|
||||
build.vendor = vendor;
|
||||
build.os = os;
|
||||
build.env = env;
|
||||
ObjectFreeze(build);
|
||||
}
|
||||
|
||||
window.__bootstrap.build = {
|
||||
build,
|
||||
setBuildInfo,
|
||||
};
|
||||
})(this);
|
||||
export { build, setBuildInfo };
|
||||
|
|
|
@ -1,153 +1,149 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const { Error } = window.__bootstrap.primordials;
|
||||
const { BadResource, Interrupted } = core;
|
||||
const core = globalThis.Deno.core;
|
||||
const { BadResource, Interrupted } = core;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const { Error } = primordials;
|
||||
|
||||
class NotFound extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "NotFound";
|
||||
}
|
||||
class NotFound extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "NotFound";
|
||||
}
|
||||
}
|
||||
|
||||
class PermissionDenied extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "PermissionDenied";
|
||||
}
|
||||
class PermissionDenied extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "PermissionDenied";
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionRefused extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "ConnectionRefused";
|
||||
}
|
||||
class ConnectionRefused extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "ConnectionRefused";
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionReset extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "ConnectionReset";
|
||||
}
|
||||
class ConnectionReset extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "ConnectionReset";
|
||||
}
|
||||
}
|
||||
|
||||
class ConnectionAborted extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "ConnectionAborted";
|
||||
}
|
||||
class ConnectionAborted extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "ConnectionAborted";
|
||||
}
|
||||
}
|
||||
|
||||
class NotConnected extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "NotConnected";
|
||||
}
|
||||
class NotConnected extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "NotConnected";
|
||||
}
|
||||
}
|
||||
|
||||
class AddrInUse extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "AddrInUse";
|
||||
}
|
||||
class AddrInUse extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "AddrInUse";
|
||||
}
|
||||
}
|
||||
|
||||
class AddrNotAvailable extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "AddrNotAvailable";
|
||||
}
|
||||
class AddrNotAvailable extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "AddrNotAvailable";
|
||||
}
|
||||
}
|
||||
|
||||
class BrokenPipe extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "BrokenPipe";
|
||||
}
|
||||
class BrokenPipe extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "BrokenPipe";
|
||||
}
|
||||
}
|
||||
|
||||
class AlreadyExists extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "AlreadyExists";
|
||||
}
|
||||
class AlreadyExists extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "AlreadyExists";
|
||||
}
|
||||
}
|
||||
|
||||
class InvalidData extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "InvalidData";
|
||||
}
|
||||
class InvalidData extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "InvalidData";
|
||||
}
|
||||
}
|
||||
|
||||
class TimedOut extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "TimedOut";
|
||||
}
|
||||
class TimedOut extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "TimedOut";
|
||||
}
|
||||
}
|
||||
|
||||
class WriteZero extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "WriteZero";
|
||||
}
|
||||
class WriteZero extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "WriteZero";
|
||||
}
|
||||
}
|
||||
|
||||
class UnexpectedEof extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "UnexpectedEof";
|
||||
}
|
||||
class UnexpectedEof extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "UnexpectedEof";
|
||||
}
|
||||
}
|
||||
|
||||
class Http extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "Http";
|
||||
}
|
||||
class Http extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "Http";
|
||||
}
|
||||
}
|
||||
|
||||
class Busy extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "Busy";
|
||||
}
|
||||
class Busy extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "Busy";
|
||||
}
|
||||
}
|
||||
|
||||
class NotSupported extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "NotSupported";
|
||||
}
|
||||
class NotSupported extends Error {
|
||||
constructor(msg) {
|
||||
super(msg);
|
||||
this.name = "NotSupported";
|
||||
}
|
||||
}
|
||||
|
||||
const errors = {
|
||||
NotFound,
|
||||
PermissionDenied,
|
||||
ConnectionRefused,
|
||||
ConnectionReset,
|
||||
ConnectionAborted,
|
||||
NotConnected,
|
||||
AddrInUse,
|
||||
AddrNotAvailable,
|
||||
BrokenPipe,
|
||||
AlreadyExists,
|
||||
InvalidData,
|
||||
TimedOut,
|
||||
Interrupted,
|
||||
WriteZero,
|
||||
UnexpectedEof,
|
||||
BadResource,
|
||||
Http,
|
||||
Busy,
|
||||
NotSupported,
|
||||
};
|
||||
const errors = {
|
||||
NotFound,
|
||||
PermissionDenied,
|
||||
ConnectionRefused,
|
||||
ConnectionReset,
|
||||
ConnectionAborted,
|
||||
NotConnected,
|
||||
AddrInUse,
|
||||
AddrNotAvailable,
|
||||
BrokenPipe,
|
||||
AlreadyExists,
|
||||
InvalidData,
|
||||
TimedOut,
|
||||
Interrupted,
|
||||
WriteZero,
|
||||
UnexpectedEof,
|
||||
BadResource,
|
||||
Http,
|
||||
Busy,
|
||||
NotSupported,
|
||||
};
|
||||
|
||||
window.__bootstrap.errors = {
|
||||
errors,
|
||||
};
|
||||
})(this);
|
||||
export { errors };
|
||||
|
|
|
@ -1,29 +1,24 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const { ObjectFreeze } = window.__bootstrap.primordials;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const { ObjectFreeze } = primordials;
|
||||
|
||||
const version = {
|
||||
deno: "",
|
||||
v8: "",
|
||||
typescript: "",
|
||||
};
|
||||
const version = {
|
||||
deno: "",
|
||||
v8: "",
|
||||
typescript: "",
|
||||
};
|
||||
|
||||
function setVersions(
|
||||
denoVersion,
|
||||
v8Version,
|
||||
tsVersion,
|
||||
) {
|
||||
version.deno = denoVersion;
|
||||
version.v8 = v8Version;
|
||||
version.typescript = tsVersion;
|
||||
function setVersions(
|
||||
denoVersion,
|
||||
v8Version,
|
||||
tsVersion,
|
||||
) {
|
||||
version.deno = denoVersion;
|
||||
version.v8 = v8Version;
|
||||
version.typescript = tsVersion;
|
||||
|
||||
ObjectFreeze(version);
|
||||
}
|
||||
ObjectFreeze(version);
|
||||
}
|
||||
|
||||
window.__bootstrap.version = {
|
||||
version,
|
||||
setVersions,
|
||||
};
|
||||
})(this);
|
||||
export { setVersions, version };
|
||||
|
|
|
@ -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.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const {
|
||||
decodeURIComponent,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
Promise,
|
||||
SafeArrayIterator,
|
||||
StringPrototypeReplace,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
const { build } = window.__bootstrap.build;
|
||||
const { URLPrototype } = window.__bootstrap.url;
|
||||
let logDebug = false;
|
||||
let logSource = "JS";
|
||||
const internals = globalThis.__bootstrap.internals;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
decodeURIComponent,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
Promise,
|
||||
SafeArrayIterator,
|
||||
StringPrototypeReplace,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
import { build } from "internal:runtime/js/01_build.js";
|
||||
import { URLPrototype } from "internal:ext/url/00_url.js";
|
||||
let logDebug = false;
|
||||
let logSource = "JS";
|
||||
|
||||
function setLogDebug(debug, source) {
|
||||
logDebug = debug;
|
||||
if (source) {
|
||||
logSource = source;
|
||||
}
|
||||
function setLogDebug(debug, source) {
|
||||
logDebug = debug;
|
||||
if (source) {
|
||||
logSource = source;
|
||||
}
|
||||
}
|
||||
|
||||
function log(...args) {
|
||||
if (logDebug) {
|
||||
// if we destructure `console` off `globalThis` too early, we don't bind to
|
||||
// the right console, therefore we don't log anything out.
|
||||
globalThis.console.log(
|
||||
`DEBUG ${logSource} -`,
|
||||
...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 log(...args) {
|
||||
if (logDebug) {
|
||||
// if we destructure `console` off `globalThis` too early, we don't bind to
|
||||
// the right console, therefore we don't log anything out.
|
||||
globalThis.console.log(
|
||||
`DEBUG ${logSource} -`,
|
||||
...new SafeArrayIterator(args),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function pathFromURL(pathOrUrl) {
|
||||
if (ObjectPrototypeIsPrototypeOf(URLPrototype, pathOrUrl)) {
|
||||
if (pathOrUrl.protocol != "file:") {
|
||||
throw new TypeError("Must be a file URL.");
|
||||
}
|
||||
function createResolvable() {
|
||||
let resolve;
|
||||
let reject;
|
||||
const promise = new Promise((res, rej) => {
|
||||
resolve = res;
|
||||
reject = rej;
|
||||
});
|
||||
promise.resolve = resolve;
|
||||
promise.reject = reject;
|
||||
return promise;
|
||||
}
|
||||
|
||||
return build.os == "windows"
|
||||
? pathFromURLWin32(pathOrUrl)
|
||||
: pathFromURLPosix(pathOrUrl);
|
||||
}
|
||||
return pathOrUrl;
|
||||
// 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.`);
|
||||
}
|
||||
|
||||
window.__bootstrap.internals = {
|
||||
...window.__bootstrap.internals ?? {},
|
||||
pathFromURL,
|
||||
return decodeURIComponent(
|
||||
StringPrototypeReplace(url.pathname, /%(?![0-9A-Fa-f]{2})/g, "%25"),
|
||||
);
|
||||
}
|
||||
|
||||
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) {
|
||||
return {
|
||||
value,
|
||||
writable: true,
|
||||
enumerable: 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,
|
||||
function nonEnumerable(value) {
|
||||
return {
|
||||
value,
|
||||
writable: true,
|
||||
enumerable: false,
|
||||
configurable: true,
|
||||
};
|
||||
})(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.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const { ops } = Deno.core;
|
||||
const { Event } = window.__bootstrap.event;
|
||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
||||
const { pathFromURL } = window.__bootstrap.util;
|
||||
const { illegalConstructorKey } = window.__bootstrap.webUtil;
|
||||
const {
|
||||
ArrayIsArray,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypeSlice,
|
||||
Map,
|
||||
MapPrototypeGet,
|
||||
MapPrototypeHas,
|
||||
MapPrototypeSet,
|
||||
FunctionPrototypeCall,
|
||||
PromiseResolve,
|
||||
PromiseReject,
|
||||
ReflectHas,
|
||||
SafeArrayIterator,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
} = window.__bootstrap.primordials;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
import { Event, EventTarget } from "internal:ext/web/02_event.js";
|
||||
import { pathFromURL } from "internal:runtime/js/06_util.js";
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
ArrayIsArray,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeMap,
|
||||
ArrayPrototypeSlice,
|
||||
Map,
|
||||
MapPrototypeGet,
|
||||
MapPrototypeHas,
|
||||
MapPrototypeSet,
|
||||
FunctionPrototypeCall,
|
||||
PromiseResolve,
|
||||
PromiseReject,
|
||||
ReflectHas,
|
||||
SafeArrayIterator,
|
||||
Symbol,
|
||||
SymbolFor,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
|
||||
/**
|
||||
* @typedef StatusCacheValue
|
||||
* @property {PermissionState} state
|
||||
* @property {PermissionStatus} status
|
||||
*/
|
||||
const illegalConstructorKey = Symbol("illegalConstructorKey");
|
||||
|
||||
/** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "sys" | "run" | "ffi" | "hrtime">} */
|
||||
const permissionNames = [
|
||||
"read",
|
||||
"write",
|
||||
"net",
|
||||
"env",
|
||||
"sys",
|
||||
"run",
|
||||
"ffi",
|
||||
"hrtime",
|
||||
];
|
||||
/**
|
||||
* @typedef StatusCacheValue
|
||||
* @property {PermissionState} state
|
||||
* @property {PermissionStatus} status
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Deno.PermissionDescriptor} desc
|
||||
* @returns {Deno.PermissionState}
|
||||
*/
|
||||
function opQuery(desc) {
|
||||
return ops.op_query_permission(desc);
|
||||
/** @type {ReadonlyArray<"read" | "write" | "net" | "env" | "sys" | "run" | "ffi" | "hrtime">} */
|
||||
const permissionNames = [
|
||||
"read",
|
||||
"write",
|
||||
"net",
|
||||
"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
|
||||
* @returns {Deno.PermissionState}
|
||||
* @param {{ state: Deno.PermissionState }} state
|
||||
* @param {unknown} key
|
||||
*/
|
||||
function opRevoke(desc) {
|
||||
return ops.op_revoke_permission(desc);
|
||||
constructor(state = null, key = null) {
|
||||
if (key != illegalConstructorKey) {
|
||||
throw new TypeError("Illegal constructor.");
|
||||
}
|
||||
super();
|
||||
this.#state = state;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Deno.PermissionDescriptor} desc
|
||||
* @returns {Deno.PermissionState}
|
||||
* @param {Event} event
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function opRequest(desc) {
|
||||
return ops.op_request_permission(desc);
|
||||
dispatchEvent(event) {
|
||||
let dispatched = super.dispatchEvent(event);
|
||||
if (dispatched && this.onchange) {
|
||||
FunctionPrototypeCall(this.onchange, this, event);
|
||||
dispatched = !event.defaultPrevented;
|
||||
}
|
||||
return dispatched;
|
||||
}
|
||||
|
||||
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 {{ 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 })
|
||||
}`;
|
||||
}
|
||||
[SymbolFor("Deno.privateCustomInspect")](inspect) {
|
||||
return `${this.constructor.name} ${
|
||||
inspect({ state: this.state, onchange: this.onchange })
|
||||
}`;
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {Map<string, StatusCacheValue>} */
|
||||
const statusCache = new Map();
|
||||
/** @type {Map<string, StatusCacheValue>} */
|
||||
const statusCache = new Map();
|
||||
|
||||
/**
|
||||
* @param {Deno.PermissionDescriptor} desc
|
||||
* @param {Deno.PermissionState} state
|
||||
* @returns {PermissionStatus}
|
||||
*/
|
||||
function cache(desc, state) {
|
||||
let { name: key } = desc;
|
||||
if (
|
||||
(desc.name === "read" || desc.name === "write" || desc.name === "ffi") &&
|
||||
ReflectHas(desc, "path")
|
||||
) {
|
||||
key += `-${desc.path}&`;
|
||||
} else if (desc.name === "net" && desc.host) {
|
||||
key += `-${desc.host}&`;
|
||||
} else if (desc.name === "run" && desc.command) {
|
||||
key += `-${desc.command}&`;
|
||||
} else if (desc.name === "env" && desc.variable) {
|
||||
key += `-${desc.variable}&`;
|
||||
} else if (desc.name === "sys" && desc.kind) {
|
||||
key += `-${desc.kind}&`;
|
||||
} else {
|
||||
key += "$";
|
||||
/**
|
||||
* @param {Deno.PermissionDescriptor} desc
|
||||
* @param {Deno.PermissionState} state
|
||||
* @returns {PermissionStatus}
|
||||
*/
|
||||
function cache(desc, state) {
|
||||
let { name: key } = desc;
|
||||
if (
|
||||
(desc.name === "read" || desc.name === "write" || desc.name === "ffi") &&
|
||||
ReflectHas(desc, "path")
|
||||
) {
|
||||
key += `-${desc.path}&`;
|
||||
} else if (desc.name === "net" && desc.host) {
|
||||
key += `-${desc.host}&`;
|
||||
} else if (desc.name === "run" && desc.command) {
|
||||
key += `-${desc.command}&`;
|
||||
} else if (desc.name === "env" && desc.variable) {
|
||||
key += `-${desc.variable}&`;
|
||||
} else if (desc.name === "sys" && desc.kind) {
|
||||
key += `-${desc.kind}&`;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
/** @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
|
||||
* @returns {desc is Deno.PermissionDescriptor}
|
||||
*/
|
||||
function isValidDescriptor(desc) {
|
||||
return typeof desc === "object" && desc !== null &&
|
||||
ArrayPrototypeIncludes(permissionNames, desc.name);
|
||||
/**
|
||||
* @param {unknown} desc
|
||||
* @returns {desc is Deno.PermissionDescriptor}
|
||||
*/
|
||||
function isValidDescriptor(desc) {
|
||||
return typeof desc === "object" && desc !== null &&
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Deno.PermissionDescriptor} desc
|
||||
* @returns {desc is Deno.PermissionDescriptor}
|
||||
*/
|
||||
function formDescriptor(desc) {
|
||||
if (
|
||||
desc.name === "read" || desc.name === "write" || desc.name === "ffi"
|
||||
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.`,
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
} 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) {
|
||||
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.`,
|
||||
if (ArrayIsArray(permissions[key])) {
|
||||
serializedPermissions[key] = ArrayPrototypeMap(
|
||||
permissions[key],
|
||||
(path) => pathFromURL(path),
|
||||
);
|
||||
}
|
||||
|
||||
formDescriptor(desc);
|
||||
|
||||
const state = opQuery(desc);
|
||||
return cache(desc, state);
|
||||
}
|
||||
|
||||
revoke(desc) {
|
||||
try {
|
||||
return PromiseResolve(this.revokeSync(desc));
|
||||
} catch (error) {
|
||||
return PromiseReject(error);
|
||||
} else {
|
||||
serializedPermissions[key] = permissions[key];
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
for (
|
||||
const key of new SafeArrayIterator(["env", "hrtime", "net", "sys"])
|
||||
) {
|
||||
if (ArrayIsArray(permissions[key])) {
|
||||
serializedPermissions[key] = ArrayPrototypeSlice(permissions[key]);
|
||||
} else {
|
||||
serializedPermissions[key] = permissions[key];
|
||||
}
|
||||
}
|
||||
|
||||
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 serializedPermissions;
|
||||
}
|
||||
return permissions;
|
||||
}
|
||||
|
||||
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"])
|
||||
) {
|
||||
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);
|
||||
export { Permissions, permissions, PermissionStatus, serializePermissions };
|
||||
|
|
|
@ -1,254 +1,253 @@
|
|||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
"use strict";
|
||||
|
||||
((window) => {
|
||||
const core = window.Deno.core;
|
||||
const ops = core.ops;
|
||||
const {
|
||||
Error,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
StringPrototypeStartsWith,
|
||||
String,
|
||||
SymbolIterator,
|
||||
SymbolToStringTag,
|
||||
} = window.__bootstrap.primordials;
|
||||
const webidl = window.__bootstrap.webidl;
|
||||
const { URL } = window.__bootstrap.url;
|
||||
const { getLocationHref } = window.__bootstrap.location;
|
||||
const { serializePermissions } = window.__bootstrap.permissions;
|
||||
const { log } = window.__bootstrap.util;
|
||||
const { ErrorEvent, MessageEvent, defineEventHandler } =
|
||||
window.__bootstrap.event;
|
||||
const { EventTarget } = window.__bootstrap.eventTarget;
|
||||
const {
|
||||
deserializeJsMessageData,
|
||||
serializeJsMessageData,
|
||||
MessagePortPrototype,
|
||||
} = window.__bootstrap.messagePort;
|
||||
const core = globalThis.Deno.core;
|
||||
const ops = core.ops;
|
||||
const primordials = globalThis.__bootstrap.primordials;
|
||||
const {
|
||||
Error,
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
StringPrototypeStartsWith,
|
||||
String,
|
||||
SymbolIterator,
|
||||
SymbolToStringTag,
|
||||
} = primordials;
|
||||
import * as webidl from "internal:ext/webidl/00_webidl.js";
|
||||
import { URL } from "internal:ext/url/00_url.js";
|
||||
import { getLocationHref } from "internal:ext/web/12_location.js";
|
||||
import { serializePermissions } from "internal:runtime/js/10_permissions.js";
|
||||
import { log } from "internal:runtime/js/06_util.js";
|
||||
import {
|
||||
defineEventHandler,
|
||||
ErrorEvent,
|
||||
EventTarget,
|
||||
MessageEvent,
|
||||
} from "internal:ext/web/02_event.js";
|
||||
import {
|
||||
deserializeJsMessageData,
|
||||
MessagePortPrototype,
|
||||
serializeJsMessageData,
|
||||
} from "internal:ext/web/13_message_port.js";
|
||||
|
||||
function createWorker(
|
||||
specifier,
|
||||
function createWorker(
|
||||
specifier,
|
||||
hasSourceCode,
|
||||
sourceCode,
|
||||
permissions,
|
||||
name,
|
||||
workerType,
|
||||
) {
|
||||
return ops.op_create_worker({
|
||||
hasSourceCode,
|
||||
sourceCode,
|
||||
permissions,
|
||||
name,
|
||||
permissions: serializePermissions(permissions),
|
||||
sourceCode,
|
||||
specifier,
|
||||
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,
|
||||
permissions: serializePermissions(permissions),
|
||||
sourceCode,
|
||||
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) {
|
||||
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) {
|
||||
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,
|
||||
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();
|
||||
this.dispatchEvent(event);
|
||||
// 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.
|
||||
if (e.fileName && !event.defaultPrevented) {
|
||||
globalThis.dispatchEvent(event);
|
||||
}
|
||||
|
||||
#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,
|
||||
});
|
||||
return event.defaultPrevented;
|
||||
}
|
||||
|
||||
this.dispatchEvent(event);
|
||||
// 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.
|
||||
if (e.fileName && !event.defaultPrevented) {
|
||||
window.dispatchEvent(event);
|
||||
#pollControl = async () => {
|
||||
while (this.#status === "RUNNING") {
|
||||
const { 0: type, 1: data } = await hostRecvCtrl(this.#id);
|
||||
|
||||
// If terminate was called then we ignore all messages
|
||||
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 () => {
|
||||
while (this.#status === "RUNNING") {
|
||||
const { 0: type, 1: data } = await hostRecvCtrl(this.#id);
|
||||
|
||||
// If terminate was called then we ignore all messages
|
||||
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}"`);
|
||||
}
|
||||
}
|
||||
#pollMessages = async () => {
|
||||
while (this.#status !== "TERMINATED") {
|
||||
const data = await hostRecvMessage(this.#id);
|
||||
if (this.#status === "TERMINATED" || data === null) {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
#pollMessages = async () => {
|
||||
while (this.#status !== "TERMINATED") {
|
||||
const data = await hostRecvMessage(this.#id);
|
||||
if (this.#status === "TERMINATED" || data === null) {
|
||||
return;
|
||||
}
|
||||
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", {
|
||||
let message, transferables;
|
||||
try {
|
||||
const v = deserializeJsMessageData(data);
|
||||
message = v[0];
|
||||
transferables = v[1];
|
||||
} catch (err) {
|
||||
const event = new MessageEvent("messageerror", {
|
||||
cancelable: false,
|
||||
data: message,
|
||||
ports: transferables.filter((t) =>
|
||||
ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t)
|
||||
),
|
||||
data: err,
|
||||
});
|
||||
this.dispatchEvent(event);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
postMessage(message, transferOrOptions = {}) {
|
||||
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;
|
||||
const data = serializeJsMessageData(message, transfer);
|
||||
if (this.#status === "RUNNING") {
|
||||
hostPostMessage(this.#id, data);
|
||||
}
|
||||
const event = new MessageEvent("message", {
|
||||
cancelable: false,
|
||||
data: message,
|
||||
ports: transferables.filter((t) =>
|
||||
ObjectPrototypeIsPrototypeOf(MessagePortPrototype, t)
|
||||
),
|
||||
});
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
};
|
||||
|
||||
terminate() {
|
||||
if (this.#status !== "TERMINATED") {
|
||||
this.#status = "TERMINATED";
|
||||
hostTerminateWorker(this.#id);
|
||||
}
|
||||
postMessage(message, transferOrOptions = {}) {
|
||||
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;
|
||||
const data = serializeJsMessageData(message, transfer);
|
||||
if (this.#status === "RUNNING") {
|
||||
hostPostMessage(this.#id, data);
|
||||
}
|
||||
|
||||
[SymbolToStringTag] = "Worker";
|
||||
}
|
||||
|
||||
defineEventHandler(Worker.prototype, "error");
|
||||
defineEventHandler(Worker.prototype, "message");
|
||||
defineEventHandler(Worker.prototype, "messageerror");
|
||||
terminate() {
|
||||
if (this.#status !== "TERMINATED") {
|
||||
this.#status = "TERMINATED";
|
||||
hostTerminateWorker(this.#id);
|
||||
}
|
||||
}
|
||||
|
||||
webidl.converters["WorkerType"] = webidl.createEnumConverter("WorkerType", [
|
||||
"classic",
|
||||
"module",
|
||||
]);
|
||||
[SymbolToStringTag] = "Worker";
|
||||
}
|
||||
|
||||
window.__bootstrap.worker = {
|
||||
Worker,
|
||||
};
|
||||
})(this);
|
||||
defineEventHandler(Worker.prototype, "error");
|
||||
defineEventHandler(Worker.prototype, "message");
|
||||
defineEventHandler(Worker.prototype, "messageerror");
|
||||
|
||||
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…
Reference in a new issue