Skip to content

Commit ed28f24

Browse files
authored
Merge pull request #628 from ptrdom/fetch-readable-stream
Fetch request body as ReadableStream
2 parents e7254c8 + eabf4b0 commit ed28f24

File tree

13 files changed

+284
-17
lines changed

13 files changed

+284
-17
lines changed

api-reports/2_12.txt

+15-3
Original file line numberDiff line numberDiff line change
@@ -15625,6 +15625,8 @@ PushSubscriptionJSON[JT] val expirationTime: java.lang.Double
1562515625
PushSubscriptionJSON[JT] val keys: js.Dictionary[String]
1562615626
PushSubscriptionOptions[JT] var applicationServerKey: js.UndefOr[Uint8Array]
1562715627
PushSubscriptionOptions[JT] var userVisibleOnly: js.UndefOr[Boolean]
15628+
QueuingStrategy[JT] var highWaterMark: Int
15629+
QueuingStrategy[JT] var size: js.Function1[T, Int]
1562815630
RTCBundlePolicy[JT]
1562915631
RTCBundlePolicy[SO] val balanced: RTCBundlePolicy
1563015632
RTCBundlePolicy[SO] val `max-bundle` = "max-bundle".asInstanceOf[RTCBundlePolicy]
@@ -15851,14 +15853,24 @@ ReadableStream[JT] def locked: Boolean
1585115853
ReadableStream[JT] def pipeThrough[U](pair: Any, options: Any?): ReadableStream[U]
1585215854
ReadableStream[JT] def pipeTo(dest: WriteableStream[T], options: Any?): Unit
1585315855
ReadableStream[JT] def tee(): js.Array[_ <: ReadableStream[T]]
15856+
ReadableStream[SO] def apply[T](underlyingSource: js.UndefOr[ReadableStreamUnderlyingSource[T]]?, queuingStrategy: js.UndefOr[QueuingStrategy[T]]?): ReadableStream[T]
1585415857
ReadableStreamController[JC] def close(): Unit
1585515858
ReadableStreamController[JC] def desiredSize: Int
15856-
ReadableStreamController[JC] def enqueue(chunk: Chunk[T]): js.UndefOr[Int]
15859+
ReadableStreamController[JC] def enqueue(): Unit
15860+
ReadableStreamController[JC] def enqueue(chunk: T): Unit
1585715861
ReadableStreamController[JC] def error(e: Any): Unit
15858-
ReadableStreamReader[JC] def cancel(reason: Any): js.Promise[Any]
15862+
ReadableStreamReader[JC] def cancel(): js.Promise[Unit]
15863+
ReadableStreamReader[JC] def cancel(reason: Any): js.Promise[Unit]
1585915864
ReadableStreamReader[JC] def closed: js.Promise[ReadableStreamReader[T]]
1586015865
ReadableStreamReader[JC] def read(): js.Promise[Chunk[T]]
1586115866
ReadableStreamReader[JC] def releaseLock(): Unit
15867+
ReadableStreamType[JT]
15868+
ReadableStreamType[SO] val bytes: ReadableStreamType
15869+
ReadableStreamUnderlyingSource[JT] var autoAllocateChunkSize: js.UndefOr[Int]
15870+
ReadableStreamUnderlyingSource[JT] var cancel: js.UndefOr[js.Function1[js.Any, js.UndefOr[js.Promise[Unit]]]]
15871+
ReadableStreamUnderlyingSource[JT] var pull: js.UndefOr[js.Function1[ReadableStreamController[T], js.UndefOr[js.Promise[Unit]]]]
15872+
ReadableStreamUnderlyingSource[JT] var start: js.UndefOr[js.Function1[ReadableStreamController[T], js.UndefOr[js.Promise[Unit]]]]
15873+
ReadableStreamUnderlyingSource[JT] var `type`: js.UndefOr[ReadableStreamType]
1586215874
ReferrerPolicy[JT]
1586315875
ReferrerPolicy[SO] val empty: ReferrerPolicy
1586415876
ReferrerPolicy[SO] val `no-referrer` = "no-referrer".asInstanceOf[ReferrerPolicy]
@@ -25958,7 +25970,7 @@ intl/NumberFormatOptions[JT] var useGrouping: js.UndefOr[Boolean]
2595825970
intl/NumberFormatOptions[SO] def apply(localeMatcher: js.UndefOr[String]?, style: js.UndefOr[String]?, currency: js.UndefOr[String]?, currencyDisplay: js.UndefOr[String]?, useGrouping: js.UndefOr[Boolean]?, minimumIntegerDigits: js.UndefOr[Double]?, minimumFractionDigits: js.UndefOr[Double]?, maximumFractionDigits: js.UndefOr[Double]?, minimumSignificantDigits: js.UndefOr[Double]?, maximumSignificantDigits: js.UndefOr[Double]?): NumberFormatOptions (@deprecated in 2.0.0)
2595925971
package[SO] type AlgorithmIdentifier = Algorithm | String
2596025972
package[SO] type BigInteger = js.typedarray.Uint8Array
25961-
package[SO] type BodyInit = Blob | BufferSource | FormData | String
25973+
package[SO] type BodyInit = Blob | BufferSource | FormData | String | ReadableStream[Uint8Array]
2596225974
package[SO] type BufferSource = ArrayBufferView | ArrayBuffer
2596325975
package[SO] type ByteString = String
2596425976
package[SO] type ClientRect = DOMRect (@deprecated in 2.0.0)

api-reports/2_13.txt

+15-3
Original file line numberDiff line numberDiff line change
@@ -15625,6 +15625,8 @@ PushSubscriptionJSON[JT] val expirationTime: java.lang.Double
1562515625
PushSubscriptionJSON[JT] val keys: js.Dictionary[String]
1562615626
PushSubscriptionOptions[JT] var applicationServerKey: js.UndefOr[Uint8Array]
1562715627
PushSubscriptionOptions[JT] var userVisibleOnly: js.UndefOr[Boolean]
15628+
QueuingStrategy[JT] var highWaterMark: Int
15629+
QueuingStrategy[JT] var size: js.Function1[T, Int]
1562815630
RTCBundlePolicy[JT]
1562915631
RTCBundlePolicy[SO] val balanced: RTCBundlePolicy
1563015632
RTCBundlePolicy[SO] val `max-bundle` = "max-bundle".asInstanceOf[RTCBundlePolicy]
@@ -15851,14 +15853,24 @@ ReadableStream[JT] def locked: Boolean
1585115853
ReadableStream[JT] def pipeThrough[U](pair: Any, options: Any?): ReadableStream[U]
1585215854
ReadableStream[JT] def pipeTo(dest: WriteableStream[T], options: Any?): Unit
1585315855
ReadableStream[JT] def tee(): js.Array[_ <: ReadableStream[T]]
15856+
ReadableStream[SO] def apply[T](underlyingSource: js.UndefOr[ReadableStreamUnderlyingSource[T]]?, queuingStrategy: js.UndefOr[QueuingStrategy[T]]?): ReadableStream[T]
1585415857
ReadableStreamController[JC] def close(): Unit
1585515858
ReadableStreamController[JC] def desiredSize: Int
15856-
ReadableStreamController[JC] def enqueue(chunk: Chunk[T]): js.UndefOr[Int]
15859+
ReadableStreamController[JC] def enqueue(): Unit
15860+
ReadableStreamController[JC] def enqueue(chunk: T): Unit
1585715861
ReadableStreamController[JC] def error(e: Any): Unit
15858-
ReadableStreamReader[JC] def cancel(reason: Any): js.Promise[Any]
15862+
ReadableStreamReader[JC] def cancel(): js.Promise[Unit]
15863+
ReadableStreamReader[JC] def cancel(reason: Any): js.Promise[Unit]
1585915864
ReadableStreamReader[JC] def closed: js.Promise[ReadableStreamReader[T]]
1586015865
ReadableStreamReader[JC] def read(): js.Promise[Chunk[T]]
1586115866
ReadableStreamReader[JC] def releaseLock(): Unit
15867+
ReadableStreamType[JT]
15868+
ReadableStreamType[SO] val bytes: ReadableStreamType
15869+
ReadableStreamUnderlyingSource[JT] var autoAllocateChunkSize: js.UndefOr[Int]
15870+
ReadableStreamUnderlyingSource[JT] var cancel: js.UndefOr[js.Function1[js.Any, js.UndefOr[js.Promise[Unit]]]]
15871+
ReadableStreamUnderlyingSource[JT] var pull: js.UndefOr[js.Function1[ReadableStreamController[T], js.UndefOr[js.Promise[Unit]]]]
15872+
ReadableStreamUnderlyingSource[JT] var start: js.UndefOr[js.Function1[ReadableStreamController[T], js.UndefOr[js.Promise[Unit]]]]
15873+
ReadableStreamUnderlyingSource[JT] var `type`: js.UndefOr[ReadableStreamType]
1586215874
ReferrerPolicy[JT]
1586315875
ReferrerPolicy[SO] val empty: ReferrerPolicy
1586415876
ReferrerPolicy[SO] val `no-referrer` = "no-referrer".asInstanceOf[ReferrerPolicy]
@@ -25958,7 +25970,7 @@ intl/NumberFormatOptions[JT] var useGrouping: js.UndefOr[Boolean]
2595825970
intl/NumberFormatOptions[SO] def apply(localeMatcher: js.UndefOr[String]?, style: js.UndefOr[String]?, currency: js.UndefOr[String]?, currencyDisplay: js.UndefOr[String]?, useGrouping: js.UndefOr[Boolean]?, minimumIntegerDigits: js.UndefOr[Double]?, minimumFractionDigits: js.UndefOr[Double]?, maximumFractionDigits: js.UndefOr[Double]?, minimumSignificantDigits: js.UndefOr[Double]?, maximumSignificantDigits: js.UndefOr[Double]?): NumberFormatOptions (@deprecated in 2.0.0)
2595925971
package[SO] type AlgorithmIdentifier = Algorithm | String
2596025972
package[SO] type BigInteger = js.typedarray.Uint8Array
25961-
package[SO] type BodyInit = Blob | BufferSource | FormData | String
25973+
package[SO] type BodyInit = Blob | BufferSource | FormData | String | ReadableStream[Uint8Array]
2596225974
package[SO] type BufferSource = ArrayBufferView | ArrayBuffer
2596325975
package[SO] type ByteString = String
2596425976
package[SO] type ClientRect = DOMRect (@deprecated in 2.0.0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package org.scalajs.dom
2+
3+
import scala.scalajs.js
4+
5+
/** [[https://streams.spec.whatwg.org/#enumdef-readablestreamtype ReadableStreamType enum]] */
6+
@js.native
7+
sealed trait ReadableStreamType extends js.Any
8+
9+
object ReadableStreamType {
10+
val bytes: ReadableStreamType = "bytes".asInstanceOf[ReadableStreamType]
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.scalajs.dom
2+
3+
opaque type ReadableStreamType <: String = String
4+
5+
object ReadableStreamType {
6+
val bytes: ReadableStreamType = "bytes"
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package org.scalajs.dom
2+
3+
import scala.scalajs.js
4+
5+
/** See [[https://streams.spec.whatwg.org/#qs-api ¶7.1. The queuing strategy API]]
6+
*
7+
* @tparam T
8+
* Type of the Chunks returned by the Stream
9+
*/
10+
trait QueuingStrategy[T] extends js.Object {
11+
12+
/** A non-negative number indicating the high water mark of the stream using this queuing strategy. */
13+
var highWaterMark: Int
14+
15+
/** (non-byte streams only)
16+
*
17+
* The result is used to determine backpressure, manifesting via the appropriate desiredSize property. For readable
18+
* streams, it also governs when the underlying source's [[ReadableStreamUnderlyingSource.pull]] method is called.
19+
*
20+
* A function that computes and returns the finite non-negative size of the given chunk value.
21+
*/
22+
var size: js.Function1[T, Int]
23+
}

dom/src/main/scala/org/scalajs/dom/ReadableStream.scala

+15
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,18 @@ trait ReadableStream[+T] extends js.Object {
9292
*/
9393
def tee(): js.Array[_ <: ReadableStream[T]] = js.native // TODO js.Tuple2[ReadableStream[T], ReadableStream[T]]
9494
}
95+
96+
object ReadableStream {
97+
98+
def apply[T](
99+
underlyingSource: js.UndefOr[ReadableStreamUnderlyingSource[T]] = js.undefined,
100+
queuingStrategy: js.UndefOr[QueuingStrategy[T]] = js.undefined
101+
): ReadableStream[T] = {
102+
js.Dynamic
103+
.newInstance(js.Dynamic.global.ReadableStream)(
104+
underlyingSource.asInstanceOf[js.Any],
105+
queuingStrategy.asInstanceOf[js.Any]
106+
)
107+
.asInstanceOf[ReadableStream[T]]
108+
}
109+
}

dom/src/main/scala/org/scalajs/dom/ReadableStreamController.scala

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package org.scalajs.dom
22

33
import scala.scalajs.js
4-
import scala.scalajs.js.annotation._
4+
import scala.scalajs.js.annotation.JSGlobal
55

66
/** [[https://streams.spec.whatwg.org/#rs-controller-class ¶3.3 Class ReadableStreamController]] of whatwg spec
77
*
@@ -15,7 +15,7 @@ import scala.scalajs.js.annotation._
1515
*/
1616
@js.native
1717
@JSGlobal
18-
class ReadableStreamController[-T](stream: ReadableStream[T] = null) extends js.Object {
18+
class ReadableStreamController[-T] private[this] () extends js.Object {
1919

2020
/** The desiredSize getter returns the desired size to fill the controlled stream’s internal queue. It can be
2121
* negative, if the queue is over-full. An underlying source should use this information to determine when and how to
@@ -39,7 +39,8 @@ class ReadableStreamController[-T](stream: ReadableStream[T] = null) extends js.
3939
* @return
4040
* seems like its an undefOr[Int] of the size
4141
*/
42-
def enqueue(chunk: Chunk[T]): js.UndefOr[Int] = js.native
42+
def enqueue(chunk: T): Unit = js.native
43+
def enqueue(): Unit = js.native
4344

4445
/** The error method will error the readable stream, making all future interactions with it fail with the given error
4546
* e.

dom/src/main/scala/org/scalajs/dom/ReadableStreamReader.scala

+2-4
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,9 @@ class ReadableStreamReader[+T](stream: ReadableStream[T]) extends js.Object {
3030
*
3131
* If the reader is active, the cancel method behaves the same as that for the associated stream. When done, it
3232
* automatically releases the lock.
33-
*
34-
* //todo determine type of reason
3533
*/
36-
// not actually sure what the return type is here
37-
def cancel(reason: Any): js.Promise[Any] = js.native
34+
def cancel(reason: Any): js.Promise[Unit] = js.native
35+
def cancel(): js.Promise[Unit] = js.native
3836

3937
/** See [[https://streams.spec.whatwg.org/#reader-read 3.4.4.3. read()]] of whatwg Stream spec.
4038
*
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package org.scalajs.dom
2+
3+
import scala.scalajs.js
4+
5+
/** See [[https://streams.spec.whatwg.org/#underlying-source-api ¶4.2.3. The underlying source API]] of whatwg streams
6+
* spec.
7+
*
8+
* @tparam T
9+
* Type of the Chunks returned by the Stream
10+
*/
11+
trait ReadableStreamUnderlyingSource[T] extends js.Object {
12+
13+
/** A function that is called immediately during creation of the ReadableStream.
14+
*
15+
* If this setup process is asynchronous, it can return a promise to signal success or failure; a rejected promise
16+
* will error the stream. Any thrown exceptions will be re-thrown by the [[ReadableStream]] constructor.
17+
*/
18+
var start: js.UndefOr[js.Function1[ReadableStreamController[T], js.UndefOr[js.Promise[Unit]]]] = js.undefined
19+
20+
/** A function that is called whenever the stream’s internal queue of chunks becomes not full, i.e. whenever the
21+
* queue’s desired size becomes positive. Generally, it will be called repeatedly until the queue reaches its high
22+
* water mark (i.e. until the desired size becomes non-positive).
23+
*
24+
* This function will not be called until [[start]] successfully completes. Additionally, it will only be called
25+
* repeatedly if it enqueues at least one chunk or fulfills a BYOB request; a no-op [[pull]] implementation will not
26+
* be continually called.
27+
*
28+
* If the function returns a promise, then it will not be called again until that promise fulfills. (If the promise
29+
* rejects, the stream will become errored.) This is mainly used in the case of pull sources, where the promise
30+
* returned represents the process of acquiring a new chunk. Throwing an exception is treated the same as returning a
31+
* rejected promise.
32+
*/
33+
var pull: js.UndefOr[js.Function1[ReadableStreamController[T], js.UndefOr[js.Promise[Unit]]]] = js.undefined
34+
35+
/** A function that is called whenever the consumer cancels the stream, via [[ReadableStream.cancel]] or
36+
* [[ReadableStreamReader.cancel():scala\.scalajs\.js\.Promise[Unit]*]]. It takes as its argument the same value as
37+
* was passed to those methods by the consumer. If the shutdown process is asynchronous, it can return a promise to
38+
* signal success or failure; the result will be communicated via the return value of the [[cancel]] method that was
39+
* called. Additionally, a rejected promise will error the stream, instead of letting it close. Throwing an exception
40+
* is treated the same as returning a rejected promise.
41+
*/
42+
var cancel: js.UndefOr[js.Function1[js.Any, js.UndefOr[js.Promise[Unit]]]] = js.undefined
43+
44+
/** Can be set to "bytes" to signal that the constructed [[ReadableStream]] is a readable byte stream.
45+
*
46+
* Setting any value other than "bytes" or undefined will cause the ReadableStream() constructor to throw an
47+
* exception.
48+
*/
49+
var `type`: js.UndefOr[ReadableStreamType] = js.undefined
50+
51+
/** (byte streams only)
52+
*
53+
* Can be set to a positive integer to cause the implementation to automatically allocate buffers for the underlying
54+
* source code to write into.
55+
*/
56+
var autoAllocateChunkSize: js.UndefOr[Int] = js.undefined
57+
}

dom/src/main/scala/org/scalajs/dom/package.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package org.scalajs
22

33
import scala.scalajs.js
44
import scala.scalajs.js.annotation._
5-
import scala.scalajs.js.typedarray.{ArrayBuffer, ArrayBufferView}
5+
import scala.scalajs.js.typedarray.{ArrayBuffer, ArrayBufferView, Uint8Array}
66
import scala.scalajs.js.|
77

88
package object dom {
@@ -32,7 +32,7 @@ package object dom {
3232

3333
/** defined at [[https://fetch.spec.whatwg.org/#body-mixin ¶6.2 Body mixin]] in whatwg Fetch spec */
3434
type BodyInit =
35-
Blob | BufferSource | FormData | String // todo: add URLSearchParams
35+
Blob | BufferSource | FormData | String | ReadableStream[Uint8Array] // todo: add URLSearchParams
3636

3737
/** WebIDL sequence<T> is js.Array[T] | JSIterable[T]. However @mseddon knows at least Blink's IDL compiler treats
3838
* these as simply js.Array[T] for now. We keep this type as a reminder to check in more detail

tests-chrome/src/test/scala/org/scalajs/dom/tests/chrome/ChromeTests.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ package org.scalajs.dom.tests.chrome
33
import org.scalajs.dom.tests.shared._
44
import org.scalajs.dom.tests.webworker._
55

6-
class ChromeTests extends SharedTests with WebWorkerTests
6+
class ChromeTests extends SharedTests with WebWorkerTests with BrowserTests

tests-firefox/src/test/scala/org/scalajs/dom/tests/firefox/FirefoxTests.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ package org.scalajs.dom.tests.firefox
33
import org.scalajs.dom.tests.shared._
44
import org.scalajs.dom.tests.webworker._
55

6-
class FirefoxTests extends SharedTests with WebWorkerTests
6+
class FirefoxTests extends SharedTests with WebWorkerTests with BrowserTests

0 commit comments

Comments
 (0)