web-streams-shim 1.0.5 → 1.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -19
- package/ReadableStreamBYOBReader.js +324 -169
- package/extensions/file.js +43 -0
- package/extensions/location.js +42 -0
- package/extensions/web-streams-extensions.js +1 -0
- package/package.json +23 -2
- package/test/test.html +550 -0
- package/web-streams-core.js +334 -453
package/README.md
CHANGED
|
@@ -6,15 +6,21 @@ This library provides essential polyfills and shims to ensure modern Web Streams
|
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
|
+
At the top of your web page put
|
|
10
|
+
|
|
9
11
|
```html
|
|
10
|
-
<script src="https://cdn.jsdelivr.net/npm/web-streams-shim
|
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/web-streams-shim/web-streams-core.js"></script>
|
|
11
13
|
```
|
|
12
14
|
|
|
15
|
+
Dynamic import
|
|
16
|
+
|
|
17
|
+
`await import('`[https://cdn.jsdelivr.net/npm/web-streams-shim/web-streams-core.js](https://cdn.jsdelivr.net/npm/web-streams-shim/web-streams-core.js)`');`
|
|
18
|
+
|
|
13
19
|
***
|
|
14
20
|
|
|
15
21
|
## Key Features and Polyfills
|
|
16
22
|
|
|
17
|
-
The library focuses on extending core browser APIs to meet the latest Web Stream and Fetch specifications.
|
|
23
|
+
The library focuses on extending core browser APIs to meet the latest Web Stream and Fetch specifications. Compatibility tables are generated dynamically from mdn's caniuse api.
|
|
18
24
|
|
|
19
25
|
### Conditional Filling
|
|
20
26
|
|
|
@@ -24,39 +30,68 @@ Each polyfill performs feature detection before initializing. If a feature is de
|
|
|
24
30
|
|
|
25
31
|
The library adds **comprehensive support for modern JavaScript iteration patterns** to `ReadableStream` and its readers.
|
|
26
32
|
|
|
27
|
-
|
|
33
|
+

|
|
34
|
+
|
|
35
|
+
| | | |
|
|
28
36
|
| :--- | :--- | :--- |
|
|
29
37
|
| [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) | [`[Symbol.asyncIterator]`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) | Allows the stream to be directly iterable in `for-await-of` loops. |
|
|
30
|
-
| [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) | `values()` | An alias for `[Symbol.asyncIterator]` for explicit iteration. |
|
|
31
38
|
|
|
32
|
-

|
|
33
39
|

|
|
34
40
|
|
|
41
|
+
| | | |
|
|
42
|
+
| :--- | :--- | :--- |
|
|
43
|
+
| [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) | `values()` | An alias for `[Symbol.asyncIterator]` for explicit iteration. |
|
|
44
|
+
|
|
45
|
+
|
|
35
46
|
### Stream Construction Utility
|
|
36
47
|
|
|
37
48
|
The library adds the static method for creating streams from existing data sources.
|
|
38
49
|
|
|
39
|
-
|
|
|
50
|
+
| | | |
|
|
40
51
|
| :--- | :--- | :--- |
|
|
41
52
|
| [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream) | [`from(obj)`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/from_static) | **Creates a new `ReadableStream` from any iterable or async iterable object**. It handles both synchronous and asynchronous iterators, including objects that yield `Promise`-like values. |
|
|
42
53
|
|
|
43
|
-

|
|
44
55
|
|
|
45
|
-
### Body
|
|
56
|
+
### Body Shim
|
|
46
57
|
|
|
47
|
-
|
|
58
|
+
This shim ensures `Request` and `Response` objects consistently expose their body as a stream.
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+

|
|
61
|
+
|
|
62
|
+
| | | |
|
|
50
63
|
| :--- | :--- | :--- |
|
|
51
64
|
| [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/body) | [`body`](https://developer.mozilla.org/en-US/docs/Web/API/Request/body) | Polyfills the `body` property to return a **`ReadableStream` representation of the body content**. This is crucial for environments where `fetch` exists but streaming is absent. |
|
|
52
|
-
| [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response/body) | [`body`](https://developer.mozilla.org/en-US/docs/Web/API/Response/body) | Provides the body content as a `ReadableStream`. |
|
|
53
|
-
| [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/bytes), [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response/bytes), [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/bytes) | [`bytes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Adds the `bytes()` method, which **asynchronously returns the object's body/content as a `Uint8Array`**. |
|
|
54
65
|
|
|
55
|
-

|
|
56
66
|

|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
67
|
+
|
|
68
|
+
| | | |
|
|
69
|
+
| :--- | :--- | :--- |
|
|
70
|
+
| [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response/body) | [`body`](https://developer.mozilla.org/en-US/docs/Web/API/Response/body) | Provides the body content as a `ReadableStream`. |
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### Bytes Shim
|
|
74
|
+
|
|
75
|
+
This shim ensures `Request` and `Response`, and `Blob` objects consistently provide the `bytes()` utility.
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
| | | |
|
|
80
|
+
| :--- | :--- | :--- |
|
|
81
|
+
| [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request/bytes) | [`bytes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Adds the `bytes()` method, which **asynchronously returns the object's body/content as a `Uint8Array`**. |
|
|
82
|
+
|
|
83
|
+

|
|
84
|
+
|
|
85
|
+
| | | |
|
|
86
|
+
| :--- | :--- | :--- |
|
|
87
|
+
| [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response/bytes) | [`bytes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Adds the `bytes()` method, which **asynchronously returns the object's body/content as a `Uint8Array`**. |
|
|
88
|
+
|
|
89
|
+

|
|
90
|
+
|
|
91
|
+
| | | |
|
|
92
|
+
| :--- | :--- | :--- |
|
|
93
|
+
| [`Blob`](https://developer.mozilla.org/en-US/docs/Web/API/Blob/bytes) | [`bytes()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Uint8Array) | Adds the `bytes()` method, which **asynchronously returns the object's body/content as a `Uint8Array`**. |
|
|
94
|
+
|
|
60
95
|
|
|
61
96
|
### Duplex Compliance Shim
|
|
62
97
|
|
|
@@ -66,14 +101,29 @@ To satisfy modern `fetch` specifications when streaming request bodies, the libr
|
|
|
66
101
|
* **Constructor Wrapping:** The global `Request` and `Response` constructors are subclassed and **wrapped** to automatically apply `duplex: 'half'` utility function to all arguments passed during instantiation.
|
|
67
102
|
* **Fetch Wrapping:** The global `fetch` function is **wrapped** to automatically apply `duplex: 'half'` to its arguments before execution, guaranteeing compliance when streams are used in options.
|
|
68
103
|
|
|
69
|
-

|
|
105
|
+
|
|
70
106
|
|
|
71
107
|
### ReadableStreamDefaultReader Constructor Support
|
|
72
108
|
|
|
73
109
|
The library adds support for the `ReadableStreamDefaultReader` constructor.
|
|
74
110
|
|
|
75
|
-
|
|
111
|
+

|
|
112
|
+
|
|
113
|
+
| | | |
|
|
76
114
|
| :--- | :--- | :--- |
|
|
77
115
|
| [`ReadableStreamDefaultReader`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader) | [`constructor(stream)`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStreamDefaultReader/ReadableStreamDefaultReader) | **Polyfills the `ReadableStreamDefaultReader` constructor** to accept a stream directly. In environments where the native constructor doesn't support this (like Bun), it delegates to `stream.getReader()` and properly sets up the prototype chain. This allows `new ReadableStreamDefaultReader(stream)` to work consistently across all runtimes. |
|
|
78
116
|
|
|
79
|
-
|
|
117
|
+
|
|
118
|
+
### Additional Shims
|
|
119
|
+
|
|
120
|
+

|
|
121
|
+
|
|
122
|
+

|
|
123
|
+
|
|
124
|
+

|
|
125
|
+
|
|
126
|
+

|
|
127
|
+
|
|
128
|
+
|
|
129
|
+

|
|
@@ -1,175 +1,330 @@
|
|
|
1
1
|
(() => {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
2
|
+
const Q = fn => {
|
|
3
|
+
try {
|
|
4
|
+
return fn?.()
|
|
5
|
+
} catch {}
|
|
6
|
+
};
|
|
7
|
+
const $global = Q(() => globalThis) ?? Q(() => global) ?? Q(() => self) ?? Q(() => window) ?? this;
|
|
8
|
+
const constructPrototype = newClass => {
|
|
9
|
+
try {
|
|
10
|
+
if (newClass?.prototype) return newClass;
|
|
11
|
+
const constProto = newClass?.constructor?.prototype;
|
|
12
|
+
if (constProto) {
|
|
13
|
+
newClass.prototype = Q(() => constProto?.bind?.(constProto)) ?? Object.create(Object(constProto));
|
|
14
|
+
return newClass;
|
|
15
|
+
}
|
|
16
|
+
newClass.prototype = Q(() => newClass?.bind?.(newClass)) ?? Object.create(Object(newClass));
|
|
17
|
+
} catch (e) {
|
|
18
|
+
console.warn(e, newClass);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
const extend = (thisClass, superClass) => {
|
|
22
|
+
try {
|
|
23
|
+
constructPrototype(thisClass);
|
|
24
|
+
constructPrototype(superClass);
|
|
25
|
+
Object.setPrototypeOf(thisClass.prototype, superClass?.prototype ?? superClass?.constructor?.prototype ?? superClass);
|
|
26
|
+
Object.setPrototypeOf(thisClass, superClass);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
console.warn(e, {
|
|
29
|
+
thisClass,
|
|
30
|
+
superClass
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
return thisClass;
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
|
|
37
|
+
- Creates a function that returns a string for all string conversion methods
|
|
38
|
+
- Used to provide consistent string representations for polyfilled functions
|
|
39
|
+
- @param {string} str - The string to return
|
|
40
|
+
- @returns {Function} A function that returns the string for all conversion methods
|
|
41
|
+
- @private
|
|
42
|
+
*/
|
|
43
|
+
const makeStringer = str => {
|
|
44
|
+
const stringer = () => str;
|
|
45
|
+
['valueOf', 'toString', 'toLocaleString', Symbol.toPrimitive].forEach(x => {
|
|
46
|
+
stringer[x] = stringer;
|
|
47
|
+
});
|
|
48
|
+
stringer[Symbol.toStringTag] = str;
|
|
49
|
+
return stringer;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
|
|
53
|
+
- Sets string conversion methods on a function to indicate it’s polyfill code
|
|
54
|
+
- Provides consistent debugging experience by showing polyfill status
|
|
55
|
+
- @param {Function} obj - The function to modify
|
|
56
|
+
- @returns {Function} The modified function
|
|
57
|
+
- @private
|
|
58
|
+
*/
|
|
59
|
+
const setStrings = (obj) => {
|
|
60
|
+
let type = 'function';
|
|
61
|
+
if(String(obj).trim().startsWith('class')||/^[A-Z]|^.[A-Z]/.test(obj?.name)){
|
|
62
|
+
type = 'class';
|
|
63
|
+
}
|
|
64
|
+
if(String(obj).trim().startsWith('async')||/async/i.test(obj?.name)){
|
|
65
|
+
type = 'async function';
|
|
66
|
+
}
|
|
67
|
+
for (const str of ['toString', 'toLocaleString', Symbol.toStringTag]) {
|
|
68
|
+
Object.defineProperty(obj, str, {
|
|
69
|
+
value: makeStringer(`${type} ${obj.name} { [polyfill code] }`),
|
|
70
|
+
configurable: true,
|
|
71
|
+
writable: true,
|
|
72
|
+
enumerable: false,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return obj;
|
|
76
|
+
};
|
|
77
|
+
const assign = (target, source) => {
|
|
78
|
+
const props = Object.getOwnPropertyDescriptors(source);
|
|
79
|
+
for (const key in props) {
|
|
80
|
+
try {
|
|
81
|
+
Object.defineProperty(target, key, props[key]);
|
|
82
|
+
} catch (e) {
|
|
83
|
+
console.warn(e, key, props[key]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const key in source) {
|
|
87
|
+
try {
|
|
88
|
+
target[key] ??= source[key];
|
|
89
|
+
} catch (e) {
|
|
90
|
+
console.warn(e, key, props[key]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return target;
|
|
94
|
+
};
|
|
95
|
+
const cloneClass = $class => {
|
|
96
|
+
const clonePrototype = assign({}, $class.prototype);
|
|
97
|
+
const clone = $class.bind(clonePrototype);
|
|
98
|
+
assign(clone, $class);
|
|
99
|
+
clone.prototype = Object.setPrototypeOf(clonePrototype, Object.getPrototypeOf($class.prototype));
|
|
100
|
+
Object.setPrototypeOf(clone, Object.getPrototypeOf($class));
|
|
101
|
+
return clone;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const _closed = Object.getOwnPropertyDescriptor($global.ReadableStreamDefaultReader.prototype, 'closed')?.get;
|
|
105
|
+
if (_closed) {
|
|
106
|
+
Object.defineProperty($global.ReadableStreamDefaultReader.prototype, 'closed', {
|
|
107
|
+
get: extend(setStrings(async function closed() {
|
|
108
|
+
try {
|
|
109
|
+
return await _closed.call(this);
|
|
110
|
+
} catch (e) {
|
|
111
|
+
console.warn(e);
|
|
112
|
+
}
|
|
113
|
+
}), _closed),
|
|
114
|
+
enumerable: false,
|
|
115
|
+
configurable: true
|
|
116
|
+
});
|
|
78
117
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
118
|
+
|
|
119
|
+
if (!$global.ReadableStreamBYOBReader) {
|
|
120
|
+
$global.ReadableStreamBYOBReader ??= cloneClass(ReadableStreamDefaultReader);
|
|
121
|
+
Object.defineProperty(ReadableStreamBYOBReader, 'name', {
|
|
122
|
+
value: 'ReadableStreamBYOBReader',
|
|
123
|
+
enumerable: true,
|
|
124
|
+
configurable: true,
|
|
125
|
+
writable: true
|
|
126
|
+
});
|
|
127
|
+
setStrings(ReadableStreamBYOBReader);
|
|
128
|
+
const _getReader = ReadableStream.prototype.getReader;
|
|
129
|
+
ReadableStream.prototype.getReader = Object.setPrototypeOf(function getReader(options) {
|
|
130
|
+
const reader = _getReader.call(this);
|
|
131
|
+
if (options?.mode == 'byob') {
|
|
132
|
+
Object.setPrototypeOf(reader, ReadableStreamBYOBReader);
|
|
133
|
+
}
|
|
134
|
+
return reader;
|
|
135
|
+
}, _getReader);
|
|
136
|
+
extend(ReadableStreamBYOBReader, ReadableStreamDefaultReader);
|
|
137
|
+
const _read = ReadableStreamBYOBReader.prototype.read;
|
|
138
|
+
ReadableStreamBYOBReader.prototype.read = extend(setStrings(async function read(view) {
|
|
139
|
+
// If no view is provided, fall back to default behavior
|
|
140
|
+
if (!view) {
|
|
141
|
+
return _read.call(this, view);
|
|
142
|
+
}
|
|
143
|
+
// Read from the underlying stream (default reader behavior)
|
|
144
|
+
const result = await _read.call(this, view);
|
|
145
|
+
// If done, return with the view and done flag
|
|
146
|
+
if (result.done != false) {
|
|
147
|
+
return {
|
|
148
|
+
value: view,
|
|
149
|
+
done: true
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
// Convert the chunk to Uint8Array if needed
|
|
153
|
+
const chunk = result.value instanceof Uint8Array ? result.value : new Uint8Array(result.value);
|
|
154
|
+
// Determine how much data we can copy
|
|
155
|
+
const bytesToCopy = Math.min(chunk.byteLength, view.byteLength);
|
|
156
|
+
// Create a temporary view to copy into the provided view
|
|
157
|
+
const targetView = new Uint8Array(view.buffer, view.byteOffset, view.byteLength);
|
|
158
|
+
// Copy the data into the provided buffer
|
|
159
|
+
targetView.set(chunk.subarray(0, bytesToCopy), 0);
|
|
160
|
+
// Create a view of the filled portion
|
|
161
|
+
const filledView = new view.constructor(view.buffer, view.byteOffset, bytesToCopy);
|
|
162
|
+
return {
|
|
163
|
+
value: filledView,
|
|
164
|
+
done: false
|
|
165
|
+
};
|
|
166
|
+
}), _read);
|
|
85
167
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
ReadableStream.prototype.getReader = Object.setPrototypeOf(function getReader(options) {
|
|
107
|
-
const reader = _getReader.call(this);
|
|
108
|
-
if (options?.mode == 'byob') {
|
|
109
|
-
Object.setPrototypeOf(reader, ReadableStreamBYOBReader);
|
|
110
|
-
}
|
|
111
|
-
return reader;
|
|
112
|
-
}, _getReader);
|
|
113
|
-
extend(ReadableStreamBYOBReader, ReadableStreamDefaultReader);
|
|
114
|
-
const _read = ReadableStreamBYOBReader.prototype.read;
|
|
115
|
-
ReadableStreamBYOBReader.prototype.read = extend(setStrings(async function read(view) {
|
|
116
|
-
// If no view is provided, fall back to default behavior
|
|
117
|
-
if (!view) {
|
|
118
|
-
return _read.call(this,view);
|
|
119
|
-
}
|
|
120
|
-
// Read from the underlying stream (default reader behavior)
|
|
121
|
-
const result = await _read.call(this,view);
|
|
122
|
-
// If done, return with the view and done flag
|
|
123
|
-
if (result.done != false) {
|
|
124
|
-
return {
|
|
125
|
-
value: view,
|
|
126
|
-
done: true
|
|
168
|
+
const supportsReadableStreamBYOBReaderConstructor = () => {
|
|
169
|
+
try {
|
|
170
|
+
const stream = new ReadableStream({
|
|
171
|
+
start(controller) {
|
|
172
|
+
controller.enqueue(new Uint8Array([0]));
|
|
173
|
+
controller.close();
|
|
174
|
+
},
|
|
175
|
+
type: 'bytes'
|
|
176
|
+
});
|
|
177
|
+
const reader = new ReadableStreamBYOBReader(stream);
|
|
178
|
+
reader.read(new Uint8Array([0])).catch((e) => console.warn(e));
|
|
179
|
+
return true;
|
|
180
|
+
} catch {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
if (!supportsReadableStreamBYOBReaderConstructor()) {
|
|
185
|
+
const _ReadableStreamBYOBReader = $global.ReadableStreamBYOBReader;
|
|
186
|
+
const $ReadableStreamBYOBReader = function ReadableStreamBYOBReader(stream) {
|
|
187
|
+
return Object.setPrototypeOf(stream.getReader(), $global.ReadableStreamBYOBReader.prototype);
|
|
127
188
|
};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
189
|
+
setStrings($ReadableStreamBYOBReader);
|
|
190
|
+
extend($ReadableStreamBYOBReader, _ReadableStreamBYOBReader);
|
|
191
|
+
$global.ReadableStreamBYOBReader = new Proxy($ReadableStreamBYOBReader, Object.setPrototypeOf({
|
|
192
|
+
construct: Object.setPrototypeOf(function construct(_, [stream]) {
|
|
193
|
+
return $ReadableStreamBYOBReader(stream);
|
|
194
|
+
}, $ReadableStreamBYOBReader.prototype)
|
|
195
|
+
}, $ReadableStreamBYOBReader));
|
|
196
|
+
$global.ReadableStreamBYOBReader.prototype.constructor = $global.ReadableStreamBYOBReader;
|
|
197
|
+
}
|
|
198
|
+
if (!$global.ReadableStreamBYOBRequest) {
|
|
199
|
+
|
|
200
|
+
const protectedProp = (obj, key, value) =>
|
|
201
|
+
Object.defineProperty(obj, `&${key}`, {
|
|
202
|
+
value,
|
|
203
|
+
writable: true,
|
|
204
|
+
enumerable: false,
|
|
205
|
+
configurable: true,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
if (!$global.ReadableStreamBYOBRequest) {
|
|
209
|
+
|
|
210
|
+
class ReadableStreamBYOBRequest {
|
|
211
|
+
constructor(controller, view) {
|
|
212
|
+
protectedProp(this, 'controller', controller);
|
|
213
|
+
protectedProp(this, 'view', view);
|
|
214
|
+
protectedProp(this, 'responded', false);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
get view() {
|
|
218
|
+
return this['&responded'] ? null : this['&view'];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
respond(bytesWritten) {
|
|
222
|
+
if (this['&responded']) {
|
|
223
|
+
throw new TypeError('This BYOB request has already been responded to');
|
|
224
|
+
}
|
|
225
|
+
this['&responded'] = true;
|
|
226
|
+
|
|
227
|
+
const filledView = new this['&view'].constructor(
|
|
228
|
+
this['&view'].buffer,
|
|
229
|
+
this['&view'].byteOffset,
|
|
230
|
+
bytesWritten
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
this['&controller'].enqueue(filledView);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
respondWithNewView(view) {
|
|
237
|
+
if (this['&responded']) {
|
|
238
|
+
throw new TypeError('This BYOB request has already been responded to');
|
|
239
|
+
}
|
|
240
|
+
this['&responded'] = true;
|
|
241
|
+
this['&controller'].enqueue(view);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
setStrings(ReadableStreamBYOBRequest);
|
|
246
|
+
$global.ReadableStreamBYOBRequest = ReadableStreamBYOBRequest;
|
|
247
|
+
|
|
248
|
+
// Symbols to link streams and controllers
|
|
249
|
+
const $stream = Symbol('*stream');
|
|
250
|
+
const $controller = Symbol('*controller');
|
|
251
|
+
const controllerPendingViews = new WeakMap();
|
|
252
|
+
|
|
253
|
+
// Add byobRequest property to default controller
|
|
254
|
+
if (!('byobRequest' in ReadableStreamDefaultController.prototype)) {
|
|
255
|
+
Object.defineProperty(ReadableStreamDefaultController.prototype, 'byobRequest', {
|
|
256
|
+
get: extend(setStrings(function byobRequest() {
|
|
257
|
+
const view = controllerPendingViews.get(this);
|
|
258
|
+
if (view) {
|
|
259
|
+
return new ReadableStreamBYOBRequest(this, view);
|
|
260
|
+
}
|
|
261
|
+
return null;
|
|
262
|
+
}), ReadableStreamBYOBRequest),
|
|
263
|
+
configurable: true,
|
|
264
|
+
enumerable: true
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Wrap ReadableStream constructor
|
|
269
|
+
const _ReadableStream = ReadableStream;
|
|
270
|
+
|
|
271
|
+
$global.ReadableStream = extend(setStrings(function ReadableStream(underlyingSource = {}, strategy) {
|
|
272
|
+
const $this = this;
|
|
273
|
+
|
|
274
|
+
if (underlyingSource?.type === 'bytes') {
|
|
275
|
+
const wrappedSource = Object.assign({}, underlyingSource);
|
|
276
|
+
const originalStart = underlyingSource.start;
|
|
277
|
+
const originalPull = underlyingSource.pull;
|
|
278
|
+
|
|
279
|
+
wrappedSource.start = extend(setStrings(function start(controller) {
|
|
280
|
+
controller[$stream] = $this;
|
|
281
|
+
$this[$controller] = controller;
|
|
282
|
+
return originalStart?.call(this, controller);
|
|
283
|
+
}), originalStart ?? ReadableStreamDefaultController);
|
|
284
|
+
|
|
285
|
+
wrappedSource.pull = extend(setStrings(function pull(controller) {
|
|
286
|
+
controller[$stream] = $this;
|
|
287
|
+
$this[$controller] = controller;
|
|
288
|
+
return originalPull?.call(this, controller);
|
|
289
|
+
}), originalPull ?? ReadableStreamDefaultController);
|
|
290
|
+
|
|
291
|
+
const stream = new _ReadableStream(wrappedSource, strategy);
|
|
292
|
+
return Object.setPrototypeOf($this, stream);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return new _ReadableStream(underlyingSource, strategy);
|
|
296
|
+
}), _ReadableStream);
|
|
297
|
+
|
|
298
|
+
// Patch getReader to connect BYOB readers with controllers
|
|
299
|
+
const _getReader = $global.ReadableStream.prototype.getReader;
|
|
300
|
+
$global.ReadableStream.prototype.getReader = extend(setStrings(function getReader(options) {
|
|
301
|
+
const reader = _getReader.call(this, options);
|
|
302
|
+
|
|
303
|
+
if (options?.mode === 'byob') {
|
|
304
|
+
const _read = reader.read;
|
|
305
|
+
|
|
306
|
+
reader.read = extend(setStrings(async function read(view) {
|
|
307
|
+
const controller = this[$controller] ?? reader[$controller] ?? this[$stream]?.[$controller];
|
|
308
|
+
|
|
309
|
+
if (controller && view) {
|
|
310
|
+
controllerPendingViews.set(controller, view);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const result = await _read.call(this, view);
|
|
314
|
+
|
|
315
|
+
if (controller) {
|
|
316
|
+
controllerPendingViews.delete(controller);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return result;
|
|
320
|
+
}), _read);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return reader;
|
|
324
|
+
}), _getReader);
|
|
325
|
+
if(typeof ReadableByteStreamController === 'undefined'){
|
|
326
|
+
$global.ReadableByteStreamContoller = setStrings(class ReadableByteStreamContoller extends ReadableStreamDefaultController{});
|
|
327
|
+
}
|
|
328
|
+
}
|
|
159
329
|
}
|
|
160
|
-
};
|
|
161
|
-
if (!supportsReadableStreamBYOBReaderConstructor()) {
|
|
162
|
-
const _ReadableStreamBYOBReader = $global.ReadableStreamBYOBReader;
|
|
163
|
-
const $ReadableStreamBYOBReader = function ReadableStreamBYOBReader(stream) {
|
|
164
|
-
return Object.setPrototypeOf(stream.getReader(), $global.ReadableStreamBYOBReader.prototype);
|
|
165
|
-
};
|
|
166
|
-
setStrings($ReadableStreamBYOBReader);
|
|
167
|
-
extend($ReadableStreamBYOBReader, _ReadableStreamBYOBReader);
|
|
168
|
-
$global.ReadableStreamBYOBReader = new Proxy($ReadableStreamBYOBReader, Object.setPrototypeOf({
|
|
169
|
-
construct: Object.setPrototypeOf(function construct(_, [stream]) {
|
|
170
|
-
return $ReadableStreamBYOBReader(stream);
|
|
171
|
-
}, $ReadableStreamBYOBReader.prototype)
|
|
172
|
-
}, $ReadableStreamBYOBReader));
|
|
173
|
-
$global.ReadableStreamBYOBReader.prototype.constructor = $global.ReadableStreamBYOBReader;
|
|
174
|
-
}
|
|
175
330
|
})();
|