@statewalker/webrun-http-browser 0.7.1 → 0.10.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/dist/index.js CHANGED
@@ -2126,6 +2126,12 @@ function asyncDisposable$2(target, dispose) {
2126
2126
  }
2127
2127
  /** Carries the serialized JS error between two webrun peers. */
2128
2128
  const PEER_ERROR_HEADER = "x-webrun-error";
2129
+ /**
2130
+ * The reason of `HttpExchange.signal` when the exchange is abandoned with no
2131
+ * error of its own (`return()`); one abandoned by `throw(error)` aborts with
2132
+ * that error.
2133
+ */
2134
+ const EXCHANGE_ABANDONED_MESSAGE = "webrun-http-streams: the exchange was abandoned before it was over";
2129
2135
  /** Hard cap on the peer-error header (M3): see `encodeErrorResponse`. */
2130
2136
  const MAX_DETAIL_CHARS = 4096;
2131
2137
  /**
@@ -2306,7 +2312,11 @@ async function drain(output) {
2306
2312
  */
2307
2313
  function httpServe(handler, options = {}) {
2308
2314
  const codec = options.codec ?? defaultCodec;
2309
- return async function* httpHandlerDuplex(input) {
2315
+ return (input) => {
2316
+ const abandoned = new AbortController();
2317
+ return abortingOnReturn(exchange(input, { signal: abandoned.signal }), (reason) => abandoned.abort(reason ?? /* @__PURE__ */ new Error("webrun-http-streams: the exchange was abandoned before it was over")));
2318
+ };
2319
+ async function* exchange(input, context) {
2310
2320
  let decoded;
2311
2321
  try {
2312
2322
  decoded = await codec.decodeRequest(input);
@@ -2318,7 +2328,7 @@ function httpServe(handler, options = {}) {
2318
2328
  const replyCodec = decoded.codec ?? codec;
2319
2329
  let result;
2320
2330
  try {
2321
- result = await handler(decoded.envelope, decoded.body);
2331
+ result = await handler(decoded.envelope, decoded.body, context);
2322
2332
  } catch (error) {
2323
2333
  yield* encodeErrorResponse(replyCodec, error, decoded.envelope.method);
2324
2334
  return;
@@ -2333,7 +2343,36 @@ function httpServe(handler, options = {}) {
2333
2343
  await result.release?.();
2334
2344
  } catch {}
2335
2345
  }
2346
+ }
2347
+ }
2348
+ /**
2349
+ * `gen`, except that `return()` and `throw()` from its consumer call `abandon`
2350
+ * at once — before they are queued behind a `next()` still parked inside the
2351
+ * generator (the handler's `await`) — unless the generator already finished.
2352
+ */
2353
+ function abortingOnReturn(gen, abandon) {
2354
+ let finished = false;
2355
+ const settle = (r) => {
2356
+ if (r.done) finished = true;
2357
+ return r;
2358
+ };
2359
+ const wrapped = {
2360
+ next: (...args) => gen.next(...args).then(settle),
2361
+ return(value) {
2362
+ if (!finished) abandon();
2363
+ finished = true;
2364
+ return gen.return(value);
2365
+ },
2366
+ throw(error) {
2367
+ if (!finished) abandon(error);
2368
+ finished = true;
2369
+ return gen.throw(error);
2370
+ },
2371
+ [Symbol.asyncIterator]() {
2372
+ return wrapped;
2373
+ }
2336
2374
  };
2375
+ return asyncDisposable$2(wrapped, () => wrapped.return(void 0));
2337
2376
  }
2338
2377
  /**
2339
2378
  * The one place that knows a runtime may not implement request body streams,
@@ -2598,17 +2637,18 @@ async function fetchOverDuplex(call, request, options = {}) {
2598
2637
  else await release?.();
2599
2638
  return new Response(null, responseInit);
2600
2639
  }
2601
- return new Response(asyncIterableToReadable(withAbort(respBody, request.signal)), responseInit);
2640
+ return new Response(responseBody(respBody, request.signal, release), responseInit);
2602
2641
  }
2603
2642
  /**
2604
2643
  * Wrap a `(Request) => Promise<Response>` handler as a `Duplex` so it can be
2605
2644
  * registered with any `webrun-streams-*` adapter's `serve`.
2606
2645
  */
2607
2646
  function serveFetchOverDuplex(handler, options = {}) {
2608
- return httpServe(async (env, body) => {
2647
+ return httpServe(async (env, body, exchange) => {
2609
2648
  const reqInit = {
2610
2649
  method: env.method,
2611
- headers: forwardableHeaders(env.headers)
2650
+ headers: forwardableHeaders(env.headers),
2651
+ signal: exchange?.signal
2612
2652
  };
2613
2653
  let upload;
2614
2654
  if (env.method !== "GET" && env.method !== "HEAD") {
@@ -2729,15 +2769,78 @@ function trackingEnd(source) {
2729
2769
  settle
2730
2770
  };
2731
2771
  }
2732
- async function* withAbort(iter, signal) {
2733
- if (!signal) {
2734
- yield* iter;
2735
- return;
2736
- }
2737
- for await (const chunk of iter) {
2738
- if (signal.aborted) throw abortReason$1(signal);
2739
- yield chunk;
2740
- }
2772
+ /**
2773
+ * How long a cancelled body waits for the read in flight before it abandons the call anyway.
2774
+ * The read's answer tells a body that has ended (the call is finished: an upload the handler
2775
+ * reads after answering goes on) from one that has not; a peer that sends nothing more never
2776
+ * answers it. A body whose end comes later than this after the cancel is abandoned too, so a
2777
+ * handler still streaming its response and still reading the upload then loses the upload.
2778
+ */
2779
+ const CANCEL_GRACE_MS = 1e3;
2780
+ /**
2781
+ * The response body as a stream that the request's signal can end at any time.
2782
+ *
2783
+ * An abort errors the stream with the abort's reason at once — a read waiting
2784
+ * for a peer that sends nothing more rejects then, not when a next chunk
2785
+ * arrives, which may be never — and abandons the call (`release`), so on a
2786
+ * multiplexed transport its port is given back whether or not anyone still
2787
+ * reads the body. The listener is removed once the body ends, however it ends:
2788
+ * read to its end, cancelled, failed or aborted. A body nobody reads or cancels
2789
+ * keeps it until the signal aborts (and keeps the call open until then anyway).
2790
+ * A cancel abandons the call too — unless the read in flight comes back with the
2791
+ * body's end, which finishes it instead (see `cancel`).
2792
+ */
2793
+ function responseBody(body, signal, release) {
2794
+ const it = body[Symbol.asyncIterator]();
2795
+ let controller;
2796
+ let ended = false;
2797
+ let reading = false;
2798
+ const end = () => {
2799
+ ended = true;
2800
+ signal?.removeEventListener("abort", onAbort);
2801
+ };
2802
+ const onAbort = () => {
2803
+ if (ended) return;
2804
+ end();
2805
+ try {
2806
+ controller.error(abortReason$1(signal));
2807
+ } catch {}
2808
+ release?.();
2809
+ };
2810
+ return new ReadableStream({
2811
+ start(c) {
2812
+ controller = c;
2813
+ if (signal?.aborted) onAbort();
2814
+ else signal?.addEventListener("abort", onAbort, { once: true });
2815
+ },
2816
+ async pull(c) {
2817
+ try {
2818
+ reading = true;
2819
+ const { done, value } = await it.next().finally(() => {
2820
+ reading = false;
2821
+ });
2822
+ if (ended) return;
2823
+ if (done) {
2824
+ end();
2825
+ c.close();
2826
+ return;
2827
+ }
2828
+ c.enqueue(value);
2829
+ } catch (err) {
2830
+ if (ended) return;
2831
+ end();
2832
+ c.error(err);
2833
+ }
2834
+ },
2835
+ cancel() {
2836
+ end();
2837
+ const returned = Promise.resolve(it.return?.()).catch(() => {});
2838
+ if (!reading) return;
2839
+ const grace = setTimeout(() => void release?.(), CANCEL_GRACE_MS);
2840
+ grace.unref?.();
2841
+ returned.finally(() => clearTimeout(grace));
2842
+ }
2843
+ });
2741
2844
  }
2742
2845
  /**
2743
2846
  * Host a `SiteHandler` over a `Connect/Serve` pair. The `start(serve, params)`
@@ -3024,8 +3127,11 @@ function listenPort(port, handler, { channelName = "", log = () => {} } = {}) {
3024
3127
  error
3025
3128
  });
3026
3129
  };
3027
- port.addEventListener("message", onMessage);
3028
- return () => port.removeEventListener("message", onMessage);
3130
+ const listener = (event) => {
3131
+ onMessage(event);
3132
+ };
3133
+ port.addEventListener("message", listener);
3134
+ return () => port.removeEventListener("message", listener);
3029
3135
  }
3030
3136
  /**
3031
3137
  * Wraps an async iterable so that an `AbortSignal` firing causes the wrapper
@@ -3403,7 +3509,11 @@ function receiveChunks(port, channelName, controller, touch) {
3403
3509
  * outstanding at a time (spec D11/D12), with no per-chunk deadline (spec D8).
3404
3510
  */
3405
3511
  async function sendChunks(port, channelName, output, { maxMessageSize, log }, signal, touch) {
3406
- const stream = throughAbort(maxMessageSize ? toChunks(maxMessageSize)(output) : output, signal);
3512
+ const framed = maxMessageSize ? toChunks(maxMessageSize)(output) : output;
3513
+ const stream = throughAbort(framed, signal);
3514
+ const producer = output;
3515
+ const returnProducer = () => void Promise.resolve(producer.return?.()).catch(() => {});
3516
+ if (framed !== output) signal.addEventListener("abort", returnProducer, { once: true });
3407
3517
  try {
3408
3518
  await sendIterator(async ({ done, value, error }) => {
3409
3519
  if (signal.aborted) return;
@@ -3427,9 +3537,38 @@ async function sendChunks(port, channelName, output, { maxMessageSize, log }, si
3427
3537
  } catch (err) {
3428
3538
  if (signal.aborted) return;
3429
3539
  throw err;
3540
+ } finally {
3541
+ signal.removeEventListener("abort", returnProducer);
3430
3542
  }
3431
3543
  }
3432
3544
  /**
3545
+ * Runs `fn` on a later task, never a microtask: a producer looping on
3546
+ * `whenPortReady` must let the transport's own events run. `setImmediate`
3547
+ * where it exists; elsewhere a `MessageChannel`, because nested `setTimeout`s
3548
+ * are clamped to 4 ms in browsers.
3549
+ */
3550
+ const setImmediateFn = globalThis.setImmediate;
3551
+ /** @internal */
3552
+ const nextTask = setImmediateFn ? (fn) => {
3553
+ setImmediateFn(fn);
3554
+ } : (() => {
3555
+ let channel;
3556
+ const queue = [];
3557
+ return (fn) => {
3558
+ if (!channel) {
3559
+ channel = new MessageChannel();
3560
+ channel.port1.onmessage = () => queue.shift()?.();
3561
+ }
3562
+ queue.push(fn);
3563
+ channel.port2.postMessage(0);
3564
+ };
3565
+ })();
3566
+ const states = /* @__PURE__ */ new WeakMap();
3567
+ /** @internal Registered by the multiplexer for each port with a window. */
3568
+ function setPortSendState(port, state) {
3569
+ states.set(port, state);
3570
+ }
3571
+ /**
3433
3572
  * One virtual port.
3434
3573
  *
3435
3574
  * `deliver` and `markClosed` are deliberately not on `port`: the consumer holds
@@ -3444,6 +3583,14 @@ function newVirtualPort(send, requestClose) {
3444
3583
  if (closing.signal.aborted) return;
3445
3584
  closing.abort(reason ?? /* @__PURE__ */ new Error("webrun-rpc: the port was closed"));
3446
3585
  };
3586
+ const close = (reason) => {
3587
+ if (closed) return;
3588
+ closed = true;
3589
+ const notify = requestClose;
3590
+ listeners.clear();
3591
+ notify(reason);
3592
+ abort(reason);
3593
+ };
3447
3594
  const port = {
3448
3595
  addEventListener(_type, listener) {
3449
3596
  listeners.add(listener);
@@ -3455,24 +3602,28 @@ function newVirtualPort(send, requestClose) {
3455
3602
  if (closed) return;
3456
3603
  send(message, transfer);
3457
3604
  },
3458
- close() {
3459
- if (closed) return;
3460
- closed = true;
3461
- const notify = requestClose;
3462
- listeners.clear();
3463
- notify();
3464
- abort();
3465
- }
3605
+ close
3466
3606
  };
3467
3607
  setPortCloseSignal(port, closing.signal);
3608
+ const dispose = Symbol.dispose;
3609
+ if (dispose) Object.defineProperty(port, dispose, {
3610
+ value: () => close(),
3611
+ configurable: true
3612
+ });
3468
3613
  return {
3469
3614
  port,
3470
3615
  deliver(payload) {
3471
3616
  if (closed) return;
3472
3617
  const event = new MessageEvent("message", { data: payload });
3618
+ let pending;
3473
3619
  for (const listener of [...listeners]) try {
3474
- listener(event);
3620
+ const result = listener(event);
3621
+ if (result && typeof result.then === "function") {
3622
+ pending ??= [];
3623
+ pending.push(result);
3624
+ }
3475
3625
  } catch {}
3626
+ return pending ? Promise.allSettled(pending) : void 0;
3476
3627
  },
3477
3628
  markClosed(reason) {
3478
3629
  closed = true;
@@ -3481,53 +3632,318 @@ function newVirtualPort(send, requestClose) {
3481
3632
  },
3482
3633
  isClosed() {
3483
3634
  return closed;
3635
+ },
3636
+ hasListeners() {
3637
+ return listeners.size > 0;
3484
3638
  }
3485
3639
  };
3486
3640
  }
3487
3641
  /**
3488
- * The message of the error that fails the calls still in flight on a
3489
- * multiplexer when it is closed (`PortMux.close()`).
3642
+ * The id of keepalive's `ping` / `pong`: one no port ever takes. An older mux
3643
+ * whose codec passes unknown envelopes through reads them as a close of that
3644
+ * id, which must therefore not be a real port (0 is the initiator's first).
3490
3645
  */
3491
- const MUX_CLOSED_MESSAGE = "webrun-rpc: the multiplexer was closed";
3646
+ const KEEPALIVE_ID = Number.MAX_SAFE_INTEGER;
3647
+ /** The longest delay a timer holds; past it `setInterval` fires at once. */
3648
+ const MAX_TIMER_MS = 2 ** 31 - 1;
3492
3649
  /** Ceiling on concurrently open virtual ports. Bounds the id table only. */
3493
3650
  const DEFAULT_MAX_PORTS = 1024;
3494
3651
  /**
3652
+ * What a message costs a port's window when the codec cannot say
3653
+ * (`PortCodec.encode`): its bytes and string lengths, found in its values up
3654
+ * to 4 levels deep (at most 256 values), and at least 64. Both ends compute it
3655
+ * from the same structured value, so they agree.
3656
+ */
3657
+ function estimateMessageSize(payload) {
3658
+ let budget = 256;
3659
+ const walk = (v, depth) => {
3660
+ if (budget-- <= 0) return 0;
3661
+ if (typeof v === "string") return v.length;
3662
+ if (v instanceof ArrayBuffer) return v.byteLength;
3663
+ if (ArrayBuffer.isView(v)) return v.byteLength;
3664
+ if (v === null || typeof v !== "object" || depth >= 4) return 8;
3665
+ let n = 16;
3666
+ if (Array.isArray(v)) for (const x of v) n += walk(x, depth + 1);
3667
+ else for (const k in v) n += k.length + walk(v[k], depth + 1);
3668
+ return n;
3669
+ };
3670
+ return Math.max(64, walk(payload, 0));
3671
+ }
3672
+ /**
3495
3673
  * The default `PortMux`: emulates multiplexing over a single port.
3496
3674
  *
3497
3675
  * A transport that already multiplexes natively supplies its own `PortMux`
3498
3676
  * instead — this implementation is for transports that offer one pipe.
3499
3677
  */
3500
3678
  function multiplexPort(port, options) {
3501
- const { codec, onPort, side = "initiator", maxPorts = DEFAULT_MAX_PORTS, maxMessageSize } = options;
3679
+ const { codec, onPort, side = "initiator", maxPorts = DEFAULT_MAX_PORTS, maxMessageSize, signal, window, frameSize, keepalive, onLeak } = options;
3680
+ const maxReassembledBytes = options.maxReassembledBytes ?? 67108864;
3681
+ const misses = keepalive?.misses ?? 3;
3682
+ if (keepalive && !(Number.isFinite(keepalive.intervalMs) && keepalive.intervalMs > 0 && keepalive.intervalMs <= MAX_TIMER_MS && Number.isSafeInteger(misses) && misses > 0)) throw new RangeError(`webrun-rpc: keepalive needs an intervalMs in (0, ${MAX_TIMER_MS}] and a positive integer misses`);
3683
+ if (frameSize != null && !(Number.isSafeInteger(frameSize) && frameSize > 0)) throw new RangeError(`webrun-rpc: frameSize must be a positive integer, got ${frameSize}`);
3684
+ if (!(Number.isSafeInteger(maxReassembledBytes) && maxReassembledBytes > 0)) throw new RangeError(`webrun-rpc: maxReassembledBytes must be a positive integer, got ${maxReassembledBytes}`);
3502
3685
  const open = /* @__PURE__ */ new Map();
3686
+ let reassembling = 0;
3687
+ const freePartial = (entry) => {
3688
+ if (!entry?.partial) return;
3689
+ reassembling -= entry.partial.charged;
3690
+ entry.partial = void 0;
3691
+ };
3692
+ const leaks = onLeak ? new FinalizationRegistry(({ id, record, release }) => {
3693
+ const entry = open.get(id);
3694
+ if (entry?.handle !== record) return;
3695
+ const { handle: _, credit: __, partial: ___, ...info } = entry;
3696
+ if (!record.hasListeners()) {
3697
+ release("leaked");
3698
+ record.markClosed(/* @__PURE__ */ new Error("webrun-rpc: the port was dropped without close()"));
3699
+ }
3700
+ try {
3701
+ onLeak?.(info);
3702
+ } catch {}
3703
+ }) : void 0;
3704
+ const touch = (id) => {
3705
+ const entry = open.get(id);
3706
+ if (entry) entry.lastActivity = Date.now();
3707
+ };
3503
3708
  let nextId = side === "initiator" ? 0 : 1;
3504
3709
  let muxClosed = false;
3710
+ /** This end counts encoded bytes and reassembles frames. */
3711
+ const exactCodec = codec.encode != null && codec.decode != null;
3505
3712
  const post = (envelope, transfer) => {
3506
3713
  if (muxClosed) return;
3714
+ codecPost(envelope, transfer);
3715
+ };
3716
+ /** Like `post`, also while closing: the mux's own close notices. */
3717
+ const codecPost = (envelope, transfer) => {
3507
3718
  try {
3508
3719
  codec.post(port, envelope, transfer);
3509
3720
  } catch {}
3510
3721
  };
3511
- const attach = (id) => {
3512
- let self;
3513
- self = newVirtualPort((payload, transfer) => post({
3514
- type: "message",
3515
- id,
3516
- payload
3517
- }, transfer), (reason) => {
3722
+ const outgoing = /* @__PURE__ */ new Map();
3723
+ const ring = [];
3724
+ let pumping = false;
3725
+ const pump = async () => {
3726
+ pumping = true;
3727
+ try {
3728
+ while (ring.length && !muxClosed) {
3729
+ if (port.whenDrained) await port.whenDrained();
3730
+ if (muxClosed) break;
3731
+ const id = ring.shift();
3732
+ const queue = outgoing.get(id);
3733
+ const item = queue?.shift();
3734
+ if (queue?.length) ring.push(id);
3735
+ else outgoing.delete(id);
3736
+ item?.();
3737
+ }
3738
+ } catch (err) {
3739
+ close(err).catch(() => {});
3740
+ } finally {
3741
+ pumping = false;
3742
+ }
3743
+ };
3744
+ const sendOnPort = (id, item) => {
3745
+ if (frameSize == null) {
3746
+ item();
3747
+ return;
3748
+ }
3749
+ let queue = outgoing.get(id);
3750
+ if (!queue) {
3751
+ queue = [];
3752
+ outgoing.set(id, queue);
3753
+ ring.push(id);
3754
+ }
3755
+ queue.push(item);
3756
+ if (!pumping) pump();
3757
+ };
3758
+ const dropOutgoing = (id) => {
3759
+ if (!outgoing.delete(id)) return;
3760
+ const at = ring.indexOf(id);
3761
+ if (at >= 0) ring.splice(at, 1);
3762
+ };
3763
+ const postBytes = (bytes) => {
3764
+ if (muxClosed) return;
3765
+ try {
3766
+ port.postMessage(bytes);
3767
+ } catch {}
3768
+ };
3769
+ const releaseWaiters = (c) => {
3770
+ for (const w of c?.waiters.splice(0) ?? []) w.resolve();
3771
+ };
3772
+ const flush = (c) => {
3773
+ while (c.queue.length) {
3774
+ const next = c.queue[0];
3775
+ if (c.enforced && c.outstanding > 0 && c.outstanding + next.size > c.window) break;
3776
+ c.queue.shift();
3777
+ c.queued -= next.size;
3778
+ c.outstanding += next.size;
3779
+ next.send();
3780
+ }
3781
+ for (const w of [...c.waiters]) if (c.queued <= w.below) {
3782
+ c.waiters.splice(c.waiters.indexOf(w), 1);
3783
+ w.resolve();
3784
+ }
3785
+ };
3786
+ const grant = (id, c, bytes) => {
3787
+ c.unconsumed -= bytes;
3788
+ c.toGrant += bytes;
3789
+ const send = () => {
3790
+ c.grantScheduled = false;
3791
+ if (!c.toGrant || !open.has(id)) return;
3518
3792
  post({
3793
+ type: "credit",
3794
+ id,
3795
+ bytes: c.toGrant
3796
+ });
3797
+ c.toGrant = 0;
3798
+ };
3799
+ if (c.toGrant >= c.window / 4) {
3800
+ send();
3801
+ return;
3802
+ }
3803
+ if (c.grantScheduled) return;
3804
+ c.grantScheduled = true;
3805
+ setTimeout(send, 0);
3806
+ };
3807
+ const attach = (id, meta, portWindow, opener, exact) => {
3808
+ const c = typeof portWindow === "number" && Number.isSafeInteger(portWindow) && portWindow > 0 ? {
3809
+ window: portWindow,
3810
+ enforced: false,
3811
+ opener,
3812
+ exact,
3813
+ outstanding: 0,
3814
+ queue: [],
3815
+ queued: 0,
3816
+ unconsumed: 0,
3817
+ waiters: [],
3818
+ toGrant: 0,
3819
+ grantScheduled: false
3820
+ } : void 0;
3821
+ const self = newVirtualPort((payload, transfer) => {
3822
+ touch(id);
3823
+ const envelope = {
3824
+ type: "message",
3825
+ id,
3826
+ payload
3827
+ };
3828
+ if (!c) {
3829
+ post(envelope, transfer);
3830
+ return;
3831
+ }
3832
+ const bytes = c.exact ? codec.encode?.(envelope) : void 0;
3833
+ const size = bytes?.byteLength ?? estimateMessageSize(payload);
3834
+ const waits = frameSize != null || c.queue.length > 0 || c.enforced && c.outstanding > 0 && c.outstanding + size > c.window;
3835
+ const held = !bytes && waits ? {
3836
+ ...envelope,
3837
+ payload: structuredClone(payload, { transfer })
3838
+ } : void 0;
3839
+ const send = (whole = false) => {
3840
+ if (!bytes) {
3841
+ sendOnPort(id, held ? () => post(held) : () => post(envelope, transfer));
3842
+ return;
3843
+ }
3844
+ if (!whole && frameSize != null && c.enforced && c.exact && bytes.byteLength > frameSize) {
3845
+ for (let at = 0; at < bytes.byteLength; at += frameSize) {
3846
+ const data = bytes.subarray(at, at + frameSize);
3847
+ const frame = at + frameSize >= bytes.byteLength ? {
3848
+ type: "frame",
3849
+ id,
3850
+ data,
3851
+ last: true
3852
+ } : {
3853
+ type: "frame",
3854
+ id,
3855
+ data
3856
+ };
3857
+ sendOnPort(id, () => post(frame));
3858
+ }
3859
+ return;
3860
+ }
3861
+ sendOnPort(id, () => postBytes(bytes));
3862
+ };
3863
+ c.queue.push({
3864
+ send,
3865
+ size
3866
+ });
3867
+ c.queued += size;
3868
+ flush(c);
3869
+ }, (reason) => {
3870
+ release(reason);
3871
+ record.markClosed(reason);
3872
+ });
3873
+ function release(reason) {
3874
+ freePartial(open.get(id));
3875
+ open.delete(id);
3876
+ leaks?.unregister(record);
3877
+ if (c?.queue.length) {
3878
+ for (const item of c.queue.splice(0)) item.send(true);
3879
+ c.queued = 0;
3880
+ }
3881
+ sendOnPort(id, () => post({
3519
3882
  type: "close",
3520
3883
  id,
3521
3884
  reason
3885
+ }));
3886
+ releaseWaiters(c);
3887
+ }
3888
+ const { markClosed, isClosed, hasListeners } = self;
3889
+ if (c) {
3890
+ const deliver = self.deliver;
3891
+ self.deliver = (payload, size = estimateMessageSize(payload)) => {
3892
+ c.unconsumed += size;
3893
+ const done = deliver(payload);
3894
+ if (done) done.then(() => grant(id, c, size));
3895
+ else grant(id, c, size);
3896
+ return done;
3897
+ };
3898
+ setPortSendState(self.port, {
3899
+ bufferedAmount: () => c.queued,
3900
+ ready: (below = 0) => new Promise((resolve) => {
3901
+ if (c.queued <= below || isClosed()) nextTask(resolve);
3902
+ else c.waiters.push({
3903
+ below,
3904
+ resolve: () => nextTask(resolve)
3905
+ });
3906
+ })
3522
3907
  });
3523
- open.delete(id);
3524
- self.markClosed();
3908
+ }
3909
+ const record = {
3910
+ deliver: self.deliver,
3911
+ markClosed,
3912
+ isClosed,
3913
+ hasListeners,
3914
+ close: (reason) => {
3915
+ if (isClosed()) return;
3916
+ release(reason);
3917
+ markClosed(reason);
3918
+ }
3919
+ };
3920
+ leaks?.register(self.port, {
3921
+ id,
3922
+ record,
3923
+ release
3924
+ }, record);
3925
+ const now = Date.now();
3926
+ open.set(id, {
3927
+ id,
3928
+ meta,
3929
+ openedAt: now,
3930
+ lastActivity: now,
3931
+ handle: record,
3932
+ credit: c
3525
3933
  });
3526
- open.set(id, self);
3527
3934
  return self;
3528
3935
  };
3529
- const handleEnvelope = (envelope) => {
3936
+ const handleEnvelope = (envelope, event) => {
3530
3937
  if (muxClosed) return;
3938
+ heardSincePing = true;
3939
+ if (envelope.type === "ping" || envelope.type === "pong") {
3940
+ peerKeepsAlive = true;
3941
+ if (envelope.type === "ping") post({
3942
+ type: "pong",
3943
+ id: KEEPALIVE_ID
3944
+ });
3945
+ return;
3946
+ }
3531
3947
  if (envelope.type === "open") {
3532
3948
  if (open.has(envelope.id)) return;
3533
3949
  if (open.size >= maxPorts) {
@@ -3538,7 +3954,18 @@ function multiplexPort(port, options) {
3538
3954
  });
3539
3955
  return;
3540
3956
  }
3541
- const handle = attach(envelope.id);
3957
+ const handle = attach(envelope.id, envelope.meta, envelope.window, false, envelope.exact === true && exactCodec);
3958
+ const credit = open.get(envelope.id)?.credit;
3959
+ if (credit) post(credit.exact ? {
3960
+ type: "credit",
3961
+ id: envelope.id,
3962
+ bytes: 0,
3963
+ exact: true
3964
+ } : {
3965
+ type: "credit",
3966
+ id: envelope.id,
3967
+ bytes: 0
3968
+ });
3542
3969
  let accepted = false;
3543
3970
  if (onPort) try {
3544
3971
  accepted = onPort(handle.port, envelope.meta) !== false;
@@ -3553,57 +3980,215 @@ function multiplexPort(port, options) {
3553
3980
  id: envelope.id,
3554
3981
  reason: "rejected"
3555
3982
  });
3983
+ return;
3556
3984
  }
3557
3985
  return;
3558
3986
  }
3559
- const handle = open.get(envelope.id);
3987
+ const entry = open.get(envelope.id);
3988
+ if (envelope.type === "credit") {
3989
+ const c = entry?.credit;
3990
+ if (!c || !(envelope.bytes >= 0)) return;
3991
+ if (!c.enforced && c.opener) {
3992
+ if (c.exact && envelope.exact !== true) {
3993
+ c.exact = false;
3994
+ c.outstanding = 0;
3995
+ }
3996
+ post({
3997
+ type: "credit",
3998
+ id: envelope.id,
3999
+ bytes: 0
4000
+ });
4001
+ }
4002
+ c.enforced = true;
4003
+ c.outstanding = Math.max(0, c.outstanding - envelope.bytes);
4004
+ flush(c);
4005
+ return;
4006
+ }
3560
4007
  if (envelope.type === "message") {
3561
- if (!handle) return;
3562
- handle.deliver(envelope.payload);
4008
+ const sizeOf = (c) => (c.exact ? byteLengthOf(event.data) : void 0) ?? estimateMessageSize(envelope.payload);
4009
+ if (!entry) return;
4010
+ touch(envelope.id);
4011
+ entry.handle.deliver(envelope.payload, entry.credit ? sizeOf(entry.credit) : void 0);
4012
+ return;
4013
+ }
4014
+ if (envelope.type === "frame") {
4015
+ if (!entry?.credit?.exact || !codec.decode || !(envelope.data instanceof Uint8Array)) return;
4016
+ touch(envelope.id);
4017
+ const c = entry.credit;
4018
+ const owed = c.unconsumed + c.toGrant;
4019
+ const partial = entry.partial ?? {
4020
+ chunks: [],
4021
+ bytes: 0,
4022
+ owed,
4023
+ charged: 0,
4024
+ ...owed > 0 ? { room: c.window - owed } : {}
4025
+ };
4026
+ entry.partial = partial;
4027
+ const size = envelope.data.byteLength;
4028
+ const charged = partial.room == null ? Math.max(0, partial.bytes + size - c.window) : 0;
4029
+ const overrun = partial.room != null && partial.bytes + size > partial.room ? `what the window left (${partial.room} of ${c.window} bytes, ${partial.owed} still owed)` : reassembling - partial.charged + charged > maxReassembledBytes ? `maxReassembledBytes (${maxReassembledBytes} bytes past the window, across all ports)` : void 0;
4030
+ if (overrun) {
4031
+ freePartial(entry);
4032
+ dropOutgoing(envelope.id);
4033
+ open.delete(envelope.id);
4034
+ releaseWaiters(c);
4035
+ entry.handle.markClosed(/* @__PURE__ */ new RangeError(`webrun-rpc: a framed message passed ${overrun}`));
4036
+ post({
4037
+ type: "close",
4038
+ id: envelope.id,
4039
+ reason: "max-reassembled-bytes"
4040
+ });
4041
+ return;
4042
+ }
4043
+ partial.bytes += size;
4044
+ reassembling += charged - partial.charged;
4045
+ partial.charged = charged;
4046
+ partial.chunks.push(envelope.data);
4047
+ if (!envelope.last) return;
4048
+ freePartial(entry);
4049
+ const whole = new Uint8Array(partial.bytes);
4050
+ let at = 0;
4051
+ for (const chunk of partial.chunks) {
4052
+ whole.set(chunk, at);
4053
+ at += chunk.byteLength;
4054
+ }
4055
+ const inner = codec.decode(whole);
4056
+ if (inner?.type !== "message" || inner.id !== envelope.id) return;
4057
+ entry.handle.deliver(inner.payload, whole.byteLength);
3563
4058
  return;
3564
4059
  }
3565
- if (!handle) return;
4060
+ dropOutgoing(envelope.id);
4061
+ if (!entry) return;
4062
+ freePartial(entry);
3566
4063
  open.delete(envelope.id);
3567
- handle.markClosed(/* @__PURE__ */ new Error("webrun-rpc: the peer closed the port"));
4064
+ releaseWaiters(entry.credit);
4065
+ entry.handle.markClosed(/* @__PURE__ */ new Error("webrun-rpc: the peer closed the port"));
3568
4066
  };
3569
4067
  const listener = (event) => {
3570
4068
  const envelope = codec.read(event);
3571
- if (envelope) handleEnvelope(envelope);
4069
+ if (envelope) handleEnvelope(envelope, event);
4070
+ };
4071
+ let peerKeepsAlive = false;
4072
+ let heardSincePing = true;
4073
+ let unanswered = 0;
4074
+ let pinger;
4075
+ const tick = () => {
4076
+ if (!keepalive) return;
4077
+ const backlogged = (port.bufferedAmount ?? 0) > 0;
4078
+ if (heardSincePing) unanswered = 0;
4079
+ else if (peerKeepsAlive && !backlogged) unanswered++;
4080
+ if (unanswered >= misses) {
4081
+ close(/* @__PURE__ */ new Error(`webrun-rpc: the peer did not answer ${misses} keepalive pings, ${keepalive.intervalMs} ms apart`)).catch(() => {});
4082
+ return;
4083
+ }
4084
+ heardSincePing = false;
4085
+ post({
4086
+ type: "ping",
4087
+ id: KEEPALIVE_ID
4088
+ });
4089
+ };
4090
+ const close = async (reason) => {
4091
+ if (muxClosed) return;
4092
+ clearInterval(pinger);
4093
+ muxClosed = true;
4094
+ signal?.removeEventListener("abort", onTransportAbort);
4095
+ port.removeEventListener("message", listener);
4096
+ const closedNow = /* @__PURE__ */ new Set();
4097
+ for (const [id, { handle, credit }] of [...open]) {
4098
+ closedNow.add(id);
4099
+ codecPost({
4100
+ type: "close",
4101
+ id
4102
+ });
4103
+ open.delete(id);
4104
+ releaseWaiters(credit);
4105
+ handle.markClosed(reason ?? /* @__PURE__ */ new Error("webrun-rpc: the multiplexer was closed"));
4106
+ }
4107
+ for (const id of outgoing.keys()) if (!closedNow.has(id)) codecPost({
4108
+ type: "close",
4109
+ id
4110
+ });
4111
+ outgoing.clear();
4112
+ ring.length = 0;
4113
+ await port.close?.();
4114
+ };
4115
+ const onTransportAbort = () => {
4116
+ close(signal?.reason).catch(() => {});
3572
4117
  };
3573
4118
  port.addEventListener("message", listener);
3574
4119
  port.start?.();
4120
+ if (keepalive) {
4121
+ pinger = setInterval(tick, keepalive.intervalMs);
4122
+ pinger.unref?.();
4123
+ }
4124
+ if (signal?.aborted) close(signal.reason).catch(() => {});
4125
+ else signal?.addEventListener("abort", onTransportAbort, { once: true });
3575
4126
  return {
3576
4127
  maxMessageSize,
3577
- async openPort(meta) {
4128
+ async openPort(meta, { signal: portSignal } = {}) {
3578
4129
  if (muxClosed) throw new Error("webrun-rpc: the multiplexer is closed");
3579
- if (open.size >= maxPorts) throw new RangeError(`webrun-rpc: maxPorts (${maxPorts}) reached`);
4130
+ if (portSignal?.aborted) throw portSignal.reason;
4131
+ if (open.size >= maxPorts) throw new RangeError(`webrun-rpc: maxPorts (${maxPorts}) reached; open ports by kind: ${describeKinds(open.values())}`);
3580
4132
  while (open.has(nextId)) nextId += 2;
3581
4133
  const id = nextId;
3582
4134
  nextId += 2;
3583
- const handle = attach(id);
3584
- post({
4135
+ const handle = attach(id, meta, window, true, exactCodec);
4136
+ post(window ? exactCodec ? {
4137
+ type: "open",
4138
+ id,
4139
+ meta,
4140
+ window,
4141
+ exact: true
4142
+ } : {
4143
+ type: "open",
4144
+ id,
4145
+ meta,
4146
+ window
4147
+ } : {
3585
4148
  type: "open",
3586
4149
  id,
3587
4150
  meta
3588
4151
  });
4152
+ if (portSignal) {
4153
+ const record = open.get(id)?.handle;
4154
+ const onAbort = () => {
4155
+ if (record && open.get(id)?.handle === record) record.close(portSignal.reason);
4156
+ };
4157
+ portSignal.addEventListener("abort", onAbort, { once: true });
4158
+ getPortCloseSignal(handle.port)?.addEventListener("abort", () => portSignal.removeEventListener("abort", onAbort), { once: true });
4159
+ }
3589
4160
  return handle.port;
3590
4161
  },
3591
- async close() {
3592
- if (muxClosed) return;
3593
- for (const [id, handle] of [...open]) {
3594
- post({
3595
- type: "close",
3596
- id
3597
- });
3598
- open.delete(id);
3599
- handle.markClosed(/* @__PURE__ */ new Error(MUX_CLOSED_MESSAGE));
3600
- }
3601
- muxClosed = true;
3602
- port.removeEventListener("message", listener);
3603
- await port.close?.();
4162
+ close,
4163
+ ports() {
4164
+ return [...open.values()].map(({ handle: _, credit, ...info }) => credit ? {
4165
+ ...info,
4166
+ ...credit.enforced ? { window: credit.window } : {},
4167
+ queued: credit.queued,
4168
+ outstanding: credit.outstanding,
4169
+ unconsumed: credit.unconsumed
4170
+ } : info);
3604
4171
  }
3605
4172
  };
3606
4173
  }
4174
+ /** The byte length of a received message's data, or `undefined` if it is not bytes. */
4175
+ function byteLengthOf(data) {
4176
+ if (data instanceof ArrayBuffer || ArrayBuffer.isView(data)) return data.byteLength;
4177
+ }
4178
+ /**
4179
+ * `"http: 1000, ws: 24"`: the three commonest `meta.kind`s among `entries`, so
4180
+ * a full table names who filled it. A port without a kind counts as `"?"`.
4181
+ */
4182
+ function describeKinds(entries) {
4183
+ const counts = /* @__PURE__ */ new Map();
4184
+ for (const { meta } of entries) {
4185
+ const kind = meta?.kind;
4186
+ const name = typeof kind === "string" ? kind : "?";
4187
+ counts.set(name, (counts.get(name) ?? 0) + 1);
4188
+ }
4189
+ if (!counts.size) return "none";
4190
+ return [...counts].sort((x, y) => y[1] - x[1]).slice(0, 3).map(([name, n]) => `${name}: ${n}`).join(", ");
4191
+ }
3607
4192
  /**
3608
4193
  * Announced with every port this pair opens. Layer 1 never reads `meta` — it is
3609
4194
  * here so a peer running something else over the same multiplexer can tell a
@@ -3665,14 +4250,17 @@ const serve = async ({ mux: factory, timeout }, handler) => {
3665
4250
  const mux = await factory((port) => {
3666
4251
  let off;
3667
4252
  let finished = false;
3668
- const tracked = (input) => (async function* () {
3669
- try {
3670
- yield* handler(input);
3671
- } finally {
3672
- finished = true;
3673
- if (off) live.delete(off);
3674
- }
3675
- })();
4253
+ const tracked = (input) => {
4254
+ const output = handler(input);
4255
+ return returningAtOnce((async function* () {
4256
+ try {
4257
+ yield* output;
4258
+ } finally {
4259
+ finished = true;
4260
+ if (off) live.delete(off);
4261
+ }
4262
+ })(), () => void output.return(void 0).catch(() => {}));
4263
+ };
3676
4264
  off = serveDuplexOverPort(port, tracked, streamOptions);
3677
4265
  if (!finished) live.add(off);
3678
4266
  });
@@ -3710,7 +4298,7 @@ function isEnvelope(value) {
3710
4298
  const candidate = value;
3711
4299
  if (typeof candidate.id !== "number") return false;
3712
4300
  if (!Number.isInteger(candidate.id) || candidate.id < 0) return false;
3713
- return candidate.type === "open" || candidate.type === "message" || candidate.type === "close";
4301
+ return candidate.type === "open" || candidate.type === "message" || candidate.type === "close" || candidate.type === "credit" || candidate.type === "frame" || candidate.type === "ping" || candidate.type === "pong";
3714
4302
  }
3715
4303
  /**
3716
4304
  * For ports whose messages are structured values — a real `MessagePort`, a
@@ -3872,7 +4460,7 @@ function trackedLink(send, release) {
3872
4460
  finish();
3873
4461
  return response;
3874
4462
  }
3875
- return new Response(guardBody(response.body, lost, () => lostReason, finish), {
4463
+ return new Response(guardBody(response.body, lost, () => lostReason, finish, request.signal), {
3876
4464
  status: response.status,
3877
4465
  statusText: response.statusText,
3878
4466
  headers: response.headers
@@ -3891,33 +4479,65 @@ function trackedLink(send, release) {
3891
4479
  return asyncDisposable(link, () => link.close());
3892
4480
  }
3893
4481
  /**
3894
- * A response body that reports its end (done, cancelled or failed) and fails
3895
- * with the link's reason when the link is lost mid-stream.
4482
+ * A response body that reports its end (done, cancelled, failed or aborted) and
4483
+ * fails with the link's reason when the link is lost mid-stream. An abort of the
4484
+ * request's signal ends it at once, read or not: the exchange is over (the layer
4485
+ * below has given its port back), so the request leaves `inflight` then.
3896
4486
  */
3897
- function guardBody(body, lost, reason, finish) {
4487
+ function guardBody(body, lost, reason, finish, signal) {
3898
4488
  const reader = body.getReader();
4489
+ let controller;
4490
+ let ended = false;
4491
+ const end = () => {
4492
+ ended = true;
4493
+ signal?.removeEventListener("abort", onAbort);
4494
+ finish();
4495
+ };
4496
+ const onAbort = () => {
4497
+ if (ended) return;
4498
+ end();
4499
+ reader.cancel().catch(() => {});
4500
+ try {
4501
+ controller.error(abortError(signal));
4502
+ } catch {}
4503
+ };
3899
4504
  return new ReadableStream({
4505
+ start(c) {
4506
+ controller = c;
4507
+ if (signal?.aborted) onAbort();
4508
+ else signal?.addEventListener("abort", onAbort, { once: true });
4509
+ },
3900
4510
  async pull(controller) {
3901
4511
  try {
3902
4512
  const { done, value } = await Promise.race([reader.read(), lost]);
4513
+ if (ended) return;
3903
4514
  if (done) {
3904
- finish();
4515
+ end();
3905
4516
  controller.close();
3906
4517
  return;
3907
4518
  }
3908
4519
  controller.enqueue(value);
3909
4520
  } catch (error) {
3910
- finish();
4521
+ if (ended) return;
4522
+ end();
3911
4523
  reader.cancel().catch(() => {});
3912
4524
  controller.error(reason() ?? error);
3913
4525
  }
3914
4526
  },
3915
4527
  async cancel(why) {
3916
- finish();
4528
+ end();
3917
4529
  await reader.cancel(why).catch(() => {});
3918
4530
  }
3919
4531
  });
3920
4532
  }
4533
+ /** The signal's reason as an `Error` (`AbortError` when it gave none). */
4534
+ function abortError(signal) {
4535
+ const why = signal.reason;
4536
+ if (why instanceof Error) return why;
4537
+ const err = new Error(why === void 0 ? "Aborted" : String(why));
4538
+ err.name = "AbortError";
4539
+ return err;
4540
+ }
3921
4541
  //#endregion
3922
4542
  //#region src/link/locks.ts
3923
4543
  function lockManager() {
@@ -6638,6 +7258,8 @@ var WorkerLinkImpl = class {
6638
7258
  _ownerLock;
6639
7259
  _attaching;
6640
7260
  _detached = false;
7261
+ /** The `seq` of the last link sent to the worker. */
7262
+ _sent = 0;
6641
7263
  constructor(_adapter, _control, init) {
6642
7264
  this._adapter = _adapter;
6643
7265
  this._control = _control;
@@ -6649,6 +7271,7 @@ var WorkerLinkImpl = class {
6649
7271
  _control.onmessage = (event) => {
6650
7272
  const message = event.data;
6651
7273
  if (message?.type === "relink") {
7274
+ if (message.seen !== void 0 && message.seen < this._sent) return;
6652
7275
  this._adapter._poke(WAKE_MESSAGE);
6653
7276
  this.attach().catch(() => {});
6654
7277
  } else if (message?.type === "keepalive") this._adapter._poke(KEEPALIVE_MESSAGE);
@@ -6695,7 +7318,8 @@ var WorkerLinkImpl = class {
6695
7318
  this._control.postMessage({
6696
7319
  type: "link",
6697
7320
  key: this.key,
6698
- routerLock: reply.routerLock
7321
+ routerLock: reply.routerLock,
7322
+ seq: ++this._sent
6699
7323
  }, [
6700
7324
  down.port2,
6701
7325
  ctl.port2,
@@ -6796,12 +7420,18 @@ function serveHttpInWorker(handler, options = {}) {
6796
7420
  const ownerLock = newLockName("webrun-http-owner");
6797
7421
  const held = holdLock(ownerLock);
6798
7422
  const controls = /* @__PURE__ */ new Set();
7423
+ const seen = /* @__PURE__ */ new Map();
6799
7424
  const tellPages = (message) => {
6800
7425
  for (const control of controls) control.postMessage(message);
6801
7426
  };
6802
7427
  const endpoint = new HandlerEndpoint(handler, {
6803
7428
  ...options,
6804
- requestRelink: () => tellPages({ type: "relink" }),
7429
+ requestRelink: () => {
7430
+ for (const control of controls) control.postMessage({
7431
+ type: "relink",
7432
+ seen: seen.get(control) ?? 0
7433
+ });
7434
+ },
6805
7435
  keepAlive: () => tellPages({ type: "keepalive" })
6806
7436
  });
6807
7437
  const onMessage = (event) => {
@@ -6813,6 +7443,7 @@ function serveHttpInWorker(handler, options = {}) {
6813
7443
  control.onmessage = (message) => {
6814
7444
  const msg = message.data;
6815
7445
  if (msg?.type === "link") {
7446
+ if (msg.seq !== void 0) seen.set(control, msg.seq);
6816
7447
  const [down, ctl, up] = message.ports;
6817
7448
  if (down) endpoint.link(msg.key, {
6818
7449
  down,
@@ -6853,9 +7484,10 @@ function serveHttpInWorker(handler, options = {}) {
6853
7484
  await endpoint.close();
6854
7485
  for (const control of controls) control.close();
6855
7486
  controls.clear();
7487
+ seen.clear();
6856
7488
  (await held)();
6857
7489
  }
6858
7490
  };
6859
7491
  }
6860
7492
  //#endregion
6861
- export { CLAIM_CALL, DEFAULT_CLOSE_TIMEOUT_MS, DEFAULT_HEARTBEAT_MS, DEFAULT_SERVICE_WORKER_TIMEOUT, DuplexSiteBuilder, HANDLER_LOST_MESSAGE, HTTP_LINK_PROTOCOL, HandlerEndpoint, HandlerTable, HttpError, HttpParseError, LinkLostError, PEER_ERROR_HEADER, PortRendezvous, ROUTER_LOST_MESSAGE, ServerWebSocket, ServiceWorkerControlError, SwHttpAdapter, SwHttpDispatcher, SwPortDispatcher, SwPortHandler, TransportClosedError, WS_PORT_PROTOCOL_VERSION, WS_VERSION_HEADER, WsCloseEvent, answerConnect, awaitActiveServiceWorker, awaitServiceWorkerControl, callChannel, callChannelWithin, callHttpService, callWebSocketService, collect, collectBytes, collectString, connectHttpLink, connectWebSocketThroughServiceWorker, createWebSocketClass, decodeJsonl, decodeMessage, decodeText, defaultCodec, deserializeError, emulateMux, encodeJsonl, encodeMessage, encodeText, fetchOverDuplex, fromReadableStream, getRelayWindowMessageHandler, handleChannelCalls, handleClaimRequests, handleHttpRequests, handleStreams, holdLock, httpCodec, httpFetch, httpServe, initHttpService, initServiceWorker, initializeConnection, isWebSocketUpgrade, joinLines, jsonEnvelopeCodec, leastInflightRoute, locksAvailable, map, newAsyncGenerator, newCreditGrantor, newCreditLedger, newHttpClientStub, newHttpCodec, newHttpServerStub, newInvokationChannel, newLockName, newRegistry, newRemoteRelayChannel, newServiceWorkerPort, newSniffingCodec, newWebSocketUpgradeRequest, normalizeToUint8Array, recieveIterator, registerConnectionsHandler, relayWebSocketClass, requestedProtocols, sendHttpRequest, sendIterator, sendOverConnection, sendStream, serializeError, serveFetchOverDuplex, serveHttpInWorker, serveHttpLink, serveWebSocketUpgrade, serviceWorkerWebSocketClass, splitLines, splitServiceUrl, startHttpDispatcher, takePort, takeUpgrade, toChunks, toReadableStream, trackedLink, unwrapTransfer, upgradeWebSocket, watchLock, withHeaders, withTransfer };
7493
+ export { CLAIM_CALL, DEFAULT_CLOSE_TIMEOUT_MS, DEFAULT_HEARTBEAT_MS, DEFAULT_SERVICE_WORKER_TIMEOUT, DuplexSiteBuilder, EXCHANGE_ABANDONED_MESSAGE, HANDLER_LOST_MESSAGE, HTTP_LINK_PROTOCOL, HandlerEndpoint, HandlerTable, HttpError, HttpParseError, LinkLostError, PEER_ERROR_HEADER, PortRendezvous, ROUTER_LOST_MESSAGE, ServerWebSocket, ServiceWorkerControlError, SwHttpAdapter, SwHttpDispatcher, SwPortDispatcher, SwPortHandler, TransportClosedError, WS_PORT_PROTOCOL_VERSION, WS_VERSION_HEADER, WsCloseEvent, answerConnect, awaitActiveServiceWorker, awaitServiceWorkerControl, callChannel, callChannelWithin, callHttpService, callWebSocketService, collect, collectBytes, collectString, connectHttpLink, connectWebSocketThroughServiceWorker, createWebSocketClass, decodeJsonl, decodeMessage, decodeText, defaultCodec, deserializeError, emulateMux, encodeJsonl, encodeMessage, encodeText, fetchOverDuplex, fromReadableStream, getRelayWindowMessageHandler, handleChannelCalls, handleClaimRequests, handleHttpRequests, handleStreams, holdLock, httpCodec, httpFetch, httpServe, initHttpService, initServiceWorker, initializeConnection, isWebSocketUpgrade, joinLines, jsonEnvelopeCodec, leastInflightRoute, locksAvailable, map, newAsyncGenerator, newCreditGrantor, newCreditLedger, newHttpClientStub, newHttpCodec, newHttpServerStub, newInvokationChannel, newLockName, newRegistry, newRemoteRelayChannel, newServiceWorkerPort, newSniffingCodec, newWebSocketUpgradeRequest, normalizeToUint8Array, recieveIterator, registerConnectionsHandler, relayWebSocketClass, requestedProtocols, sendHttpRequest, sendIterator, sendOverConnection, sendStream, serializeError, serveFetchOverDuplex, serveHttpInWorker, serveHttpLink, serveWebSocketUpgrade, serviceWorkerWebSocketClass, splitLines, splitServiceUrl, startHttpDispatcher, takePort, takeUpgrade, toChunks, toReadableStream, trackedLink, unwrapTransfer, upgradeWebSocket, watchLock, withHeaders, withTransfer };