@statewalker/webrun-rpc 0.5.0 → 0.7.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 +206 -12
- package/dist/async-dispose.d.ts +10 -0
- package/dist/async-dispose.d.ts.map +1 -0
- package/dist/connect-serve.d.ts +14 -1
- package/dist/connect-serve.d.ts.map +1 -1
- package/dist/duplex-over-port.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +626 -60
- package/dist/listen-port.d.ts.map +1 -1
- package/dist/message-target.d.ts +14 -1
- package/dist/message-target.d.ts.map +1 -1
- package/dist/multiplex-port.d.ts +11 -2
- package/dist/multiplex-port.d.ts.map +1 -1
- package/dist/port-credit.d.ts +27 -0
- package/dist/port-credit.d.ts.map +1 -0
- package/dist/port-types.d.ts +140 -6
- package/dist/port-types.d.ts.map +1 -1
- package/dist/return-at-once.d.ts +17 -0
- package/dist/return-at-once.d.ts.map +1 -0
- package/dist/structured-codec.d.ts.map +1 -1
- package/dist/transfer-port-mux.d.ts.map +1 -1
- package/dist/virtual-port.d.ts +8 -2
- package/dist/virtual-port.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/async-dispose.ts +22 -0
- package/src/close-signal.ts +1 -1
- package/src/connect-serve.ts +30 -4
- package/src/duplex-over-port.ts +9 -1
- package/src/index.ts +9 -1
- package/src/listen-port.ts +9 -2
- package/src/message-target.ts +14 -1
- package/src/multiplex-port.ts +610 -33
- package/src/port-credit.ts +61 -0
- package/src/port-types.ts +124 -8
- package/src/return-at-once.ts +51 -0
- package/src/structured-codec.ts +9 -1
- package/src/transfer-port-mux.ts +32 -8
- package/src/virtual-port.ts +41 -11
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
|
-
| `
|
|
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.
|
|
282
|
-
`MessagePort` semantics; waiting strategies belong above
|
|
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,213 @@ 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
|
+
| `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. |
|
|
302
|
+
| `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. |
|
|
303
|
+
|
|
304
|
+
### A port with a window holds only its own bytes
|
|
305
|
+
|
|
306
|
+
Without a window, a port has no backpressure: a slow consumer turns into unbounded memory, and
|
|
307
|
+
every other port waits behind the shared queue. With `window: n`, each port this mux opens asks
|
|
308
|
+
for flow control in its `open`, and the peer's mux applies it to that port in both directions.
|
|
309
|
+
Each side may then have at most `n` bytes of a port's messages delivered to the other side and
|
|
310
|
+
not yet consumed. What exceeds the window waits in the port's send queue:
|
|
311
|
+
|
|
312
|
+
- `portBufferedAmount(port)` returns the queued bytes;
|
|
313
|
+
- `whenPortReady(port, below = 0)` resolves once at most `below` bytes are queued.
|
|
314
|
+
|
|
315
|
+
A sender that must not queue without bound awaits `whenPortReady` before posting more. It always
|
|
316
|
+
resolves on a later task, never synchronously: a producer loop with room to spare would otherwise
|
|
317
|
+
never yield, and the transport's own events (the credit that refills the window, the socket's
|
|
318
|
+
writes) would never run.
|
|
319
|
+
|
|
320
|
+
A message counts as consumed once every listener has returned, or, for a listener that returns a
|
|
321
|
+
promise, once it settles. A consumer that takes messages from its own queue returns a promise
|
|
322
|
+
that settles when it takes them. **A listener whose promise never settles keeps its port's window
|
|
323
|
+
spent for good**: correct for backpressure, and visible in `mux.ports()`, whose `unconsumed`
|
|
324
|
+
stays at the window (with `queued` and `outstanding` on the sending side).
|
|
325
|
+
|
|
326
|
+
Credit goes back in batches: at once from a quarter of the window, else on the next task. 10,000
|
|
327
|
+
small messages and their echoes take fewer than 200 credit envelopes.
|
|
328
|
+
|
|
329
|
+
**What a message costs.** When both ends' codecs implement `encode` / `decode`, as `msgpackCodec`
|
|
330
|
+
does, the window counts the encoded bytes, envelope included, and the message is encoded once,
|
|
331
|
+
when it is posted. Each end says so in its `open` or its ack (`exact: true`); if either cannot,
|
|
332
|
+
both count an estimate computed from the same value: byte length for `ArrayBuffer`s and typed
|
|
333
|
+
arrays, a string's length, an object's values 4 levels deep and at most 256 of them, and at least
|
|
334
|
+
64 bytes. Two ends counting different sizes would leak window on every message until the port
|
|
335
|
+
wedged. A message larger than the window is sent once nothing else is outstanding.
|
|
336
|
+
|
|
337
|
+
**Older peers.** A peer from before the window reads `window` in an `open` but never sends
|
|
338
|
+
credit, and a codec from before it drops `credit` envelopes. So neither end holds back until the
|
|
339
|
+
other has shown it reads credit: the responder acknowledges the window with a zero-byte `credit`
|
|
340
|
+
before `onPort` runs (so what `onPort` posts reaches the opener after it, counted the same way on
|
|
341
|
+
both ends), and the opener answers that ack with one of its own. Until then
|
|
342
|
+
each end counts bytes but holds nothing back, so a port to an older peer, or over an older codec,
|
|
343
|
+
keeps running without flow control, as it always did; `ports()` reports `window` only once it is
|
|
344
|
+
in force. A peer's `window` that is not a positive integer is ignored.
|
|
345
|
+
|
|
346
|
+
**Closing does not wait for credit.** A port closed with messages still queued sends them at once,
|
|
347
|
+
then its close, regardless of the window: a peer that stopped returning credit (a listener that
|
|
348
|
+
never settles) must still learn the port is closed, and nothing is kept for a port nobody holds.
|
|
349
|
+
A message that has to wait in the queue is copied when posted (`structuredClone`, or encoded at
|
|
350
|
+
once by a byte codec), as `postMessage` copies at once, so a caller reusing its buffer does not
|
|
351
|
+
change what is sent. `listenPort` counts a request as consumed when it reaches its handler, not
|
|
352
|
+
when the handler finishes, so a handler waiting on a later call on the same port does not hold
|
|
353
|
+
the window that call needs. The window is off by default: 256 KiB favours latency under load, 1-4 MiB favours bulk
|
|
354
|
+
throughput.
|
|
355
|
+
|
|
356
|
+
### Large messages take turns with small ones
|
|
357
|
+
|
|
358
|
+
All ports share one pipe, and a message leaves whole. So without more, a port sending 1 MiB holds
|
|
359
|
+
the pipe for the whole megabyte, and a 64-byte message on another port waits behind it: the
|
|
360
|
+
window bounds how much is in flight, not the order on the wire. With `frameSize: n`:
|
|
361
|
+
|
|
362
|
+
- every windowed port's messages, and every port's `close`, wait in a per-port queue, and the
|
|
363
|
+
queues take turns, one item each, whenever the pipe has room (a port without a window posts its
|
|
364
|
+
messages at once, as without `frameSize`);
|
|
365
|
+
- a message whose encoding is larger than `n` is split into `frame` envelopes of `n` bytes, which
|
|
366
|
+
the peer reassembles before delivery.
|
|
367
|
+
|
|
368
|
+
A small message then waits for one frame, not for a whole large message. **It needs the pipe to
|
|
369
|
+
report when it is full**: `MessageTarget`'s optional `whenDrained()` (and `bufferedAmount`), which
|
|
370
|
+
`messageTargetOverStream` in `@statewalker/webrun-streams-libp2p` provides. A pipe without it,
|
|
371
|
+
such as a `MessagePort`, accepts everything at once, so the queues empty in one go and only the
|
|
372
|
+
frames of messages posted together interleave.
|
|
373
|
+
|
|
374
|
+
It also needs `window` and byte codecs (`encode` / `decode`) on both ends. A side frames only on a
|
|
375
|
+
port whose window is in force with exact sizes agreed (`exact: true` in the `open` and in its
|
|
376
|
+
acknowledgement), and accepts frames only there; a frame anywhere else is dropped. An older peer,
|
|
377
|
+
or one whose codec cannot decode, gets whole messages. A frame sequence buffers no more than the
|
|
378
|
+
same message sent whole would. 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`).
|
|
295
381
|
|
|
296
382
|
`openPort(meta?)` allocates a port, announces it and returns the local end without waiting for the
|
|
297
383
|
peer to accept. That keeps layer 1 free of round trips: messages posted before a rejection are
|
|
298
384
|
dropped at the far end and the port goes inert. It is asynchronous because a natively multiplexed
|
|
299
385
|
transport cannot produce a port synchronously and both must share one shape. Its guard failures
|
|
300
386
|
are rejections, not synchronous throws: `RangeError: webrun-rpc: maxPorts (<n>) reached` and
|
|
301
|
-
`Error: webrun-rpc: the multiplexer is closed`.
|
|
302
|
-
|
|
303
|
-
the
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
387
|
+
`Error: webrun-rpc: the multiplexer is closed`. The `RangeError` goes on with the three commonest
|
|
388
|
+
`meta.kind`s among the open ports, `open ports by kind: http: 1000, ws: 24`, so a full table names
|
|
389
|
+
the consumer that filled it (`"?"` counts ports without a kind). A duplicate `open` id from the
|
|
390
|
+
peer is ignored rather than replacing a live port, and a local `openPort` skips ids the peer
|
|
391
|
+
already claimed with the wrong parity.
|
|
392
|
+
|
|
393
|
+
`PortEnvelope` is `{ type: "open", id, meta?, window?, exact? }`, `{ type: "message", id, payload }`,
|
|
394
|
+
`{ type: "close", id, reason? }`, with a window `{ type: "credit", id, bytes, exact? }`, with
|
|
395
|
+
`frameSize` `{ type: "frame", id, data, last? }`, and with `keepalive` `{ type: "ping", id }` /
|
|
396
|
+
`{ type: "pong", id }` (on `Number.MAX_SAFE_INTEGER`). The codecs' `read` accept only objects with a non-negative integer `id`
|
|
397
|
+
and one of those seven types, so other traffic on a shared port is
|
|
398
|
+
ignored.
|
|
308
399
|
`structuredCodec` passes envelopes through unencoded, so `ArrayBuffer`s move zero-copy through the
|
|
309
400
|
transfer list; it omits an empty transfer list because some implementations reject one.
|
|
310
401
|
|
|
402
|
+
### A port lives until its transport or its user lets go
|
|
403
|
+
|
|
404
|
+
A port leaves the table only when one side closes it or the mux closes. Nothing else frees it, and
|
|
405
|
+
a full table refuses every new port. So both lifetimes are wired in explicitly:
|
|
406
|
+
|
|
407
|
+
- **The transport.** A `MessagePort`, a terminated worker or a dead libp2p stream gives no close
|
|
408
|
+
event, so the mux cannot notice by itself. Pass `signal` and abort it from whatever does notice
|
|
409
|
+
(a worker's `exit`, a stream's `close`, a liveness lock): the mux closes with `signal.reason`,
|
|
410
|
+
every port's close signal aborts with it, and calls in flight reject at once instead of hanging.
|
|
411
|
+
`close(reason)` does the same by hand. A port opened from a close-signal listener while the mux
|
|
412
|
+
closes is refused, and a transport `close()` that rejects on this path is swallowed rather than
|
|
413
|
+
left as an unhandled rejection.
|
|
414
|
+
- **The user.** `openPort(meta, { signal })` closes the port when `signal` aborts, with
|
|
415
|
+
`signal.reason` as the reason of the port's close signal; a signal already aborted rejects
|
|
416
|
+
`openPort` with its reason and opens nothing. `transferPortMux` honours the signal too. Virtual
|
|
417
|
+
ports are also disposable where the runtime defines `Symbol.dispose`, so
|
|
418
|
+
`using port = (await mux.openPort()) as MessageTarget & Disposable` closes it on scope exit. The
|
|
419
|
+
cast is needed because the declared type leaves `Symbol.dispose` out: naming it would require
|
|
420
|
+
every consumer to compile with the `esnext.disposable` lib.
|
|
421
|
+
|
|
422
|
+
What aborts `signal` depends on the transport. A Node worker reports its death:
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
import { Worker } from "node:worker_threads";
|
|
426
|
+
|
|
427
|
+
const worker = new Worker(new URL("./worker.js", import.meta.url));
|
|
428
|
+
const { port1, port2 } = new MessageChannel();
|
|
429
|
+
worker.postMessage({ port: port2 }, [port2]);
|
|
430
|
+
const exited = new AbortController();
|
|
431
|
+
worker.once("exit", (code) => exited.abort(new Error(`the worker exited (${code})`)));
|
|
432
|
+
const mux = multiplexPort(port1, { codec: structuredCodec, signal: exited.signal });
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
A browser realm (a tab, an iframe, a worker) reports nothing, but the browser releases its
|
|
436
|
+
[Web Locks](https://developer.mozilla.org/docs/Web/API/Web_Locks_API) when it ends. The other realm
|
|
437
|
+
holds a lock for its whole life, and this side asks for the same lock, which it gets only then:
|
|
438
|
+
|
|
439
|
+
```ts
|
|
440
|
+
// In the other realm: a name of its own (realms of one origin share locks), and
|
|
441
|
+
// "ready" only from inside the callback, once the lock is really held.
|
|
442
|
+
const lock = `realm:${crypto.randomUUID()}`;
|
|
443
|
+
navigator.locks.request(lock, () => {
|
|
444
|
+
port.postMessage({ ready: lock });
|
|
445
|
+
return new Promise(() => {}); // held until this realm ends
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Here, on that message (`{ ready }`) — asked before the lock is held, it would be granted at once:
|
|
449
|
+
const gone = new AbortController();
|
|
450
|
+
navigator.locks.request(message.ready, () => gone.abort(new Error("the other realm is gone")));
|
|
451
|
+
const mux = multiplexPort(port, { codec: structuredCodec, signal: gone.signal });
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
Locks are per browser profile, so this works between realms of one origin, never across a
|
|
455
|
+
network. In Node 24 a worker's locks are not visible to the thread that started it (the parent's
|
|
456
|
+
request is granted while the worker holds the lock), so use `exit` there.
|
|
457
|
+
|
|
458
|
+
**When nothing reports the death, `keepalive` probes for it.** With
|
|
459
|
+
`keepalive: { intervalMs: 5000, misses: 3 }` the mux pings every 5 s, the peer answers, and
|
|
460
|
+
anything the peer sends counts as an answer. After 3 pings in a row with nothing back from a peer
|
|
461
|
+
that has answered before, the mux closes with `webrun-rpc: the peer did not answer 3 keepalive
|
|
462
|
+
pings, 5000 ms apart`, as if `signal` had aborted. The silence it takes is between `misses ×
|
|
463
|
+
intervalMs` and one interval more. That catches a realm that died with no lock to tell and one
|
|
464
|
+
that froze. It cannot tell a peer whose event loop is blocked for that long from a dead one, so
|
|
465
|
+
size the product above the longest pause you expect.
|
|
466
|
+
|
|
467
|
+
- **What counts as silence.** Misses are pings unanswered, not time since the last message, so
|
|
468
|
+
timer jitter cannot turn a prompt answer into a miss, and `misses: 1` is usable. A tick taken
|
|
469
|
+
while this end's own pipe reports a backlog (`bufferedAmount > 0`, as `messageTargetOverStream`
|
|
470
|
+
does) is not counted: a ping stuck behind this end's own upload has not reached the peer.
|
|
471
|
+
- **Who is held to it.** Every current mux answers pings, whether or not it sets `keepalive`, so
|
|
472
|
+
one end is enough, and the two ends' intervals need not match. An older peer never answers and
|
|
473
|
+
is never closed for it. Nor is a peer that dies before its first answer: wire `signal` as well
|
|
474
|
+
where the transport can report death.
|
|
475
|
+
- **What it does not catch.** A peer that keeps sending but stopped consuming is alive by this
|
|
476
|
+
measure; the window (`window`) is what bounds that.
|
|
477
|
+
- **Wire.** `ping` and `pong` are envelopes of their own, sent on the largest safe integer id,
|
|
478
|
+
which no port takes. An older mux whose codec passes unknown envelopes through reads them as a
|
|
479
|
+
close of that id, and so closes nothing. The bundled codecs of older versions drop them.
|
|
480
|
+
|
|
481
|
+
The interval timer is `unref`'d in Node, so it never keeps a process alive. `intervalMs` must be in
|
|
482
|
+
(0, 2³¹ − 1] (past that, timers fire at once) and `misses` a positive integer, or `multiplexPort`
|
|
483
|
+
throws a `RangeError`.
|
|
484
|
+
|
|
485
|
+
`ports()` lists what is open, with each port's `meta`, `openedAt` and `lastActivity` (the last
|
|
486
|
+
message either way), for finding a consumer that never closes.
|
|
487
|
+
|
|
488
|
+
**A dropped port can be reported (`onLeak`).** Weak references cannot free leaks on their own:
|
|
489
|
+
the mux must deliver to every open port, and an abandoned port's listeners usually close over the
|
|
490
|
+
port, so it stays reachable from the table. What they can do is catch the ports nothing else
|
|
491
|
+
holds. The mux keeps only its own record of a port, never the consumer's object, so with
|
|
492
|
+
`onLeak` a port its consumer dropped without `close()` is reported once the collector reclaims it
|
|
493
|
+
(`onLeak(info)`, with its `meta`). What happens next depends on its listeners:
|
|
494
|
+
|
|
495
|
+
- **No listener left:** nothing can reach the port again, so it is freed on both ends as a close
|
|
496
|
+
would free it. Its close signal aborts with `webrun-rpc: the port was dropped without close()`;
|
|
497
|
+
the peer's port closes as on any close (`webrun-rpc: the peer closed the port`).
|
|
498
|
+
- **A listener left** (a subscription that keeps only its handler): the port is still receiving,
|
|
499
|
+
so it is reported and left open. Hold the port, or close it, to silence the report.
|
|
500
|
+
|
|
501
|
+
A port whose own listener refers to it is never collected, so never reported; when the collector
|
|
502
|
+
runs is up to the runtime. It is a development aid: each port costs a `FinalizationRegistry`
|
|
503
|
+
entry.
|
|
504
|
+
|
|
311
505
|
### No close is observable at layer 1
|
|
312
506
|
|
|
313
507
|
`MessageTarget` has no close event and a virtual port exposes no state. When a port closes, its
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Makes `target` disposable with `await using`: installs `dispose` as its
|
|
3
|
+
* `[Symbol.asyncDispose]`, where the runtime defines the symbol. Hand-written
|
|
4
|
+
* iterators lose what a native generator gets from `AsyncIteratorPrototype`;
|
|
5
|
+
* this gives it back. A no-op where the symbol is missing. Kept off the
|
|
6
|
+
* declared types, which would require consumers to compile with the
|
|
7
|
+
* `esnext.disposable` lib.
|
|
8
|
+
*/
|
|
9
|
+
export declare function asyncDisposable<T extends object>(target: T, dispose: () => Promise<unknown>): T;
|
|
10
|
+
//# sourceMappingURL=async-dispose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"async-dispose.d.ts","sourceRoot":"","sources":["../src/async-dispose.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAa/F"}
|
package/dist/connect-serve.d.ts
CHANGED
|
@@ -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,19 @@ 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
|
+
/** Liveness of the peer by ping/pong — see `PortMuxOptions.keepalive`. */
|
|
90
|
+
keepalive?: {
|
|
91
|
+
intervalMs: number;
|
|
92
|
+
misses?: number;
|
|
93
|
+
};
|
|
94
|
+
/** Development aid: report ports dropped without `close()` — see `PortMuxOptions.onLeak`. */
|
|
95
|
+
onLeak?: PortMuxOptions["onLeak"];
|
|
83
96
|
}
|
|
84
97
|
/**
|
|
85
98
|
* 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;
|
|
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,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;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,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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duplex-over-port.d.ts","sourceRoot":"","sources":["../src/duplex-over-port.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAOZ,MAAM,6BAA6B,CAAC;AAIrC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"duplex-over-port.d.ts","sourceRoot":"","sources":["../src/duplex-over-port.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,MAAM,EAOZ,MAAM,6BAA6B,CAAC;AAIrC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAIzD;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,4BAA4B,CAAC;AActD,MAAM,WAAW,qBAAqB;IACpC;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6CAA6C;IAC7C,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CACpC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,aAAa,EAAE,OAAO,GAAE,qBAA0B,GAAG,MAAM,CAE/F;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,aAAa,EACnB,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,qBAA0B,GAClC,MAAM,IAAI,CA8BZ"}
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,8 @@ export * from "./listen-bidi.js";
|
|
|
11
11
|
export * from "./listen-port.js";
|
|
12
12
|
export * from "./message-target.js";
|
|
13
13
|
export { DEFAULT_MAX_PORTS, MUX_CLOSED_MESSAGE, multiplexPort } from "./multiplex-port.js";
|
|
14
|
-
export
|
|
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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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,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"}
|