@statewalker/webrun-rpc 0.5.1 → 0.8.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 CHANGED
@@ -62,7 +62,7 @@ Stream tier and adapter:
62
62
  | `serve(params: PortParams, handler)` | Registers a `Duplex` handler. Resolves an idempotent teardown that abandons live streams, then closes the mux. |
63
63
  | `PortParams` | `{ mux: PortMuxFactory; timeout?: number }`. |
64
64
  | `PortMuxFactory`, `OnPort` | `(onPort?: OnPort) => PortMux \| Promise<PortMux>`; `(port, meta?) => boolean \| undefined`, `false` rejects. |
65
- | `overPipe(pipe, options: OverPipeOptions)` | A factory over one pipe via `multiplexPort`. Options: `codec`, `side`, `maxPorts`, `maxMessageSize`. |
65
+ | `overPipe(pipe, options: OverPipeOptions)` | A factory over one pipe via `multiplexPort`. Options: `codec`, `side`, `maxPorts`, `maxMessageSize`, `signal`, `window`, `frameSize`. |
66
66
  | `overPorts(factory)` | Pass-through, for a mux this package knows nothing about. |
67
67
  | `duplexOverPort(port, options?)` | A `Duplex` running one stream on `port`. |
68
68
  | `serveDuplexOverPort(port, handler, options?)` | The serving half. Returns an idempotent teardown that abandons the stream and notifies the peer. |
@@ -75,8 +75,10 @@ Port multiplexing:
75
75
  | --- | --- |
76
76
  | `multiplexPort(port, options: PortMuxOptions)` | Emulated multiplexing over one port. |
77
77
  | `transferPortMux(target, options?: TransferPortMuxOptions)` | Multiplexing with real transferred `MessagePort`s. |
78
- | `PortMux` | `openPort(meta?)`, `close()`, `maxMessageSize`. |
79
- | `PortCodec`, `PortEnvelope` | The wire seam: `post(port, envelope, transfer?)`, `read(event)`. |
78
+ | `PortMux` | `openPort(meta?, { signal }?)`, `close(reason?)`, `ports?()`, `maxMessageSize`. |
79
+ | `portBufferedAmount(port)`, `whenPortReady(port, below?)` | A windowed port's queued bytes, and a promise for room. See below. |
80
+ | `PortInfo`, `OpenPortOptions` | What `ports()` reports per port (`id`, `meta`, `openedAt`, `lastActivity`); `openPort`'s options. |
81
+ | `PortCodec`, `PortEnvelope` | The wire seam: `post(port, envelope, transfer?)`, `read(event)`, and optionally `encode(envelope)` / `decode(data)` for byte codecs (exact window sizes, frames). |
80
82
  | `structuredCodec` | Envelopes passed through unencoded. |
81
83
  | `DEFAULT_MAX_PORTS` | `1024`. |
82
84
  | `PORT_TRANSFER` | `"webrun-rpc:port-transfer"`, the `type` of the message that carries a port. |
@@ -278,8 +280,9 @@ multiplexer's port, and the RPC tier runs over a virtual port with no byte-strea
278
280
 
279
281
  ### A port sends and receives messages, nothing more
280
282
 
281
- `multiplexPort` has no backpressure, acknowledgements, credit or buffer ceiling. That matches
282
- `MessagePort` semantics; waiting strategies belong above. The one safety property it holds: **a
283
+ Without `window`, `multiplexPort` has no backpressure, acknowledgements, credit or buffer ceiling.
284
+ That matches `MessagePort` semantics; waiting strategies belong above, or in the opt-in window
285
+ described below. The one safety property it always holds: **a
283
286
  message for a port with no consumer is dropped, never queued**, so a peer flooding an unaccepted
284
287
  port cannot grow memory here.
285
288
 
@@ -292,22 +295,238 @@ port cannot grow memory here.
292
295
  | `side` | `"initiator"` (default) allocates even ids, `"responder"` odd, so both ends can open concurrently with no negotiation. The two ends of one pipe must disagree. |
293
296
  | `maxPorts` | Ceiling on concurrently open ports, default `DEFAULT_MAX_PORTS` (1024). Closing a port frees its slot. It bounds the id table, never the total over time, and never delays a message. A peer `open` past the limit is answered with `close` reason `"max-ports"`. |
294
297
  | `maxMessageSize` | Largest payload a chunk may carry, if the transport has a limit. Reported to the stream tier, never enforced. See below. |
298
+ | `signal` | The transport's life. On abort the mux closes with `signal.reason`; an already-aborted signal gives a closed mux. See below. |
299
+ | `window` | Per-port flow control in bytes, off when unset. See "A port with a window holds only its own bytes" below. |
300
+ | `frameSize` | Fair framing in bytes, off when unset; needs `window`. See "Large messages take turns with small ones" below. |
301
+ | `maxReassembledBytes` | Ceiling on what the peer's framed messages hold past their ports' windows, summed over all ports, default `DEFAULT_MAX_REASSEMBLED_BYTES` (64 MiB). Past it, or past what a port's window leaves, the port closes. See "Large messages take turns with small ones" below. |
302
+ | `keepalive` | `{ intervalMs, misses? }`, off when unset: ping the peer and close the mux after `misses` unanswered pings from a peer that answered before. See "A port lives until its transport or its user lets go" below. |
303
+ | `onLeak` | Development aid, off when unset: called with a port's `PortInfo` when its consumer dropped it without `close()`, and the port is freed. See "A port lives until its transport or its user lets go" below. |
304
+
305
+ ### A port with a window holds only its own bytes
306
+
307
+ Without a window, a port has no backpressure: a slow consumer turns into unbounded memory, and
308
+ every other port waits behind the shared queue. With `window: n`, each port this mux opens asks
309
+ for flow control in its `open`, and the peer's mux applies it to that port in both directions.
310
+ Each side may then have at most `n` bytes of a port's messages delivered to the other side and
311
+ not yet consumed. What exceeds the window waits in the port's send queue:
312
+
313
+ - `portBufferedAmount(port)` returns the queued bytes;
314
+ - `whenPortReady(port, below = 0)` resolves once at most `below` bytes are queued.
315
+
316
+ A sender that must not queue without bound awaits `whenPortReady` before posting more. It always
317
+ resolves on a later task, never synchronously: a producer loop with room to spare would otherwise
318
+ never yield, and the transport's own events (the credit that refills the window, the socket's
319
+ writes) would never run.
320
+
321
+ A message counts as consumed once every listener has returned, or, for a listener that returns a
322
+ promise, once it settles. A consumer that takes messages from its own queue returns a promise
323
+ that settles when it takes them. **A listener whose promise never settles keeps its port's window
324
+ spent for good**: correct for backpressure, and visible in `mux.ports()`, whose `unconsumed`
325
+ stays at the window (with `queued` and `outstanding` on the sending side).
326
+
327
+ Credit goes back in batches: at once from a quarter of the window, else on the next task. 10,000
328
+ small messages and their echoes take fewer than 200 credit envelopes.
329
+
330
+ **What a message costs.** When both ends' codecs implement `encode` / `decode`, as `msgpackCodec`
331
+ does, the window counts the encoded bytes, envelope included, and the message is encoded once,
332
+ when it is posted. Each end says so in its `open` or its ack (`exact: true`); if either cannot,
333
+ both count an estimate computed from the same value: byte length for `ArrayBuffer`s and typed
334
+ arrays, a string's length, an object's values 4 levels deep and at most 256 of them, and at least
335
+ 64 bytes. Two ends counting different sizes would leak window on every message until the port
336
+ wedged. A message larger than the window is sent once nothing else is outstanding.
337
+
338
+ **Older peers.** A peer from before the window reads `window` in an `open` but never sends
339
+ credit, and a codec from before it drops `credit` envelopes. So neither end holds back until the
340
+ other has shown it reads credit: the responder acknowledges the window with a zero-byte `credit`
341
+ before `onPort` runs (so what `onPort` posts reaches the opener after it, counted the same way on
342
+ both ends), and the opener answers that ack with one of its own. Until then
343
+ each end counts bytes but holds nothing back, so a port to an older peer, or over an older codec,
344
+ keeps running without flow control, as it always did; `ports()` reports `window` only once it is
345
+ in force. A peer's `window` that is not a positive integer is ignored.
346
+
347
+ **Closing does not wait for credit.** A port closed with messages still queued sends them at once,
348
+ then its close, regardless of the window: a peer that stopped returning credit (a listener that
349
+ never settles) must still learn the port is closed, and nothing is kept for a port nobody holds.
350
+ A message that has to wait in the queue is copied when posted (`structuredClone`, or encoded at
351
+ once by a byte codec), as `postMessage` copies at once, so a caller reusing its buffer does not
352
+ change what is sent. `listenPort` counts a request as consumed when it reaches its handler, not
353
+ when the handler finishes, so a handler waiting on a later call on the same port does not hold
354
+ the window that call needs. The window is off by default: 256 KiB favours latency under load, 1-4 MiB favours bulk
355
+ throughput.
356
+
357
+ ### Large messages take turns with small ones
358
+
359
+ All ports share one pipe, and a message leaves whole. So without more, a port sending 1 MiB holds
360
+ the pipe for the whole megabyte, and a 64-byte message on another port waits behind it: the
361
+ window bounds how much is in flight, not the order on the wire. With `frameSize: n`:
362
+
363
+ - every windowed port's messages, and every port's `close`, wait in a per-port queue, and the
364
+ queues take turns, one item each, whenever the pipe has room (a port without a window posts its
365
+ messages at once, as without `frameSize`);
366
+ - a message whose encoding is larger than `n` is split into `frame` envelopes of `n` bytes, which
367
+ the peer reassembles before delivery.
368
+
369
+ A small message then waits for one frame, not for a whole large message. **It needs the pipe to
370
+ report when it is full**: `MessageTarget`'s optional `whenDrained()` (and `bufferedAmount`), which
371
+ `messageTargetOverStream` in `@statewalker/webrun-streams-libp2p` provides. A pipe without it,
372
+ such as a `MessagePort`, accepts everything at once, so the queues empty in one go and only the
373
+ frames of messages posted together interleave.
374
+
375
+ It also needs `window` and byte codecs (`encode` / `decode`) on both ends. A side frames only on a
376
+ port whose window is in force with exact sizes agreed (`exact: true` in the `open` and in its
377
+ acknowledgement), and accepts frames only there; a frame anywhere else is dropped. An older peer,
378
+ or one whose codec cannot decode, gets whole messages. If the pipe's `whenDrained()` rejects, the mux closes with that
379
+ error, so the ports waiting behind it fail instead of hanging. 16-64 KiB frames cost one small envelope each (about 15 bytes with
380
+ `msgpackCodec`).
381
+
382
+ **A peer cannot make this side hold frames without limit.** Two limits apply as frames arrive:
383
+
384
+ - a message begun while this side still owed the port credit (delivered or consumed bytes not yet
385
+ granted back) must fit what the window left, since an honest sender counts at least that much as
386
+ outstanding; one begun with nothing owed may be larger than the window, as a whole message may;
387
+ - what messages larger than the window hold past it, summed across every port, is at most
388
+ `maxReassembledBytes` (64 MiB by default), so opening many ports does not multiply it. A partial
389
+ message within its window is not charged: the window bounds it already, as it bounds unframed
390
+ traffic, so many ports framing honest messages at once never trip the cap.
391
+
392
+ A frame past either limit closes its port: the partial message is dropped, the port's close signal
393
+ aborts with a `RangeError` ("webrun-rpc: a framed message passed what the window left …" or
394
+ "… passed maxReassembledBytes …"), and the peer's port closes as on any peer close. On the wire
395
+ the close carries reason `"max-reassembled-bytes"`. A port that closes with messages still queued
396
+ sends them whole, not framed, so they never trip the window check on the way out.
397
+
398
+ The cap is the receiver's: a sender with `frameSize` that frames a message more than the
399
+ peer's `maxReassembledBytes` past the window has its port closed, where the same message sent whole would arrive.
400
+ Set it above the largest message the peer legitimately frames; a message just past it looks like
401
+ a closed port, not a lost message.
402
+
403
+ Frames carry no sequence number. Reassembly relies on what every `MessageTarget` already
404
+ promises: an ordered, reliable pipe. Each port's frames leave from its own queue in order, and
405
+ other ports' frames interleave but are told apart by port id.
295
406
 
296
407
  `openPort(meta?)` allocates a port, announces it and returns the local end without waiting for the
297
408
  peer to accept. That keeps layer 1 free of round trips: messages posted before a rejection are
298
409
  dropped at the far end and the port goes inert. It is asynchronous because a natively multiplexed
299
410
  transport cannot produce a port synchronously and both must share one shape. Its guard failures
300
411
  are rejections, not synchronous throws: `RangeError: webrun-rpc: maxPorts (<n>) reached` and
301
- `Error: webrun-rpc: the multiplexer is closed`. A duplicate `open` id from the peer is ignored
302
- rather than replacing a live port, and a local `openPort` skips ids the peer already claimed with
303
- the wrong parity.
304
-
305
- `PortEnvelope` is `{ type: "open", id, meta? }`, `{ type: "message", id, payload }` or
306
- `{ type: "close", id, reason? }`. `structuredCodec.read` accepts only objects with a non-negative
307
- integer `id` and one of those three types, so other traffic on a shared port is ignored.
412
+ `Error: webrun-rpc: the multiplexer is closed`. The `RangeError` goes on with the three commonest
413
+ `meta.kind`s among the open ports, `open ports by kind: http: 1000, ws: 24`, so a full table names
414
+ the consumer that filled it (`"?"` counts ports without a kind). A duplicate `open` id from the
415
+ peer is ignored rather than replacing a live port, and a local `openPort` skips ids the peer
416
+ already claimed with the wrong parity.
417
+
418
+ `PortEnvelope` is `{ type: "open", id, meta?, window?, exact? }`, `{ type: "message", id, payload }`,
419
+ `{ type: "close", id, reason? }`, with a window `{ type: "credit", id, bytes, exact? }`, with
420
+ `frameSize` `{ type: "frame", id, data, last? }`, and with `keepalive` `{ type: "ping", id }` /
421
+ `{ type: "pong", id }` (on `Number.MAX_SAFE_INTEGER`). The codecs' `read` accept only objects with a non-negative integer `id`
422
+ and one of those seven types, so other traffic on a shared port is
423
+ ignored.
308
424
  `structuredCodec` passes envelopes through unencoded, so `ArrayBuffer`s move zero-copy through the
309
425
  transfer list; it omits an empty transfer list because some implementations reject one.
310
426
 
427
+ ### A port lives until its transport or its user lets go
428
+
429
+ A port leaves the table only when one side closes it or the mux closes. Nothing else frees it, and
430
+ a full table refuses every new port. So both lifetimes are wired in explicitly:
431
+
432
+ - **The transport.** A `MessagePort`, a terminated worker or a dead libp2p stream gives no close
433
+ event, so the mux cannot notice by itself. Pass `signal` and abort it from whatever does notice
434
+ (a worker's `exit`, a stream's `close`, a liveness lock): the mux closes with `signal.reason`,
435
+ every port's close signal aborts with it, and calls in flight reject at once instead of hanging.
436
+ `close(reason)` does the same by hand. A port opened from a close-signal listener while the mux
437
+ closes is refused, and a transport `close()` that rejects on this path is swallowed rather than
438
+ left as an unhandled rejection.
439
+ - **The user.** `openPort(meta, { signal })` closes the port when `signal` aborts, with
440
+ `signal.reason` as the reason of the port's close signal; a signal already aborted rejects
441
+ `openPort` with its reason and opens nothing. `transferPortMux` honours the signal too. Virtual
442
+ ports are also disposable where the runtime defines `Symbol.dispose`, so
443
+ `using port = (await mux.openPort()) as MessageTarget & Disposable` closes it on scope exit. The
444
+ cast is needed because the declared type leaves `Symbol.dispose` out: naming it would require
445
+ every consumer to compile with the `esnext.disposable` lib.
446
+
447
+ What aborts `signal` depends on the transport. A Node worker reports its death:
448
+
449
+ ```ts
450
+ import { Worker } from "node:worker_threads";
451
+
452
+ const worker = new Worker(new URL("./worker.js", import.meta.url));
453
+ const { port1, port2 } = new MessageChannel();
454
+ worker.postMessage({ port: port2 }, [port2]);
455
+ const exited = new AbortController();
456
+ worker.once("exit", (code) => exited.abort(new Error(`the worker exited (${code})`)));
457
+ const mux = multiplexPort(port1, { codec: structuredCodec, signal: exited.signal });
458
+ ```
459
+
460
+ A browser realm (a tab, an iframe, a worker) reports nothing, but the browser releases its
461
+ [Web Locks](https://developer.mozilla.org/docs/Web/API/Web_Locks_API) when it ends. The other realm
462
+ holds a lock for its whole life, and this side asks for the same lock, which it gets only then:
463
+
464
+ ```ts
465
+ // In the other realm: a name of its own (realms of one origin share locks), and
466
+ // "ready" only from inside the callback, once the lock is really held.
467
+ const lock = `realm:${crypto.randomUUID()}`;
468
+ navigator.locks.request(lock, () => {
469
+ port.postMessage({ ready: lock });
470
+ return new Promise(() => {}); // held until this realm ends
471
+ });
472
+
473
+ // Here, on that message (`{ ready }`) — asked before the lock is held, it would be granted at once:
474
+ const gone = new AbortController();
475
+ navigator.locks.request(message.ready, () => gone.abort(new Error("the other realm is gone")));
476
+ const mux = multiplexPort(port, { codec: structuredCodec, signal: gone.signal });
477
+ ```
478
+
479
+ Locks are per browser profile, so this works between realms of one origin, never across a
480
+ network. In Node 24 a worker's locks are not visible to the thread that started it (the parent's
481
+ request is granted while the worker holds the lock), so use `exit` there.
482
+
483
+ **When nothing reports the death, `keepalive` probes for it.** With
484
+ `keepalive: { intervalMs: 5000, misses: 3 }` the mux pings every 5 s, the peer answers, and
485
+ anything the peer sends counts as an answer. After 3 pings in a row with nothing back from a peer
486
+ that has answered before, the mux closes with `webrun-rpc: the peer did not answer 3 keepalive
487
+ pings, 5000 ms apart`, as if `signal` had aborted. The silence it takes is between `misses ×
488
+ intervalMs` and one interval more. That catches a realm that died with no lock to tell and one
489
+ that froze. It cannot tell a peer whose event loop is blocked for that long from a dead one, so
490
+ size the product above the longest pause you expect.
491
+
492
+ - **What counts as silence.** Misses are pings unanswered, not time since the last message, so
493
+ timer jitter cannot turn a prompt answer into a miss, and `misses: 1` is usable. A tick taken
494
+ while this end's own pipe reports a backlog (`bufferedAmount > 0`, as `messageTargetOverStream`
495
+ does) is not counted: a ping stuck behind this end's own upload has not reached the peer.
496
+ - **Who is held to it.** Every current mux answers pings, whether or not it sets `keepalive`, so
497
+ one end is enough, and the two ends' intervals need not match. An older peer never answers and
498
+ is never closed for it. Nor is a peer that dies before its first answer: wire `signal` as well
499
+ where the transport can report death.
500
+ - **What it does not catch.** A peer that keeps sending but stopped consuming is alive by this
501
+ measure; the window (`window`) is what bounds that.
502
+ - **Wire.** `ping` and `pong` are envelopes of their own, sent on the largest safe integer id,
503
+ which no port takes. An older mux whose codec passes unknown envelopes through reads them as a
504
+ close of that id, and so closes nothing. The bundled codecs of older versions drop them.
505
+
506
+ The interval timer is `unref`'d in Node, so it never keeps a process alive. `intervalMs` must be in
507
+ (0, 2³¹ − 1] (past that, timers fire at once) and `misses` a positive integer, or `multiplexPort`
508
+ throws a `RangeError`.
509
+
510
+ `ports()` lists what is open, with each port's `meta`, `openedAt` and `lastActivity` (the last
511
+ message either way), for finding a consumer that never closes.
512
+
513
+ **A dropped port can be reported (`onLeak`).** Weak references cannot free leaks on their own:
514
+ the mux must deliver to every open port, and an abandoned port's listeners usually close over the
515
+ port, so it stays reachable from the table. What they can do is catch the ports nothing else
516
+ holds. The mux keeps only its own record of a port, never the consumer's object, so with
517
+ `onLeak` a port its consumer dropped without `close()` is reported once the collector reclaims it
518
+ (`onLeak(info)`, with its `meta`). What happens next depends on its listeners:
519
+
520
+ - **No listener left:** nothing can reach the port again, so it is freed on both ends as a close
521
+ would free it. Its close signal aborts with `webrun-rpc: the port was dropped without close()`;
522
+ the peer's port closes as on any close (`webrun-rpc: the peer closed the port`).
523
+ - **A listener left** (a subscription that keeps only its handler): the port is still receiving,
524
+ so it is reported and left open. Hold the port, or close it, to silence the report.
525
+
526
+ A port whose own listener refers to it is never collected, so never reported; when the collector
527
+ runs is up to the runtime. It is a development aid: each port costs a `FinalizationRegistry`
528
+ entry.
529
+
311
530
  ### No close is observable at layer 1
312
531
 
313
532
  `MessageTarget` has no close event and a virtual port exposes no state. When a port closes, its
@@ -395,6 +614,12 @@ caller without one does not.
395
614
  An abort unwinds a producing handler through `iter.return()`, not by throwing into it. A handler's
396
615
  `catch` never sees the abort reason; only `finally` runs. Put cleanup in `finally`.
397
616
 
617
+ `serve` forwards that `return()` to the handler's output at once, even while the handler is still
618
+ working on its first chunk, with or without `maxMessageSize` (whose chunker would queue it too). Its own tracking wrapper would otherwise queue it behind that pending
619
+ `next()`, as any async generator does. A handler that wraps its output to watch for `return()`
620
+ therefore sees the abandonment when it happens, not when it next yields:
621
+ `@statewalker/webrun-http-streams` aborts the served `Request.signal` that way.
622
+
398
623
  ### `transferPortMux` moves real ports
399
624
 
400
625
  Each `openPort` creates a `MessageChannel`, posts `{ type: PORT_TRANSFER, meta }` with one end in
@@ -1,6 +1,6 @@
1
1
  import type { Connect, Serve } from "@statewalker/webrun-streams";
2
2
  import type { MessageTarget } from "./message-target.js";
3
- import type { PortCodec, PortMux } from "./port-types.js";
3
+ import type { PortCodec, PortMux, PortMuxOptions } from "./port-types.js";
4
4
  /** What a mux calls when the peer opens a port. `false` rejects it. */
5
5
  export type OnPort = (port: MessageTarget, meta?: unknown) => boolean | undefined;
6
6
  /**
@@ -80,6 +80,21 @@ export interface OverPipeOptions {
80
80
  * transport's real ceiling — see `PortMuxOptions.maxMessageSize`.
81
81
  */
82
82
  maxMessageSize?: number;
83
+ /** The pipe's life: aborting it closes the mux. See `PortMuxOptions.signal`. */
84
+ signal?: AbortSignal;
85
+ /** Experimental per-port flow control in bytes — see `PortMuxOptions.window`. */
86
+ window?: number;
87
+ /** Fair framing in bytes — see `PortMuxOptions.frameSize`. */
88
+ frameSize?: number;
89
+ /** Ceiling on a message reassembled from frames — see `PortMuxOptions.maxReassembledBytes`. */
90
+ maxReassembledBytes?: number;
91
+ /** Liveness of the peer by ping/pong — see `PortMuxOptions.keepalive`. */
92
+ keepalive?: {
93
+ intervalMs: number;
94
+ misses?: number;
95
+ };
96
+ /** Development aid: report ports dropped without `close()` — see `PortMuxOptions.onLeak`. */
97
+ onLeak?: PortMuxOptions["onLeak"];
83
98
  }
84
99
  /**
85
100
  * Ports over ONE PIPE OF BYTES — a `MessagePort`, a worker, a WebSocket.
@@ -1 +1 @@
1
- {"version":3,"file":"connect-serve.d.ts","sourceRoot":"","sources":["../src/connect-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAU,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAM1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAW1D,uEAAuE;AACvE,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC;AAElF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE7E,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;OAUG;IACH,GAAG,EAAE,cAAc,CAAC;IACpB;;;yEAGqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,OAAO,CAAC,UAAU,CAyCvC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,UAAU,CAqDnC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,KAAK,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAEtF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAEjE"}
1
+ {"version":3,"file":"connect-serve.d.ts","sourceRoot":"","sources":["../src/connect-serve.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAU,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAM1E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAW1E,uEAAuE;AACvE,MAAM,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,aAAa,EAAE,IAAI,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,SAAS,CAAC;AAElF;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;AAE7E,MAAM,WAAW,UAAU;IACzB;;;;;;;;;;OAUG;IACH,GAAG,EAAE,cAAc,CAAC;IACpB;;;yEAGqE;IACrE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,OAAO,CAAC,UAAU,CAyCvC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,KAAK,EAAE,KAAK,CAAC,UAAU,CA8DnC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,mFAAmF;IACnF,KAAK,EAAE,SAAS,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,WAAW,GAAG,WAAW,CAAC;IACjC,oEAAoE;IACpE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8DAA8D;IAC9D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+FAA+F;IAC/F,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,SAAS,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,6FAA6F;IAC7F,MAAM,CAAC,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;CACnC;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAEtF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CAEjE"}
package/dist/index.d.ts CHANGED
@@ -10,8 +10,9 @@ export * from "./io-send.js";
10
10
  export * from "./listen-bidi.js";
11
11
  export * from "./listen-port.js";
12
12
  export * from "./message-target.js";
13
- export { DEFAULT_MAX_PORTS, MUX_CLOSED_MESSAGE, multiplexPort } from "./multiplex-port.js";
14
- export type { PortCodec, PortEnvelope, PortMux, PortMuxOptions } from "./port-types.js";
13
+ export { DEFAULT_MAX_PORTS, DEFAULT_MAX_REASSEMBLED_BYTES, MUX_CLOSED_MESSAGE, multiplexPort, } from "./multiplex-port.js";
14
+ export { type PortSendState, portBufferedAmount, whenPortReady } from "./port-credit.js";
15
+ export type { OpenPortOptions, PortCodec, PortEnvelope, PortInfo, PortMux, PortMuxOptions, } from "./port-types.js";
15
16
  export * from "./recieve.js";
16
17
  export * from "./send.js";
17
18
  export { structuredCodec } from "./structured-codec.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,OAAO,EACP,KAAK,MAAM,EACX,KAAK,eAAe,EACpB,QAAQ,EACR,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,GACN,MAAM,oBAAoB,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAC3F,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACxF,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,0BAA0B,EAAE,MAAM,mBAAmB,CAAC;AAC/D,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC3E,OAAO,EACL,OAAO,EACP,KAAK,MAAM,EACX,KAAK,eAAe,EACpB,QAAQ,EACR,SAAS,EACT,KAAK,cAAc,EACnB,KAAK,UAAU,EACf,KAAK,GACN,MAAM,oBAAoB,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,qBAAqB,CAAC;AACpC,OAAO,EACL,iBAAiB,EACjB,6BAA6B,EAC7B,kBAAkB,EAClB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,KAAK,aAAa,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACzF,YAAY,EACV,eAAe,EACf,SAAS,EACT,YAAY,EACZ,QAAQ,EACR,OAAO,EACP,cAAc,GACf,MAAM,iBAAiB,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,cAAc,wBAAwB,CAAC"}