@urun-sh/core 0.3.4 → 0.3.6

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/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - **BREAKING: `WtMicUplinkHandle` and `WtCamUplinkHandle` require
6
+ `deliveredDatagrams(): number`; `WtUplinkSink` requires `delivered(): number`.**
7
+ A consumer that supplies only `setSource()` (and `requestKeyframe()` for the
8
+ camera) to `TransportSession.wtMicUplinkSeam` / `wtCamUplinkSeam` no longer
9
+ type-checks. Implementors report the datagrams QUIC has ACCEPTED from that
10
+ lane. There is deliberately no optional-with-fallback form: a lane that
11
+ cannot answer "did anything actually leave?" is the bug being removed
12
+ (urun-sh/urun-infra#2053 — `isProducingMedia` claimed a live microphone from
13
+ handle presence plus `MediaStreamTrack.readyState`, so the SDK reported a
14
+ healthy mic through an entire session in which nothing was transmitted).
15
+ Migration: return a per-lane counter incremented when that lane's datagram
16
+ write resolves. `WtUplinkSink` is internal — it is not exposed by the package
17
+ entrypoints — so only the two handle types affect external implementors.
18
+
19
+ - **Media liveness is level-triggered, and a sender sequence RESTART resyncs
20
+ instead of wedging the leg forever** (urun-sh/urun-infra#2053). Both media
21
+ lanes kept a monotonic high-water mark with no RFC 3550 §A.1 restart rule, so
22
+ a sender that restarted its sequence space was discarded for the life of the
23
+ session — silently, and on video before the keyframe bit was even read. The
24
+ decode-stall watchdog is no longer one-shot, uplink datagram write rejections
25
+ are no longer swallowed, and publication requires delivery evidence.
26
+
5
27
  - **WT uplink lanes now share ONE datagram writer (`getWriter` lock).**
6
28
  `WritableStream.getWriter()` LOCKS the stream — the WHATWG contract is one
7
29
  writer at a time. `WebTransportSession.start()` built its uplink sink by
@@ -16,6 +38,37 @@
16
38
  `releaseLock()`-ed on stop/death. The pre-existing fakes handed back a fresh
17
39
  writer on every call, hiding the double-acquire; the new
18
40
  `wt-uplink-writer-lock.test.ts` fake implements the real lock contract.
41
+ - **A dead WT-only leg now LOUD-FAILS instead of silently "failing over" to a
42
+ transport that was never dialled.** On a fully-granted leg the negotiator
43
+ calls `webRtcDial.skip()` — no `RTCPeerConnection`, no mediasoup Device, no
44
+ server media transports. When QUIC then died mid-session, `armFailover`
45
+ re-drove the deferred dial fire-and-forget and immediately awaited
46
+ `startWebRtc()`, whose `WebRtcSession.start()` awaits a `track` event that a
47
+ never-dialled leg may never receive. The await never settled: `session` was
48
+ never reassigned (`activeTransport` kept reporting the DEAD `'webtransport'`),
49
+ `transport.established` was never notified, `onTransportChange` never fired,
50
+ and NOTHING reached the app — a black pane behind a misleading
51
+ `quic died mid-session, failing over to webrtc` line (qwen-avatar
52
+ 2026-09-01). The failover is now bounded by `webRtcFailoverDeadlineMs`
53
+ (default 10s); on expiry the negotiator drops the stale session
54
+ (`activeTransport === null`), skips the false `transport.established(webrtc)`
55
+ notify, logs a specific `media transport lost — …` error naming the WT-only /
56
+ no-RTCPeerConnection condition, and surfaces it through the new
57
+ `onMediaTransportLost` seam (plumbed through `negotiateDialTransport`, which
58
+ also clears the stale track overrides and arms the WT retries). The warn line
59
+ itself no longer claims a WebRTC leg exists on a WT-only leg. The abandoned
60
+ WebRTC session is retained synchronously and stopped DIRECTLY on expiry (a
61
+ cleanup chained off the abandoned `start()` promise never runs in exactly the
62
+ never-settles case the deadline exists for, leaking the peer connection for
63
+ the life of the page), and both the adopt and the give-up paths are guarded
64
+ against a `disconnect()` — or a background WT re-upgrade — that landed while
65
+ `start()` was in flight.
66
+ - **A mid-session WT death no longer masquerades as a failed negotiation.**
67
+ `engage()`'s `await media.firstMedia` rejection escaped to the connect-path
68
+ catch, so a death minutes after a SUCCESSFUL dial wore the
69
+ `transport negotiation unavailable, staying on webrtc` line and re-drove the
70
+ deferred WebRTC dial a second time. It is now caught where it belongs; the
71
+ negotiator's failover path owns the death.
19
72
 
20
73
  ## 0.3.0
21
74