@statewalker/webrun-http-browser 0.12.2 → 0.12.3
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/dist/index.js +76 -46
- package/dist/link/http-link.d.ts.map +1 -1
- package/dist/relay-sw.js +72 -45
- package/dist/relay-worker.js +72 -45
- package/dist/sw/worker-service.d.ts.map +1 -1
- package/dist/sw-worker.js +72 -45
- package/dist/sw.js +76 -46
- package/dist/worker.js +76 -46
- package/package.json +3 -3
- package/src/link/http-link.ts +41 -26
- package/src/sw/worker-service.ts +12 -1
package/dist/sw-worker.js
CHANGED
|
@@ -1312,7 +1312,10 @@
|
|
|
1312
1312
|
}
|
|
1313
1313
|
const onAbort = () => void release();
|
|
1314
1314
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1315
|
-
|
|
1315
|
+
options.keepAlive;
|
|
1316
|
+
drain(output).finally(() => {
|
|
1317
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1318
|
+
});
|
|
1316
1319
|
};
|
|
1317
1320
|
let decoded;
|
|
1318
1321
|
try {
|
|
@@ -1675,7 +1678,8 @@
|
|
|
1675
1678
|
}
|
|
1676
1679
|
const { envelope, body: respBody, release, finish } = await httpFetch(call, env, body, {
|
|
1677
1680
|
...options,
|
|
1678
|
-
signal: request.signal ?? void 0
|
|
1681
|
+
signal: request.signal ?? void 0,
|
|
1682
|
+
keepAlive: request
|
|
1679
1683
|
});
|
|
1680
1684
|
const responseInit = {
|
|
1681
1685
|
status: envelope.status,
|
|
@@ -1687,7 +1691,7 @@
|
|
|
1687
1691
|
else await release?.();
|
|
1688
1692
|
return new Response(null, responseInit);
|
|
1689
1693
|
}
|
|
1690
|
-
return new Response(responseBody(respBody, request
|
|
1694
|
+
return new Response(responseBody(respBody, request, release), responseInit);
|
|
1691
1695
|
}
|
|
1692
1696
|
/**
|
|
1693
1697
|
* Wrap a `(Request) => Promise<Response>` handler as a `Duplex` so it can be
|
|
@@ -1840,7 +1844,9 @@
|
|
|
1840
1844
|
* A cancel abandons the call too — unless the read in flight comes back with the
|
|
1841
1845
|
* body's end, which finishes it instead (see `cancel`).
|
|
1842
1846
|
*/
|
|
1843
|
-
function responseBody(body,
|
|
1847
|
+
function responseBody(body, request, release) {
|
|
1848
|
+
const keep = { request };
|
|
1849
|
+
const signal = request.signal ?? void 0;
|
|
1844
1850
|
const it = body[Symbol.asyncIterator]();
|
|
1845
1851
|
let controller;
|
|
1846
1852
|
let ended = false;
|
|
@@ -1848,6 +1854,7 @@
|
|
|
1848
1854
|
const end = () => {
|
|
1849
1855
|
ended = true;
|
|
1850
1856
|
signal?.removeEventListener("abort", onAbort);
|
|
1857
|
+
keep.request = void 0;
|
|
1851
1858
|
};
|
|
1852
1859
|
const onAbort = () => {
|
|
1853
1860
|
if (ended) return;
|
|
@@ -2879,12 +2886,6 @@
|
|
|
2879
2886
|
const producer = output;
|
|
2880
2887
|
const returnProducer = () => void Promise.resolve(producer.return?.()).catch(() => {});
|
|
2881
2888
|
if (framed !== output) signal.addEventListener("abort", returnProducer, { once: true });
|
|
2882
|
-
let onAborted = () => {};
|
|
2883
|
-
const aborted = new Promise((resolve) => {
|
|
2884
|
-
onAborted = () => resolve();
|
|
2885
|
-
});
|
|
2886
|
-
if (signal.aborted) onAborted();
|
|
2887
|
-
else signal.addEventListener("abort", onAborted, { once: true });
|
|
2888
2889
|
try {
|
|
2889
2890
|
await sendIterator(async ({ done, value, error }) => {
|
|
2890
2891
|
if (signal.aborted) return;
|
|
@@ -2906,7 +2907,7 @@
|
|
|
2906
2907
|
});
|
|
2907
2908
|
touch();
|
|
2908
2909
|
if (done && !error) onSent();
|
|
2909
|
-
if (portBufferedAmount(port) > 0) await
|
|
2910
|
+
if (portBufferedAmount(port) > 0) await whenReadyOrAborted(port, signal);
|
|
2910
2911
|
return;
|
|
2911
2912
|
}
|
|
2912
2913
|
await callPort(port, chunk, {
|
|
@@ -2922,10 +2923,25 @@
|
|
|
2922
2923
|
throw err;
|
|
2923
2924
|
} finally {
|
|
2924
2925
|
signal.removeEventListener("abort", returnProducer);
|
|
2925
|
-
signal.removeEventListener("abort", onAborted);
|
|
2926
2926
|
}
|
|
2927
2927
|
}
|
|
2928
2928
|
/**
|
|
2929
|
+
* A pipelined sender waits only for room in the window, or for the abort. The
|
|
2930
|
+
* abort listener lives for this one wait: a promise held for the whole stream
|
|
2931
|
+
* would keep one reaction per wait until the stream ends.
|
|
2932
|
+
*/
|
|
2933
|
+
function whenReadyOrAborted(port, signal) {
|
|
2934
|
+
if (signal.aborted) return Promise.resolve();
|
|
2935
|
+
return new Promise((resolve) => {
|
|
2936
|
+
const settle = () => {
|
|
2937
|
+
signal.removeEventListener("abort", settle);
|
|
2938
|
+
resolve();
|
|
2939
|
+
};
|
|
2940
|
+
signal.addEventListener("abort", settle, { once: true });
|
|
2941
|
+
whenPortReady(port).then(settle, settle);
|
|
2942
|
+
});
|
|
2943
|
+
}
|
|
2944
|
+
/**
|
|
2929
2945
|
* One virtual port.
|
|
2930
2946
|
*
|
|
2931
2947
|
* `deliver` and `markClosed` are deliberately not on `port`: the consumer holds
|
|
@@ -3923,7 +3939,12 @@
|
|
|
3923
3939
|
finish();
|
|
3924
3940
|
return response;
|
|
3925
3941
|
}
|
|
3926
|
-
|
|
3942
|
+
failers.delete(fail);
|
|
3943
|
+
const body = guardBody(response.body, finish, request);
|
|
3944
|
+
fail = body.fail;
|
|
3945
|
+
if (closed) fail(lostReason);
|
|
3946
|
+
else if (!finished) failers.add(fail);
|
|
3947
|
+
return new Response(body.stream, {
|
|
3927
3948
|
status: response.status,
|
|
3928
3949
|
statusText: response.statusText,
|
|
3929
3950
|
headers: response.headers
|
|
@@ -3942,56 +3963,62 @@
|
|
|
3942
3963
|
return asyncDisposable(link, () => link.close());
|
|
3943
3964
|
}
|
|
3944
3965
|
/**
|
|
3945
|
-
* A response body that reports its end (done, cancelled, failed or aborted)
|
|
3946
|
-
*
|
|
3947
|
-
*
|
|
3948
|
-
*
|
|
3966
|
+
* A response body that reports its end (done, cancelled, failed or aborted).
|
|
3967
|
+
* `fail(error)` ends it with `error` at once — the link calls it when it is lost
|
|
3968
|
+
* mid-stream. An abort of the request's signal does the same with the signal's
|
|
3969
|
+
* reason, read or not: the exchange is over (the layer below has given its port
|
|
3970
|
+
* back), so the request leaves `inflight` then. A read waits on the reader
|
|
3971
|
+
* alone, so nothing outlives the chunk it brings.
|
|
3949
3972
|
*/
|
|
3950
|
-
function guardBody(body,
|
|
3973
|
+
function guardBody(body, finish, request) {
|
|
3974
|
+
const keep = { request };
|
|
3975
|
+
const signal = request.signal ?? void 0;
|
|
3951
3976
|
const reader = body.getReader();
|
|
3952
3977
|
let controller;
|
|
3953
3978
|
let ended = false;
|
|
3954
3979
|
const end = () => {
|
|
3955
3980
|
ended = true;
|
|
3956
3981
|
signal?.removeEventListener("abort", onAbort);
|
|
3982
|
+
keep.request = void 0;
|
|
3957
3983
|
finish();
|
|
3958
3984
|
};
|
|
3959
|
-
const
|
|
3985
|
+
const fail = (error) => {
|
|
3960
3986
|
if (ended) return;
|
|
3961
3987
|
end();
|
|
3962
3988
|
reader.cancel().catch(() => {});
|
|
3963
3989
|
try {
|
|
3964
|
-
controller.error(
|
|
3990
|
+
controller.error(error);
|
|
3965
3991
|
} catch {}
|
|
3966
3992
|
};
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3993
|
+
const onAbort = () => fail(abortError(signal));
|
|
3994
|
+
return {
|
|
3995
|
+
stream: new ReadableStream({
|
|
3996
|
+
start(c) {
|
|
3997
|
+
controller = c;
|
|
3998
|
+
if (signal?.aborted) onAbort();
|
|
3999
|
+
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
4000
|
+
},
|
|
4001
|
+
async pull(controller) {
|
|
4002
|
+
try {
|
|
4003
|
+
const { done, value } = await reader.read();
|
|
4004
|
+
if (ended) return;
|
|
4005
|
+
if (done) {
|
|
4006
|
+
end();
|
|
4007
|
+
controller.close();
|
|
4008
|
+
return;
|
|
4009
|
+
}
|
|
4010
|
+
controller.enqueue(value);
|
|
4011
|
+
} catch (error) {
|
|
4012
|
+
fail(error);
|
|
3981
4013
|
}
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
if (ended) return;
|
|
4014
|
+
},
|
|
4015
|
+
cancel(why) {
|
|
3985
4016
|
end();
|
|
3986
|
-
reader.cancel().catch(() => {});
|
|
3987
|
-
controller.error(reason() ?? error);
|
|
4017
|
+
setTimeout(() => void reader.cancel(why).catch(() => {}), 0);
|
|
3988
4018
|
}
|
|
3989
|
-
},
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
await reader.cancel(why).catch(() => {});
|
|
3993
|
-
}
|
|
3994
|
-
});
|
|
4019
|
+
}),
|
|
4020
|
+
fail
|
|
4021
|
+
};
|
|
3995
4022
|
}
|
|
3996
4023
|
/** The signal's reason as an `Error` (`AbortError` when it gave none). */
|
|
3997
4024
|
function abortError(signal) {
|
package/dist/sw.js
CHANGED
|
@@ -1183,7 +1183,10 @@ async function httpFetch(call, env, body, options = {}) {
|
|
|
1183
1183
|
}
|
|
1184
1184
|
const onAbort = () => void release();
|
|
1185
1185
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1186
|
-
|
|
1186
|
+
options.keepAlive;
|
|
1187
|
+
drain(output).finally(() => {
|
|
1188
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1189
|
+
});
|
|
1187
1190
|
};
|
|
1188
1191
|
let decoded;
|
|
1189
1192
|
try {
|
|
@@ -1546,7 +1549,8 @@ async function fetchOverDuplex(call, request, options = {}) {
|
|
|
1546
1549
|
}
|
|
1547
1550
|
const { envelope, body: respBody, release, finish } = await httpFetch(call, env, body, {
|
|
1548
1551
|
...options,
|
|
1549
|
-
signal: request.signal ?? void 0
|
|
1552
|
+
signal: request.signal ?? void 0,
|
|
1553
|
+
keepAlive: request
|
|
1550
1554
|
});
|
|
1551
1555
|
const responseInit = {
|
|
1552
1556
|
status: envelope.status,
|
|
@@ -1558,7 +1562,7 @@ async function fetchOverDuplex(call, request, options = {}) {
|
|
|
1558
1562
|
else await release?.();
|
|
1559
1563
|
return new Response(null, responseInit);
|
|
1560
1564
|
}
|
|
1561
|
-
return new Response(responseBody(respBody, request
|
|
1565
|
+
return new Response(responseBody(respBody, request, release), responseInit);
|
|
1562
1566
|
}
|
|
1563
1567
|
/**
|
|
1564
1568
|
* Wrap a `(Request) => Promise<Response>` handler as a `Duplex` so it can be
|
|
@@ -1711,7 +1715,9 @@ const CANCEL_GRACE_MS = 1e3;
|
|
|
1711
1715
|
* A cancel abandons the call too — unless the read in flight comes back with the
|
|
1712
1716
|
* body's end, which finishes it instead (see `cancel`).
|
|
1713
1717
|
*/
|
|
1714
|
-
function responseBody(body,
|
|
1718
|
+
function responseBody(body, request, release) {
|
|
1719
|
+
const keep = { request };
|
|
1720
|
+
const signal = request.signal ?? void 0;
|
|
1715
1721
|
const it = body[Symbol.asyncIterator]();
|
|
1716
1722
|
let controller;
|
|
1717
1723
|
let ended = false;
|
|
@@ -1719,6 +1725,7 @@ function responseBody(body, signal, release) {
|
|
|
1719
1725
|
const end = () => {
|
|
1720
1726
|
ended = true;
|
|
1721
1727
|
signal?.removeEventListener("abort", onAbort);
|
|
1728
|
+
keep.request = void 0;
|
|
1722
1729
|
};
|
|
1723
1730
|
const onAbort = () => {
|
|
1724
1731
|
if (ended) return;
|
|
@@ -2547,12 +2554,6 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
|
|
|
2547
2554
|
const producer = output;
|
|
2548
2555
|
const returnProducer = () => void Promise.resolve(producer.return?.()).catch(() => {});
|
|
2549
2556
|
if (framed !== output) signal.addEventListener("abort", returnProducer, { once: true });
|
|
2550
|
-
let onAborted = () => {};
|
|
2551
|
-
const aborted = new Promise((resolve) => {
|
|
2552
|
-
onAborted = () => resolve();
|
|
2553
|
-
});
|
|
2554
|
-
if (signal.aborted) onAborted();
|
|
2555
|
-
else signal.addEventListener("abort", onAborted, { once: true });
|
|
2556
2557
|
try {
|
|
2557
2558
|
await sendIterator(async ({ done, value, error }) => {
|
|
2558
2559
|
if (signal.aborted) return;
|
|
@@ -2574,7 +2575,7 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
|
|
|
2574
2575
|
});
|
|
2575
2576
|
touch();
|
|
2576
2577
|
if (done && !error) onSent();
|
|
2577
|
-
if (portBufferedAmount(port) > 0) await
|
|
2578
|
+
if (portBufferedAmount(port) > 0) await whenReadyOrAborted(port, signal);
|
|
2578
2579
|
return;
|
|
2579
2580
|
}
|
|
2580
2581
|
await callPort(port, chunk, {
|
|
@@ -2590,10 +2591,25 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
|
|
|
2590
2591
|
throw err;
|
|
2591
2592
|
} finally {
|
|
2592
2593
|
signal.removeEventListener("abort", returnProducer);
|
|
2593
|
-
signal.removeEventListener("abort", onAborted);
|
|
2594
2594
|
}
|
|
2595
2595
|
}
|
|
2596
2596
|
/**
|
|
2597
|
+
* A pipelined sender waits only for room in the window, or for the abort. The
|
|
2598
|
+
* abort listener lives for this one wait: a promise held for the whole stream
|
|
2599
|
+
* would keep one reaction per wait until the stream ends.
|
|
2600
|
+
*/
|
|
2601
|
+
function whenReadyOrAborted(port, signal) {
|
|
2602
|
+
if (signal.aborted) return Promise.resolve();
|
|
2603
|
+
return new Promise((resolve) => {
|
|
2604
|
+
const settle = () => {
|
|
2605
|
+
signal.removeEventListener("abort", settle);
|
|
2606
|
+
resolve();
|
|
2607
|
+
};
|
|
2608
|
+
signal.addEventListener("abort", settle, { once: true });
|
|
2609
|
+
whenPortReady(port).then(settle, settle);
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
/**
|
|
2597
2613
|
* One virtual port.
|
|
2598
2614
|
*
|
|
2599
2615
|
* `deliver` and `markClosed` are deliberately not on `port`: the consumer holds
|
|
@@ -3591,7 +3607,12 @@ function trackedLink(send, release) {
|
|
|
3591
3607
|
finish();
|
|
3592
3608
|
return response;
|
|
3593
3609
|
}
|
|
3594
|
-
|
|
3610
|
+
failers.delete(fail);
|
|
3611
|
+
const body = guardBody(response.body, finish, request);
|
|
3612
|
+
fail = body.fail;
|
|
3613
|
+
if (closed) fail(lostReason);
|
|
3614
|
+
else if (!finished) failers.add(fail);
|
|
3615
|
+
return new Response(body.stream, {
|
|
3595
3616
|
status: response.status,
|
|
3596
3617
|
statusText: response.statusText,
|
|
3597
3618
|
headers: response.headers
|
|
@@ -3610,56 +3631,62 @@ function trackedLink(send, release) {
|
|
|
3610
3631
|
return asyncDisposable(link, () => link.close());
|
|
3611
3632
|
}
|
|
3612
3633
|
/**
|
|
3613
|
-
* A response body that reports its end (done, cancelled, failed or aborted)
|
|
3614
|
-
*
|
|
3615
|
-
*
|
|
3616
|
-
*
|
|
3634
|
+
* A response body that reports its end (done, cancelled, failed or aborted).
|
|
3635
|
+
* `fail(error)` ends it with `error` at once — the link calls it when it is lost
|
|
3636
|
+
* mid-stream. An abort of the request's signal does the same with the signal's
|
|
3637
|
+
* reason, read or not: the exchange is over (the layer below has given its port
|
|
3638
|
+
* back), so the request leaves `inflight` then. A read waits on the reader
|
|
3639
|
+
* alone, so nothing outlives the chunk it brings.
|
|
3617
3640
|
*/
|
|
3618
|
-
function guardBody(body,
|
|
3641
|
+
function guardBody(body, finish, request) {
|
|
3642
|
+
const keep = { request };
|
|
3643
|
+
const signal = request.signal ?? void 0;
|
|
3619
3644
|
const reader = body.getReader();
|
|
3620
3645
|
let controller;
|
|
3621
3646
|
let ended = false;
|
|
3622
3647
|
const end = () => {
|
|
3623
3648
|
ended = true;
|
|
3624
3649
|
signal?.removeEventListener("abort", onAbort);
|
|
3650
|
+
keep.request = void 0;
|
|
3625
3651
|
finish();
|
|
3626
3652
|
};
|
|
3627
|
-
const
|
|
3653
|
+
const fail = (error) => {
|
|
3628
3654
|
if (ended) return;
|
|
3629
3655
|
end();
|
|
3630
3656
|
reader.cancel().catch(() => {});
|
|
3631
3657
|
try {
|
|
3632
|
-
controller.error(
|
|
3658
|
+
controller.error(error);
|
|
3633
3659
|
} catch {}
|
|
3634
3660
|
};
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3661
|
+
const onAbort = () => fail(abortError(signal));
|
|
3662
|
+
return {
|
|
3663
|
+
stream: new ReadableStream({
|
|
3664
|
+
start(c) {
|
|
3665
|
+
controller = c;
|
|
3666
|
+
if (signal?.aborted) onAbort();
|
|
3667
|
+
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
3668
|
+
},
|
|
3669
|
+
async pull(controller) {
|
|
3670
|
+
try {
|
|
3671
|
+
const { done, value } = await reader.read();
|
|
3672
|
+
if (ended) return;
|
|
3673
|
+
if (done) {
|
|
3674
|
+
end();
|
|
3675
|
+
controller.close();
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3678
|
+
controller.enqueue(value);
|
|
3679
|
+
} catch (error) {
|
|
3680
|
+
fail(error);
|
|
3649
3681
|
}
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
if (ended) return;
|
|
3682
|
+
},
|
|
3683
|
+
cancel(why) {
|
|
3653
3684
|
end();
|
|
3654
|
-
reader.cancel().catch(() => {});
|
|
3655
|
-
controller.error(reason() ?? error);
|
|
3685
|
+
setTimeout(() => void reader.cancel(why).catch(() => {}), 0);
|
|
3656
3686
|
}
|
|
3657
|
-
},
|
|
3658
|
-
|
|
3659
|
-
|
|
3660
|
-
await reader.cancel(why).catch(() => {});
|
|
3661
|
-
}
|
|
3662
|
-
});
|
|
3687
|
+
}),
|
|
3688
|
+
fail
|
|
3689
|
+
};
|
|
3663
3690
|
}
|
|
3664
3691
|
/** The signal's reason as an `Error` (`AbortError` when it gave none). */
|
|
3665
3692
|
function abortError(signal) {
|
|
@@ -6928,7 +6955,10 @@ function serveHttpInWorker(handler, options = {}) {
|
|
|
6928
6955
|
let WebSocketClass;
|
|
6929
6956
|
return {
|
|
6930
6957
|
fetch(input, init) {
|
|
6931
|
-
const request = input instanceof Request && init === void 0 ? input : new Request(input instanceof Request ? input : new URL(`${input}`, baseUrl()),
|
|
6958
|
+
const request = input instanceof Request && init === void 0 ? input : new Request(input instanceof Request ? input : new URL(`${input}`, baseUrl()), {
|
|
6959
|
+
...init,
|
|
6960
|
+
signal: init && "signal" in init ? init.signal : input instanceof Request ? input.signal : void 0
|
|
6961
|
+
});
|
|
6932
6962
|
return endpoint.fetch(request);
|
|
6933
6963
|
},
|
|
6934
6964
|
connectWebSocket,
|
package/dist/worker.js
CHANGED
|
@@ -1124,7 +1124,10 @@ async function httpFetch(call, env, body, options = {}) {
|
|
|
1124
1124
|
}
|
|
1125
1125
|
const onAbort = () => void release();
|
|
1126
1126
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
1127
|
-
|
|
1127
|
+
options.keepAlive;
|
|
1128
|
+
drain(output).finally(() => {
|
|
1129
|
+
signal?.removeEventListener("abort", onAbort);
|
|
1130
|
+
});
|
|
1128
1131
|
};
|
|
1129
1132
|
let decoded;
|
|
1130
1133
|
try {
|
|
@@ -1432,7 +1435,8 @@ async function fetchOverDuplex(call, request, options = {}) {
|
|
|
1432
1435
|
}
|
|
1433
1436
|
const { envelope, body: respBody, release, finish } = await httpFetch(call, env, body, {
|
|
1434
1437
|
...options,
|
|
1435
|
-
signal: request.signal ?? void 0
|
|
1438
|
+
signal: request.signal ?? void 0,
|
|
1439
|
+
keepAlive: request
|
|
1436
1440
|
});
|
|
1437
1441
|
const responseInit = {
|
|
1438
1442
|
status: envelope.status,
|
|
@@ -1444,7 +1448,7 @@ async function fetchOverDuplex(call, request, options = {}) {
|
|
|
1444
1448
|
else await release?.();
|
|
1445
1449
|
return new Response(null, responseInit);
|
|
1446
1450
|
}
|
|
1447
|
-
return new Response(responseBody(respBody, request
|
|
1451
|
+
return new Response(responseBody(respBody, request, release), responseInit);
|
|
1448
1452
|
}
|
|
1449
1453
|
/**
|
|
1450
1454
|
* Wrap a `(Request) => Promise<Response>` handler as a `Duplex` so it can be
|
|
@@ -1597,7 +1601,9 @@ const CANCEL_GRACE_MS = 1e3;
|
|
|
1597
1601
|
* A cancel abandons the call too — unless the read in flight comes back with the
|
|
1598
1602
|
* body's end, which finishes it instead (see `cancel`).
|
|
1599
1603
|
*/
|
|
1600
|
-
function responseBody(body,
|
|
1604
|
+
function responseBody(body, request, release) {
|
|
1605
|
+
const keep = { request };
|
|
1606
|
+
const signal = request.signal ?? void 0;
|
|
1601
1607
|
const it = body[Symbol.asyncIterator]();
|
|
1602
1608
|
let controller;
|
|
1603
1609
|
let ended = false;
|
|
@@ -1605,6 +1611,7 @@ function responseBody(body, signal, release) {
|
|
|
1605
1611
|
const end = () => {
|
|
1606
1612
|
ended = true;
|
|
1607
1613
|
signal?.removeEventListener("abort", onAbort);
|
|
1614
|
+
keep.request = void 0;
|
|
1608
1615
|
};
|
|
1609
1616
|
const onAbort = () => {
|
|
1610
1617
|
if (ended) return;
|
|
@@ -2433,12 +2440,6 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
|
|
|
2433
2440
|
const producer = output;
|
|
2434
2441
|
const returnProducer = () => void Promise.resolve(producer.return?.()).catch(() => {});
|
|
2435
2442
|
if (framed !== output) signal.addEventListener("abort", returnProducer, { once: true });
|
|
2436
|
-
let onAborted = () => {};
|
|
2437
|
-
const aborted = new Promise((resolve) => {
|
|
2438
|
-
onAborted = () => resolve();
|
|
2439
|
-
});
|
|
2440
|
-
if (signal.aborted) onAborted();
|
|
2441
|
-
else signal.addEventListener("abort", onAborted, { once: true });
|
|
2442
2443
|
try {
|
|
2443
2444
|
await sendIterator(async ({ done, value, error }) => {
|
|
2444
2445
|
if (signal.aborted) return;
|
|
@@ -2460,7 +2461,7 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
|
|
|
2460
2461
|
});
|
|
2461
2462
|
touch();
|
|
2462
2463
|
if (done && !error) onSent();
|
|
2463
|
-
if (portBufferedAmount(port) > 0) await
|
|
2464
|
+
if (portBufferedAmount(port) > 0) await whenReadyOrAborted(port, signal);
|
|
2464
2465
|
return;
|
|
2465
2466
|
}
|
|
2466
2467
|
await callPort(port, chunk, {
|
|
@@ -2476,10 +2477,25 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
|
|
|
2476
2477
|
throw err;
|
|
2477
2478
|
} finally {
|
|
2478
2479
|
signal.removeEventListener("abort", returnProducer);
|
|
2479
|
-
signal.removeEventListener("abort", onAborted);
|
|
2480
2480
|
}
|
|
2481
2481
|
}
|
|
2482
2482
|
/**
|
|
2483
|
+
* A pipelined sender waits only for room in the window, or for the abort. The
|
|
2484
|
+
* abort listener lives for this one wait: a promise held for the whole stream
|
|
2485
|
+
* would keep one reaction per wait until the stream ends.
|
|
2486
|
+
*/
|
|
2487
|
+
function whenReadyOrAborted(port, signal) {
|
|
2488
|
+
if (signal.aborted) return Promise.resolve();
|
|
2489
|
+
return new Promise((resolve) => {
|
|
2490
|
+
const settle = () => {
|
|
2491
|
+
signal.removeEventListener("abort", settle);
|
|
2492
|
+
resolve();
|
|
2493
|
+
};
|
|
2494
|
+
signal.addEventListener("abort", settle, { once: true });
|
|
2495
|
+
whenPortReady(port).then(settle, settle);
|
|
2496
|
+
});
|
|
2497
|
+
}
|
|
2498
|
+
/**
|
|
2483
2499
|
* One virtual port.
|
|
2484
2500
|
*
|
|
2485
2501
|
* `deliver` and `markClosed` are deliberately not on `port`: the consumer holds
|
|
@@ -3477,7 +3493,12 @@ function trackedLink(send, release) {
|
|
|
3477
3493
|
finish();
|
|
3478
3494
|
return response;
|
|
3479
3495
|
}
|
|
3480
|
-
|
|
3496
|
+
failers.delete(fail);
|
|
3497
|
+
const body = guardBody(response.body, finish, request);
|
|
3498
|
+
fail = body.fail;
|
|
3499
|
+
if (closed) fail(lostReason);
|
|
3500
|
+
else if (!finished) failers.add(fail);
|
|
3501
|
+
return new Response(body.stream, {
|
|
3481
3502
|
status: response.status,
|
|
3482
3503
|
statusText: response.statusText,
|
|
3483
3504
|
headers: response.headers
|
|
@@ -3496,56 +3517,62 @@ function trackedLink(send, release) {
|
|
|
3496
3517
|
return asyncDisposable(link, () => link.close());
|
|
3497
3518
|
}
|
|
3498
3519
|
/**
|
|
3499
|
-
* A response body that reports its end (done, cancelled, failed or aborted)
|
|
3500
|
-
*
|
|
3501
|
-
*
|
|
3502
|
-
*
|
|
3520
|
+
* A response body that reports its end (done, cancelled, failed or aborted).
|
|
3521
|
+
* `fail(error)` ends it with `error` at once — the link calls it when it is lost
|
|
3522
|
+
* mid-stream. An abort of the request's signal does the same with the signal's
|
|
3523
|
+
* reason, read or not: the exchange is over (the layer below has given its port
|
|
3524
|
+
* back), so the request leaves `inflight` then. A read waits on the reader
|
|
3525
|
+
* alone, so nothing outlives the chunk it brings.
|
|
3503
3526
|
*/
|
|
3504
|
-
function guardBody(body,
|
|
3527
|
+
function guardBody(body, finish, request) {
|
|
3528
|
+
const keep = { request };
|
|
3529
|
+
const signal = request.signal ?? void 0;
|
|
3505
3530
|
const reader = body.getReader();
|
|
3506
3531
|
let controller;
|
|
3507
3532
|
let ended = false;
|
|
3508
3533
|
const end = () => {
|
|
3509
3534
|
ended = true;
|
|
3510
3535
|
signal?.removeEventListener("abort", onAbort);
|
|
3536
|
+
keep.request = void 0;
|
|
3511
3537
|
finish();
|
|
3512
3538
|
};
|
|
3513
|
-
const
|
|
3539
|
+
const fail = (error) => {
|
|
3514
3540
|
if (ended) return;
|
|
3515
3541
|
end();
|
|
3516
3542
|
reader.cancel().catch(() => {});
|
|
3517
3543
|
try {
|
|
3518
|
-
controller.error(
|
|
3544
|
+
controller.error(error);
|
|
3519
3545
|
} catch {}
|
|
3520
3546
|
};
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3547
|
+
const onAbort = () => fail(abortError(signal));
|
|
3548
|
+
return {
|
|
3549
|
+
stream: new ReadableStream({
|
|
3550
|
+
start(c) {
|
|
3551
|
+
controller = c;
|
|
3552
|
+
if (signal?.aborted) onAbort();
|
|
3553
|
+
else signal?.addEventListener("abort", onAbort, { once: true });
|
|
3554
|
+
},
|
|
3555
|
+
async pull(controller) {
|
|
3556
|
+
try {
|
|
3557
|
+
const { done, value } = await reader.read();
|
|
3558
|
+
if (ended) return;
|
|
3559
|
+
if (done) {
|
|
3560
|
+
end();
|
|
3561
|
+
controller.close();
|
|
3562
|
+
return;
|
|
3563
|
+
}
|
|
3564
|
+
controller.enqueue(value);
|
|
3565
|
+
} catch (error) {
|
|
3566
|
+
fail(error);
|
|
3535
3567
|
}
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
if (ended) return;
|
|
3568
|
+
},
|
|
3569
|
+
cancel(why) {
|
|
3539
3570
|
end();
|
|
3540
|
-
reader.cancel().catch(() => {});
|
|
3541
|
-
controller.error(reason() ?? error);
|
|
3571
|
+
setTimeout(() => void reader.cancel(why).catch(() => {}), 0);
|
|
3542
3572
|
}
|
|
3543
|
-
},
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
await reader.cancel(why).catch(() => {});
|
|
3547
|
-
}
|
|
3548
|
-
});
|
|
3573
|
+
}),
|
|
3574
|
+
fail
|
|
3575
|
+
};
|
|
3549
3576
|
}
|
|
3550
3577
|
/** The signal's reason as an `Error` (`AbortError` when it gave none). */
|
|
3551
3578
|
function abortError(signal) {
|
|
@@ -4879,7 +4906,10 @@ function serveHttpInWorker(handler, options = {}) {
|
|
|
4879
4906
|
let WebSocketClass;
|
|
4880
4907
|
return {
|
|
4881
4908
|
fetch(input, init) {
|
|
4882
|
-
const request = input instanceof Request && init === void 0 ? input : new Request(input instanceof Request ? input : new URL(`${input}`, baseUrl()),
|
|
4909
|
+
const request = input instanceof Request && init === void 0 ? input : new Request(input instanceof Request ? input : new URL(`${input}`, baseUrl()), {
|
|
4910
|
+
...init,
|
|
4911
|
+
signal: init && "signal" in init ? init.signal : input instanceof Request ? input.signal : void 0
|
|
4912
|
+
});
|
|
4883
4913
|
return endpoint.fetch(request);
|
|
4884
4914
|
},
|
|
4885
4915
|
connectWebSocket,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@statewalker/webrun-http-browser",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "ServiceWorker-based HTTP and WebSocket server for browsers: handlers in pages and Dedicated Workers, same-origin and relay modes, streams with backpressure",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
],
|
|
59
59
|
"dependencies": {
|
|
60
60
|
"idb-keyval": "^6.3.0",
|
|
61
|
-
"@statewalker/webrun-http-streams": "^0.4.
|
|
62
|
-
"@statewalker/webrun-rpc": "^0.10.
|
|
61
|
+
"@statewalker/webrun-http-streams": "^0.4.1",
|
|
62
|
+
"@statewalker/webrun-rpc": "^0.10.1",
|
|
63
63
|
"@statewalker/webrun-streams": "^0.2.3"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|