Skip to content

Commit d97a405

Browse files
authored
Merge pull request #13 from piboistudios/dev
2 parents b9a4a33 + 9a09de6 commit d97a405

File tree

5 files changed

+200
-2
lines changed

5 files changed

+200
-2
lines changed

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@octetstream/eslint-config": "6.2.2",
4545
"@types/mime-types": "2.1.1",
4646
"@types/node": "18.7.23",
47+
"@types/sinon": "^10.0.13",
4748
"@typescript-eslint/eslint-plugin": "5.38.1",
4849
"@typescript-eslint/parser": "5.38.1",
4950
"ava": "4.3.3",
@@ -60,8 +61,10 @@
6061
"husky": "8.0.1",
6162
"lint-staged": "13.0.3",
6263
"pinst": "3.0.0",
64+
"sinon": "^14.0.2",
6365
"ts-node": "10.9.1",
6466
"ttypescript": "1.5.13",
65-
"typescript": "4.8.4"
67+
"typescript": "4.8.4",
68+
"web-streams-polyfill": "4.0.0-beta.3"
6669
}
6770
}

pnpm-lock.yaml

Lines changed: 101 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/FormDataEncoder.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable no-restricted-globals */
22
import type {RawHeaders, FormDataEncoderHeaders} from "./util/Headers.js"
3+
import {getStreamIterator} from "./util/getStreamIterator.js"
34
import {createBoundary} from "./util/createBoundary.js"
45
import {normalizeValue} from "./util/normalizeValue.js"
56
import {isPlainObject} from "./util/isPlainObject.js"
@@ -336,7 +337,7 @@ export class FormDataEncoder {
336337
async* encode(): AsyncGenerator<Uint8Array, void, undefined> {
337338
for (const part of this.values()) {
338339
if (isFile(part)) {
339-
yield* part.stream()
340+
yield* getStreamIterator(part.stream())
340341
} else {
341342
yield part
342343
}

src/util/getStreamIterator.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import test from "ava"
2+
3+
import {ReadableStream} from "web-streams-polyfill"
4+
import {stub} from "sinon"
5+
6+
import {getStreamIterator} from "./getStreamIterator.js"
7+
8+
test(
9+
"Returns readable stream as is, if it implements Symbol.asyncIterator",
10+
11+
t => {
12+
const stream = new ReadableStream()
13+
14+
t.is(getStreamIterator(stream), stream)
15+
}
16+
)
17+
18+
test(
19+
"Returns fallback when given stream does not implement Symbol.asyncIterator",
20+
21+
t => {
22+
const stream = new ReadableStream()
23+
24+
stub(stream, Symbol.asyncIterator).get(() => undefined)
25+
26+
t.false(getStreamIterator(stream) instanceof ReadableStream)
27+
}
28+
)
29+
30+
test("Reads from the stream using fallback", async t => {
31+
const expected = "Some text"
32+
33+
const stream = new ReadableStream({
34+
pull(controller) {
35+
controller.enqueue(new TextEncoder().encode(expected))
36+
controller.close()
37+
}
38+
})
39+
40+
stub(stream, Symbol.asyncIterator).get(() => undefined)
41+
42+
let actual = ""
43+
const decoder = new TextDecoder()
44+
for await (const chunk of getStreamIterator(stream)) {
45+
actual += decoder.decode(chunk, {stream: true})
46+
}
47+
48+
actual += decoder.decode()
49+
50+
t.is(actual, expected)
51+
})

src/util/getStreamIterator.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import {isFunction} from "./isFunction.js"
2+
3+
/**
4+
* Checks if the value is async iterable
5+
*/
6+
const isAsyncIterable = (
7+
value: unknown
8+
): value is AsyncIterable<Uint8Array> => (
9+
isFunction((value as AsyncIterable<Uint8Array>)[Symbol.asyncIterator])
10+
)
11+
12+
/**
13+
* Reads from given ReadableStream
14+
*
15+
* @param readable A ReadableStream to read from
16+
*/
17+
async function* readStream(
18+
readable: ReadableStream<Uint8Array>
19+
): AsyncGenerator<Uint8Array, void, undefined> {
20+
const reader = readable.getReader()
21+
22+
while (true) {
23+
const {done, value} = await reader.read()
24+
25+
if (done) {
26+
break
27+
}
28+
29+
yield value
30+
}
31+
}
32+
33+
/**
34+
* Turns ReadableStream into async iterable when the `Symbol.asyncIterable` is not implemented on given stream.
35+
*
36+
* @param source A ReadableStream to create async iterator for
37+
*/
38+
export const getStreamIterator = (
39+
source: ReadableStream<Uint8Array> | AsyncIterable<Uint8Array>
40+
): AsyncIterable<Uint8Array> => (
41+
isAsyncIterable(source) ? source : readStream(source)
42+
)

0 commit comments

Comments
 (0)