@statewalker/webrun-streams 0.1.0 → 0.2.0
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 +197 -13
- package/dist/collect.d.ts +7 -0
- package/dist/collect.d.ts.map +1 -0
- package/dist/duplex.d.ts +52 -0
- package/dist/duplex.d.ts.map +1 -0
- package/dist/emulate-mux.d.ts +39 -0
- package/dist/emulate-mux.d.ts.map +1 -0
- package/dist/errors.d.ts +8 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/flow-control.d.ts +71 -0
- package/dist/flow-control.d.ts.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/{index.mjs → index.js} +251 -56
- package/dist/jsonl.d.ts +5 -0
- package/dist/jsonl.d.ts.map +1 -0
- package/dist/lines.d.ts +5 -0
- package/dist/lines.d.ts.map +1 -0
- package/dist/map.d.ts +3 -0
- package/dist/map.d.ts.map +1 -0
- package/dist/new-async-generator.d.ts +70 -0
- package/dist/new-async-generator.d.ts.map +1 -0
- package/dist/normalize.d.ts +8 -0
- package/dist/normalize.d.ts.map +1 -0
- package/dist/readable-streams.d.ts +13 -0
- package/dist/readable-streams.d.ts.map +1 -0
- package/dist/recieve-iterator.d.ts +14 -0
- package/dist/recieve-iterator.d.ts.map +1 -0
- package/dist/send-iterator.d.ts +15 -0
- package/dist/send-iterator.d.ts.map +1 -0
- package/dist/text.d.ts +5 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/to-chunks.d.ts +11 -0
- package/dist/to-chunks.d.ts.map +1 -0
- package/dist/uint32.d.ts +25 -0
- package/dist/uint32.d.ts.map +1 -0
- package/package.json +13 -8
- package/src/duplex.ts +59 -0
- package/src/emulate-mux.ts +188 -74
- package/src/flow-control.ts +146 -0
- package/src/index.ts +2 -0
- package/src/readable-streams.ts +49 -13
- package/src/uint32.ts +34 -0
- package/LICENSE +0 -21
package/src/readable-streams.ts
CHANGED
|
@@ -1,21 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The iterator ↔ `ReadableStream` boundary.
|
|
3
|
+
*
|
|
4
|
+
* Both adapters must carry CANCELLATION, not just data: a response body leaves
|
|
5
|
+
* a handler as a `ReadableStream`, crosses a transport as an iterator, and
|
|
6
|
+
* becomes a `ReadableStream` again at the caller — so when the caller walks
|
|
7
|
+
* away, the only path back to the handler's producer runs through both of
|
|
8
|
+
* these functions. Teardown that stops at an adapter leaves a producer running
|
|
9
|
+
* for ever.
|
|
10
|
+
*/
|
|
11
|
+
|
|
1
12
|
export function toReadableStream(it: AsyncIterator<Uint8Array>): ReadableStream<Uint8Array> {
|
|
2
13
|
return new ReadableStream<Uint8Array>({
|
|
14
|
+
/**
|
|
15
|
+
* One chunk per pull. An earlier version drained the whole iterator inside
|
|
16
|
+
* a single `pull`, which defeated the stream's own backpressure (every
|
|
17
|
+
* chunk was enqueued as fast as the producer could make them, however slow
|
|
18
|
+
* the reader was) and left no point between chunks at which a cancellation
|
|
19
|
+
* could take effect.
|
|
20
|
+
*/
|
|
3
21
|
async pull(controller) {
|
|
4
|
-
let handled = false;
|
|
5
22
|
try {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
controller.enqueue(value);
|
|
23
|
+
const slot = await it.next();
|
|
24
|
+
if (!slot || slot.done) {
|
|
25
|
+
controller.close();
|
|
26
|
+
return;
|
|
11
27
|
}
|
|
28
|
+
controller.enqueue((await slot.value) as Uint8Array);
|
|
12
29
|
} catch (error) {
|
|
13
|
-
handled = true;
|
|
14
30
|
controller.error(error);
|
|
15
|
-
} finally {
|
|
16
|
-
if (!handled) controller.close();
|
|
17
31
|
}
|
|
18
32
|
},
|
|
33
|
+
/**
|
|
34
|
+
* Release the source. NOT awaited: `.return()` on an async generator that
|
|
35
|
+
* is parked awaiting its own source is queued behind that pending
|
|
36
|
+
* `next()`, so awaiting it here would hang `reader.cancel()` on exactly
|
|
37
|
+
* the producers that most need cancelling.
|
|
38
|
+
*/
|
|
39
|
+
cancel(reason) {
|
|
40
|
+
void Promise.resolve(it.return?.(reason)).catch(() => {});
|
|
41
|
+
},
|
|
19
42
|
});
|
|
20
43
|
}
|
|
21
44
|
|
|
@@ -23,9 +46,22 @@ export async function* fromReadableStream(
|
|
|
23
46
|
stream: ReadableStream<Uint8Array>,
|
|
24
47
|
): AsyncGenerator<Uint8Array, void, unknown> {
|
|
25
48
|
const reader = stream.getReader();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
49
|
+
let drained = false;
|
|
50
|
+
try {
|
|
51
|
+
while (true) {
|
|
52
|
+
const { done, value } = await reader.read();
|
|
53
|
+
if (done) {
|
|
54
|
+
drained = true;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
if (value !== undefined) yield value;
|
|
58
|
+
}
|
|
59
|
+
} finally {
|
|
60
|
+
// A consumer that stops early (`break`, `.return()`, an error) must cancel
|
|
61
|
+
// the source, or whatever fills it keeps filling it. A stream that ended
|
|
62
|
+
// on its own is merely released — cancelling it would be a lie to any
|
|
63
|
+
// `cancel()` hook watching for an abandoned reader.
|
|
64
|
+
if (drained) reader.releaseLock();
|
|
65
|
+
else await reader.cancel().catch(() => {});
|
|
30
66
|
}
|
|
31
67
|
}
|
package/src/uint32.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Credit payloads are a single big-endian uint32, matching the frame header's
|
|
3
|
+
* byte order.
|
|
4
|
+
*
|
|
5
|
+
* Deliberately **not** re-exported from `index.ts`: this is an internal codec
|
|
6
|
+
* for the `emulateMux` wire format, not a compatibility commitment. Tests
|
|
7
|
+
* import it by path.
|
|
8
|
+
*/
|
|
9
|
+
/** The largest credit a single frame can advertise or grant: 2^32 - 1. */
|
|
10
|
+
export const MAX_UINT32 = 0xffffffff;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Clamps rather than wraps. `n >>> 0` is the obvious spelling and it is wrong
|
|
14
|
+
* here: 2^32 becomes **0**, so a 4 GiB window advertises *zero credit* and the
|
|
15
|
+
* peer stalls forever with no error — the exact silent hang credit exists to
|
|
16
|
+
* remove. 2^32 + 5 becomes 5, which looks like a working window and is worse.
|
|
17
|
+
* Anything above the ceiling is advertised as the ceiling.
|
|
18
|
+
*/
|
|
19
|
+
export function encodeUint32(n: number): Uint8Array {
|
|
20
|
+
const bytes = new Uint8Array(4);
|
|
21
|
+
const clamped = Number.isFinite(n) ? Math.min(MAX_UINT32, Math.max(0, Math.floor(n))) : 0;
|
|
22
|
+
new DataView(bytes.buffer).setUint32(0, clamped, false);
|
|
23
|
+
return bytes;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Returns `undefined` rather than a garbage number when the payload is too
|
|
28
|
+
* short, so a truncated frame — or one from a peer predating credit — is
|
|
29
|
+
* detectable at the call site instead of silently granting nonsense.
|
|
30
|
+
*/
|
|
31
|
+
export function decodeUint32(bytes: Uint8Array): number | undefined {
|
|
32
|
+
if (bytes.byteLength < 4) return undefined;
|
|
33
|
+
return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(0, false);
|
|
34
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2022-2026 statewalker
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|