summaryrefslogtreecommitdiff
path: root/ext
diff options
context:
space:
mode:
Diffstat (limited to 'ext')
-rw-r--r--ext/fetch/22_body.js14
-rw-r--r--ext/fetch/lib.deno_fetch.d.ts2
-rw-r--r--ext/web/06_streams.js63
-rw-r--r--ext/webidl/00_webidl.js126
-rw-r--r--ext/webidl/internal.d.ts26
5 files changed, 183 insertions, 48 deletions
diff --git a/ext/fetch/22_body.js b/ext/fetch/22_body.js
index 61a06b4af..c7e977c0b 100644
--- a/ext/fetch/22_body.js
+++ b/ext/fetch/22_body.js
@@ -15,6 +15,7 @@ import { core, primordials } from "ext:core/mod.js";
const {
isAnyArrayBuffer,
isArrayBuffer,
+ isStringObject,
} = core;
const {
ArrayBufferIsView,
@@ -466,6 +467,8 @@ function extractBody(object) {
if (object.locked || isReadableStreamDisturbed(object)) {
throw new TypeError("ReadableStream is locked or disturbed");
}
+ } else if (object[webidl.AsyncIterable] === webidl.AsyncIterable) {
+ stream = ReadableStream.from(object.open());
}
if (typeof source === "string") {
// WARNING: this deviates from spec (expects length to be set)
@@ -483,6 +486,9 @@ function extractBody(object) {
return { body, contentType };
}
+webidl.converters["async iterable<Uint8Array>"] = webidl
+ .createAsyncIterableConverter(webidl.converters.Uint8Array);
+
webidl.converters["BodyInit_DOMString"] = (V, prefix, context, opts) => {
// Union for (ReadableStream or Blob or ArrayBufferView or ArrayBuffer or FormData or URLSearchParams or USVString)
if (ObjectPrototypeIsPrototypeOf(ReadableStreamPrototype, V)) {
@@ -501,6 +507,14 @@ webidl.converters["BodyInit_DOMString"] = (V, prefix, context, opts) => {
if (ArrayBufferIsView(V)) {
return webidl.converters["ArrayBufferView"](V, prefix, context, opts);
}
+ if (webidl.isAsyncIterable(V) && !isStringObject(V)) {
+ return webidl.converters["async iterable<Uint8Array>"](
+ V,
+ prefix,
+ context,
+ 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.
diff --git a/ext/fetch/lib.deno_fetch.d.ts b/ext/fetch/lib.deno_fetch.d.ts
index d219a3859..8614dec89 100644
--- a/ext/fetch/lib.deno_fetch.d.ts
+++ b/ext/fetch/lib.deno_fetch.d.ts
@@ -163,6 +163,8 @@ type BodyInit =
| FormData
| URLSearchParams
| ReadableStream<Uint8Array>
+ | Iterable<Uint8Array>
+ | AsyncIterable<Uint8Array>
| string;
/** @category Fetch */
type RequestDestination =
diff --git a/ext/web/06_streams.js b/ext/web/06_streams.js
index a4f2275c5..f29e5f204 100644
--- a/ext/web/06_streams.js
+++ b/ext/web/06_streams.js
@@ -70,7 +70,6 @@ const {
String,
Symbol,
SymbolAsyncIterator,
- SymbolIterator,
SymbolFor,
TypeError,
TypedArrayPrototypeGetBuffer,
@@ -5084,34 +5083,6 @@ function initializeCountSizeFunction(globalObject) {
WeakMapPrototypeSet(countSizeFunctionWeakMap, globalObject, size);
}
-// Ref: https://tc39.es/ecma262/#sec-getiterator
-function getAsyncOrSyncIterator(obj) {
- let iterator;
- if (obj[SymbolAsyncIterator] != null) {
- iterator = obj[SymbolAsyncIterator]();
- if (!isObject(iterator)) {
- throw new TypeError(
- "[Symbol.asyncIterator] returned a non-object value",
- );
- }
- } else if (obj[SymbolIterator] != null) {
- iterator = obj[SymbolIterator]();
- if (!isObject(iterator)) {
- throw new TypeError("[Symbol.iterator] returned a non-object value");
- }
- } else {
- throw new TypeError("No iterator found");
- }
- if (typeof iterator.next !== "function") {
- throw new TypeError("iterator.next is not a function");
- }
- return iterator;
-}
-
-function isObject(x) {
- return (typeof x === "object" && x != null) || typeof x === "function";
-}
-
const _resourceBacking = Symbol("[[resourceBacking]]");
// This distinction exists to prevent unrefable streams being used in
// regular fast streams that are unaware of refability
@@ -5197,21 +5168,22 @@ class ReadableStream {
}
static from(asyncIterable) {
+ const prefix = "Failed to execute 'ReadableStream.from'";
webidl.requiredArguments(
arguments.length,
1,
- "Failed to execute 'ReadableStream.from'",
+ prefix,
);
- asyncIterable = webidl.converters.any(asyncIterable);
-
- const iterator = getAsyncOrSyncIterator(asyncIterable);
+ asyncIterable = webidl.converters["async iterable<any>"](
+ asyncIterable,
+ prefix,
+ "Argument 1",
+ );
+ const iter = asyncIterable.open();
const stream = createReadableStream(noop, async () => {
// deno-lint-ignore prefer-primordials
- const res = await iterator.next();
- if (!isObject(res)) {
- throw new TypeError("iterator.next value is not an object");
- }
+ const res = await iter.next();
if (res.done) {
readableStreamDefaultControllerClose(stream[_controller]);
} else {
@@ -5221,17 +5193,8 @@ class ReadableStream {
);
}
}, async (reason) => {
- if (iterator.return == null) {
- return undefined;
- } else {
- // deno-lint-ignore prefer-primordials
- const res = await iterator.return(reason);
- if (!isObject(res)) {
- throw new TypeError("iterator.return value is not an object");
- } else {
- return undefined;
- }
- }
+ // deno-lint-ignore prefer-primordials
+ await iter.return(reason);
}, 0);
return stream;
}
@@ -6892,6 +6855,10 @@ webidl.converters.StreamPipeOptions = webidl
{ key: "signal", converter: webidl.converters.AbortSignal },
]);
+webidl.converters["async iterable<any>"] = webidl.createAsyncIterableConverter(
+ webidl.converters.any,
+);
+
internals.resourceForReadableStream = resourceForReadableStream;
export {
diff --git a/ext/webidl/00_webidl.js b/ext/webidl/00_webidl.js
index 1d05aae5f..eb18cbcc3 100644
--- a/ext/webidl/00_webidl.js
+++ b/ext/webidl/00_webidl.js
@@ -26,6 +26,7 @@ const {
Float32Array,
Float64Array,
FunctionPrototypeBind,
+ FunctionPrototypeCall,
Int16Array,
Int32Array,
Int8Array,
@@ -77,6 +78,7 @@ const {
StringPrototypeToWellFormed,
Symbol,
SymbolIterator,
+ SymbolAsyncIterator,
SymbolToStringTag,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetSymbolToStringTag,
@@ -920,6 +922,127 @@ function createSequenceConverter(converter) {
};
}
+function isAsyncIterable(obj) {
+ if (obj[SymbolAsyncIterator] === undefined) {
+ if (obj[SymbolIterator] === undefined) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+const AsyncIterable = Symbol("[[asyncIterable]]");
+
+function createAsyncIterableConverter(converter) {
+ return function (
+ V,
+ prefix = undefined,
+ context = undefined,
+ opts = { __proto__: null },
+ ) {
+ if (type(V) !== "Object") {
+ throw makeException(
+ TypeError,
+ "can not be converted to async iterable.",
+ prefix,
+ context,
+ );
+ }
+
+ let isAsync = true;
+ let method = V[SymbolAsyncIterator];
+ if (method === undefined) {
+ method = V[SymbolIterator];
+
+ if (method === undefined) {
+ throw makeException(
+ TypeError,
+ "is not iterable.",
+ prefix,
+ context,
+ );
+ }
+
+ isAsync = false;
+ }
+
+ return {
+ value: V,
+ [AsyncIterable]: AsyncIterable,
+ open(context) {
+ const iter = FunctionPrototypeCall(method, V);
+ if (type(iter) !== "Object") {
+ throw new TypeError(
+ `${context} could not be iterated because iterator method did not return object, but ${
+ type(iter)
+ }.`,
+ );
+ }
+
+ let asyncIterator = iter;
+
+ if (!isAsync) {
+ asyncIterator = {
+ // deno-lint-ignore require-await
+ async next() {
+ // deno-lint-ignore prefer-primordials
+ return iter.next();
+ },
+ };
+ }
+
+ return {
+ async next() {
+ // deno-lint-ignore prefer-primordials
+ const iterResult = await asyncIterator.next();
+ if (type(iterResult) !== "Object") {
+ throw TypeError(
+ `${context} failed to iterate next value because the next() method did not return an object, but ${
+ type(iterResult)
+ }.`,
+ );
+ }
+
+ if (iterResult.done) {
+ return { done: true };
+ }
+
+ const iterValue = converter(
+ iterResult.value,
+ `${context} failed to iterate next value`,
+ `The value returned from the next() method`,
+ opts,
+ );
+
+ return { done: false, value: iterValue };
+ },
+ async return(reason) {
+ if (asyncIterator.return === undefined) {
+ return undefined;
+ }
+
+ // deno-lint-ignore prefer-primordials
+ const returnPromiseResult = await asyncIterator.return(reason);
+ if (type(returnPromiseResult) !== "Object") {
+ throw TypeError(
+ `${context} failed to close iterator because the return() method did not return an object, but ${
+ type(returnPromiseResult)
+ }.`,
+ );
+ }
+
+ return undefined;
+ },
+ [SymbolAsyncIterator]() {
+ return this;
+ },
+ };
+ },
+ };
+ };
+}
+
function createRecordConverter(keyConverter, valueConverter) {
return (V, prefix, context, opts) => {
if (type(V) !== "Object") {
@@ -1302,9 +1425,11 @@ function setlike(obj, objPrototype, readonly) {
export {
assertBranded,
+ AsyncIterable,
brand,
configureInterface,
converters,
+ createAsyncIterableConverter,
createBranded,
createDictionaryConverter,
createEnumConverter,
@@ -1315,6 +1440,7 @@ export {
createSequenceConverter,
illegalConstructor,
invokeCallbackFunction,
+ isAsyncIterable,
makeException,
mixinPairIterable,
requiredArguments,
diff --git a/ext/webidl/internal.d.ts b/ext/webidl/internal.d.ts
index 1ce45463e..375d548d3 100644
--- a/ext/webidl/internal.d.ts
+++ b/ext/webidl/internal.d.ts
@@ -439,6 +439,27 @@ declare module "ext:deno_webidl/00_webidl.js" {
) => T[];
/**
+ * Create a converter that converts an async iterable of the inner type.
+ */
+ function createAsyncIterableConverter<V, T>(
+ converter: (
+ v: V,
+ prefix?: string,
+ context?: string,
+ opts?: any,
+ ) => T,
+ ): (
+ v: any,
+ prefix?: string,
+ context?: string,
+ opts?: any,
+ ) => ConvertedAsyncIterable<V, T>;
+
+ interface ConvertedAsyncIterable<V, T> extends AsyncIterableIterator<T> {
+ value: V;
+ }
+
+ /**
* Create a converter that converts a Promise of the inner type.
*/
function createPromiseConverter<T>(
@@ -559,4 +580,9 @@ declare module "ext:deno_webidl/00_webidl.js" {
| "Symbol"
| "BigInt"
| "Object";
+
+ /**
+ * Check whether a value is an async iterable.
+ */
+ function isAsyncIterable(v: any): boolean;
}