1
0
Fork 0
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:
Leo Kettmeir 2023-02-07 20:22:46 +01:00 committed by GitHub
parent 65500f36e8
commit b4aa153097
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
123 changed files with 41574 additions and 41713 deletions

View file

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

View file

@ -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")]
{

File diff suppressed because it is too large Load diff

View file

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

View file

@ -1,5 +1,4 @@
1
queueMicrotask
error: Uncaught Error: bar
throw new Error("bar");
^

View file

@ -0,0 +1 @@
await import("internal:runtime/js/01_build.js");

View 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

View file

@ -0,0 +1 @@
import "internal:runtime/js/01_build.js";

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

View file

@ -427,6 +427,8 @@
});
ObjectAssign(globalThis.__bootstrap, { core });
const internals = {};
ObjectAssign(globalThis.__bootstrap, { internals });
ObjectAssign(globalThis.Deno, { core });
// Direct bindings on `globalThis`

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View file

@ -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
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -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
View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

File diff suppressed because it is too large Load diff

View file

@ -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 frs state is "loading", throw an InvalidStateError DOMException.
if (this[state] === "loading") {
throw new DOMException(
"Invalid FileReader state.",
"InvalidStateError",
);
}
// 2. Set frs state to "loading".
this[state] = "loading";
// 3. Set frs result to null.
this[result] = null;
// 4. Set frs error to null.
this[error] = null;
/**
* @param {Blob} blob
* @param {{kind: "ArrayBuffer" | "Text" | "DataUrl" | "BinaryString", encoding?: string}} readtype
*/
#readOperation(blob, readtype) {
// 1. If frs state is "loading", throw an InvalidStateError DOMException.
if (this[state] === "loading") {
throw new DOMException(
"Invalid FileReader state.",
"InvalidStateError",
);
}
// 2. Set frs state to "loading".
this[state] = "loading";
// 3. Set frs result to null.
this[result] = null;
// 4. Set frs 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 frs state to "done".
this[state] = "done";
// 2. Let result be the result of package data given bytes, type, blobs 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 frs 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 frs state to "done".
this[state] = "done";
this[error] = err;
// 2. Let result be the result of package data given bytes, type, blobs 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 frs 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 frs 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 frs 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 };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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