@norskvideo/moq-net 0.1.2 → 0.1.4
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 +1 -0
- package/announced.d.ts +111 -21
- package/announced.d.ts.map +1 -1
- package/announced.js +213 -40
- package/announced.js.map +1 -1
- package/broadcast.d.ts +80 -25
- package/broadcast.d.ts.map +1 -1
- package/broadcast.js +270 -49
- package/broadcast.js.map +1 -1
- package/connection/accept.d.ts +6 -0
- package/connection/accept.d.ts.map +1 -1
- package/connection/accept.js +42 -13
- package/connection/accept.js.map +1 -1
- package/connection/browser.d.ts +5 -0
- package/connection/browser.d.ts.map +1 -0
- package/connection/browser.js +31 -0
- package/connection/browser.js.map +1 -0
- package/connection/connect.d.ts +43 -1
- package/connection/connect.d.ts.map +1 -1
- package/connection/connect.js +118 -123
- package/connection/connect.js.map +1 -1
- package/connection/established.d.ts +43 -16
- package/connection/established.d.ts.map +1 -1
- package/connection/established.js.map +1 -1
- package/connection/handshake.d.ts +12 -1
- package/connection/handshake.d.ts.map +1 -1
- package/connection/handshake.js +30 -14
- package/connection/handshake.js.map +1 -1
- package/connection/index.d.ts +8 -0
- package/connection/index.d.ts.map +1 -1
- package/connection/index.js +6 -0
- package/connection/index.js.map +1 -1
- package/connection/reload.d.ts +66 -9
- package/connection/reload.d.ts.map +1 -1
- package/connection/reload.js +192 -66
- package/connection/reload.js.map +1 -1
- package/connection/stats.d.ts +92 -0
- package/connection/stats.d.ts.map +1 -0
- package/connection/stats.js +35 -0
- package/connection/stats.js.map +1 -0
- package/connection/transport.d.ts +10 -0
- package/connection/transport.d.ts.map +1 -0
- package/connection/transport.js +12 -0
- package/connection/transport.js.map +1 -0
- package/consume.d.ts +44 -0
- package/consume.d.ts.map +1 -0
- package/consume.js +60 -0
- package/consume.js.map +1 -0
- package/datagram.d.ts +23 -0
- package/datagram.d.ts.map +1 -0
- package/datagram.js +3 -0
- package/datagram.js.map +1 -0
- package/error.d.ts +76 -0
- package/error.d.ts.map +1 -0
- package/error.js +107 -0
- package/error.js.map +1 -0
- package/group.d.ts +143 -38
- package/group.d.ts.map +1 -1
- package/group.js +270 -68
- package/group.js.map +1 -1
- package/ietf/adapter.d.ts +2 -2
- package/ietf/adapter.d.ts.map +1 -1
- package/ietf/adapter.js +20 -10
- package/ietf/adapter.js.map +1 -1
- package/ietf/aliases.d.ts +73 -5
- package/ietf/aliases.d.ts.map +1 -1
- package/ietf/aliases.js +121 -14
- package/ietf/aliases.js.map +1 -1
- package/ietf/cluster.d.ts +141 -0
- package/ietf/cluster.d.ts.map +1 -0
- package/ietf/cluster.js +204 -0
- package/ietf/cluster.js.map +1 -0
- package/ietf/connection.d.ts +39 -6
- package/ietf/connection.d.ts.map +1 -1
- package/ietf/connection.js +73 -12
- package/ietf/connection.js.map +1 -1
- package/ietf/fetch.d.ts +17 -0
- package/ietf/fetch.d.ts.map +1 -1
- package/ietf/fetch.js +21 -0
- package/ietf/fetch.js.map +1 -1
- package/ietf/filter.d.ts +87 -0
- package/ietf/filter.d.ts.map +1 -0
- package/ietf/filter.js +292 -0
- package/ietf/filter.js.map +1 -0
- package/ietf/index.d.ts +2 -0
- package/ietf/index.d.ts.map +1 -1
- package/ietf/index.js +3 -0
- package/ietf/index.js.map +1 -1
- package/ietf/namespace.d.ts +6 -0
- package/ietf/namespace.d.ts.map +1 -1
- package/ietf/namespace.js +27 -2
- package/ietf/namespace.js.map +1 -1
- package/ietf/object.d.ts +54 -6
- package/ietf/object.d.ts.map +1 -1
- package/ietf/object.js +178 -25
- package/ietf/object.js.map +1 -1
- package/ietf/parameters.d.ts +35 -10
- package/ietf/parameters.d.ts.map +1 -1
- package/ietf/parameters.js +236 -45
- package/ietf/parameters.js.map +1 -1
- package/ietf/priority.d.ts +5 -0
- package/ietf/priority.d.ts.map +1 -0
- package/ietf/priority.js +10 -0
- package/ietf/priority.js.map +1 -0
- package/ietf/properties.d.ts +13 -2
- package/ietf/properties.d.ts.map +1 -1
- package/ietf/properties.js +65 -8
- package/ietf/properties.js.map +1 -1
- package/ietf/publish.d.ts.map +1 -1
- package/ietf/publish.js +13 -3
- package/ietf/publish.js.map +1 -1
- package/ietf/publish_namespace.d.ts +16 -2
- package/ietf/publish_namespace.d.ts.map +1 -1
- package/ietf/publish_namespace.js +25 -6
- package/ietf/publish_namespace.js.map +1 -1
- package/ietf/publisher.d.ts +40 -7
- package/ietf/publisher.d.ts.map +1 -1
- package/ietf/publisher.js +751 -134
- package/ietf/publisher.js.map +1 -1
- package/ietf/request.js +1 -1
- package/ietf/request.js.map +1 -1
- package/ietf/solicit.d.ts +41 -0
- package/ietf/solicit.d.ts.map +1 -0
- package/ietf/solicit.js +47 -0
- package/ietf/solicit.js.map +1 -0
- package/ietf/subscribe.d.ts +41 -2
- package/ietf/subscribe.d.ts.map +1 -1
- package/ietf/subscribe.js +103 -22
- package/ietf/subscribe.js.map +1 -1
- package/ietf/subscribe_namespace.d.ts +21 -7
- package/ietf/subscribe_namespace.d.ts.map +1 -1
- package/ietf/subscribe_namespace.js +31 -14
- package/ietf/subscribe_namespace.js.map +1 -1
- package/ietf/subscriber.d.ts +20 -7
- package/ietf/subscriber.d.ts.map +1 -1
- package/ietf/subscriber.js +512 -142
- package/ietf/subscriber.js.map +1 -1
- package/ietf/version.d.ts +7 -1
- package/ietf/version.d.ts.map +1 -1
- package/ietf/version.js +7 -0
- package/ietf/version.js.map +1 -1
- package/index.d.ts +10 -5
- package/index.d.ts.map +1 -1
- package/index.js +10 -5
- package/index.js.map +1 -1
- package/internal.d.ts +29 -0
- package/internal.d.ts.map +1 -0
- package/internal.js +8 -0
- package/internal.js.map +1 -0
- package/lite/announce.d.ts +62 -15
- package/lite/announce.d.ts.map +1 -1
- package/lite/announce.js +230 -94
- package/lite/announce.js.map +1 -1
- package/lite/connection.d.ts +69 -20
- package/lite/connection.d.ts.map +1 -1
- package/lite/connection.js +128 -81
- package/lite/connection.js.map +1 -1
- package/lite/datagram.d.ts +23 -0
- package/lite/datagram.d.ts.map +1 -0
- package/lite/datagram.js +57 -0
- package/lite/datagram.js.map +1 -0
- package/lite/datagram_stream.d.ts +8 -0
- package/lite/datagram_stream.d.ts.map +1 -0
- package/lite/datagram_stream.js +44 -0
- package/lite/datagram_stream.js.map +1 -0
- package/lite/fetch.d.ts.map +1 -1
- package/lite/fetch.js +2 -2
- package/lite/fetch.js.map +1 -1
- package/lite/index.d.ts +3 -0
- package/lite/index.d.ts.map +1 -1
- package/lite/index.js +3 -0
- package/lite/index.js.map +1 -1
- package/lite/priority.d.ts +67 -0
- package/lite/priority.d.ts.map +1 -0
- package/lite/priority.js +114 -0
- package/lite/priority.js.map +1 -0
- package/lite/probe.d.ts +18 -2
- package/lite/probe.d.ts.map +1 -1
- package/lite/probe.js +24 -22
- package/lite/probe.js.map +1 -1
- package/lite/publisher.d.ts +14 -7
- package/lite/publisher.d.ts.map +1 -1
- package/lite/publisher.js +406 -84
- package/lite/publisher.js.map +1 -1
- package/lite/setup.d.ts +83 -6
- package/lite/setup.d.ts.map +1 -1
- package/lite/setup.js +203 -31
- package/lite/setup.js.map +1 -1
- package/lite/stream.d.ts +12 -3
- package/lite/stream.d.ts.map +1 -1
- package/lite/stream.js +7 -4
- package/lite/stream.js.map +1 -1
- package/lite/subscribe.d.ts +37 -23
- package/lite/subscribe.d.ts.map +1 -1
- package/lite/subscribe.js +70 -55
- package/lite/subscribe.js.map +1 -1
- package/lite/subscriber.d.ts +29 -23
- package/lite/subscriber.d.ts.map +1 -1
- package/lite/subscriber.js +689 -112
- package/lite/subscriber.js.map +1 -1
- package/lite/track.d.ts +25 -13
- package/lite/track.d.ts.map +1 -1
- package/lite/track.js +60 -35
- package/lite/track.js.map +1 -1
- package/lite/version.d.ts +39 -4
- package/lite/version.d.ts.map +1 -1
- package/lite/version.js +139 -17
- package/lite/version.js.map +1 -1
- package/mock.d.ts +53 -3
- package/mock.d.ts.map +1 -1
- package/mock.js +110 -21
- package/mock.js.map +1 -1
- package/{lite/origin.d.ts → origin.d.ts} +25 -1
- package/origin.d.ts.map +1 -0
- package/{lite/origin.js → origin.js} +25 -1
- package/origin.js.map +1 -0
- package/package.json +6 -4
- package/path.d.ts +67 -0
- package/path.d.ts.map +1 -1
- package/path.js +128 -0
- package/path.js.map +1 -1
- package/stream.d.ts +94 -4
- package/stream.d.ts.map +1 -1
- package/stream.js +128 -17
- package/stream.js.map +1 -1
- package/time.d.ts +109 -86
- package/time.d.ts.map +1 -1
- package/time.js +80 -30
- package/time.js.map +1 -1
- package/track.d.ts +298 -47
- package/track.d.ts.map +1 -1
- package/track.js +721 -109
- package/track.js.map +1 -1
- package/util/timeout.d.ts +5 -0
- package/util/timeout.d.ts.map +1 -0
- package/util/timeout.js +18 -0
- package/util/timeout.js.map +1 -0
- package/util/utf8.d.ts +3 -0
- package/util/utf8.d.ts.map +1 -0
- package/util/utf8.js +7 -0
- package/util/utf8.js.map +1 -0
- package/varint.d.ts +14 -3
- package/varint.d.ts.map +1 -1
- package/varint.js +23 -12
- package/varint.js.map +1 -1
- package/zod.d.ts +5 -5
- package/zod.d.ts.map +1 -1
- package/zod.js.map +1 -1
- package/bandwidth.d.ts +0 -11
- package/bandwidth.d.ts.map +0 -1
- package/bandwidth.js +0 -7
- package/bandwidth.js.map +0 -1
- package/connection/cert.test.d.ts +0 -2
- package/connection/cert.test.d.ts.map +0 -1
- package/connection/cert.test.js +0 -26
- package/connection/cert.test.js.map +0 -1
- package/connection/reload.test.d.ts +0 -2
- package/connection/reload.test.d.ts.map +0 -1
- package/connection/reload.test.js +0 -39
- package/connection/reload.test.js.map +0 -1
- package/group.test.d.ts +0 -2
- package/group.test.d.ts.map +0 -1
- package/group.test.js +0 -66
- package/group.test.js.map +0 -1
- package/ietf/aliases.test.d.ts +0 -2
- package/ietf/aliases.test.d.ts.map +0 -1
- package/ietf/aliases.test.js +0 -30
- package/ietf/aliases.test.js.map +0 -1
- package/ietf/ietf.test.d.ts +0 -2
- package/ietf/ietf.test.d.ts.map +0 -1
- package/ietf/ietf.test.js +0 -873
- package/ietf/ietf.test.js.map +0 -1
- package/integration.test.d.ts +0 -2
- package/integration.test.d.ts.map +0 -1
- package/integration.test.js +0 -90
- package/integration.test.js.map +0 -1
- package/lite/origin.d.ts.map +0 -1
- package/lite/origin.js.map +0 -1
- package/lite/subscriber.test.d.ts +0 -2
- package/lite/subscriber.test.d.ts.map +0 -1
- package/lite/subscriber.test.js +0 -30
- package/lite/subscriber.test.js.map +0 -1
- package/lite/track.test.d.ts +0 -2
- package/lite/track.test.d.ts.map +0 -1
- package/lite/track.test.js +0 -53
- package/lite/track.test.js.map +0 -1
- package/path.test.d.ts +0 -2
- package/path.test.d.ts.map +0 -1
- package/path.test.js +0 -169
- package/path.test.js.map +0 -1
- package/stream.test.d.ts +0 -2
- package/stream.test.d.ts.map +0 -1
- package/stream.test.js +0 -210
- package/stream.test.js.map +0 -1
- package/track.test.d.ts +0 -2
- package/track.test.d.ts.map +0 -1
- package/track.test.js +0 -56
- package/track.test.js.map +0 -1
- package/util/error.d.ts +0 -3
- package/util/error.d.ts.map +0 -1
- package/util/error.js +0 -9
- package/util/error.js.map +0 -1
- package/util/index.d.ts +0 -2
- package/util/index.d.ts.map +0 -1
- package/util/index.js +0 -3
- package/util/index.js.map +0 -1
- package/varint.test.d.ts +0 -2
- package/varint.test.d.ts.map +0 -1
- package/varint.test.js +0 -106
- package/varint.test.js.map +0 -1
package/broadcast.js
CHANGED
|
@@ -1,76 +1,297 @@
|
|
|
1
1
|
/* @ts-self-types="./broadcast.d.ts" */
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Broadcast role handles: a named collection of tracks produced by a publisher.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { Once, Signal } from "@norskvideo/moq-signals";
|
|
8
|
+
import { hooks } from "./internal.js";
|
|
9
|
+
import * as track from "./track.js";
|
|
10
|
+
/** Reactive backing state shared by broadcast producers and consumers. */
|
|
11
|
+
class BroadcastState {
|
|
6
12
|
requested = new Signal([]);
|
|
7
|
-
closed = new
|
|
13
|
+
closed = new Once();
|
|
14
|
+
tracks = new Map();
|
|
15
|
+
sequences = new Map();
|
|
16
|
+
// Live consumer handles sharing this state (see {@link Consumer.clone}). The broadcast
|
|
17
|
+
// closes once the last one closes, so a shared consumer can be handed to several callers.
|
|
18
|
+
consumers = 0;
|
|
19
|
+
}
|
|
20
|
+
function dequeueRequest(state) {
|
|
21
|
+
const requested = state.requested.peek();
|
|
22
|
+
requested.sort((a, b) => a.priority - b.priority);
|
|
23
|
+
return requested.pop();
|
|
24
|
+
}
|
|
25
|
+
// Close the broadcast and reject any requests still pending in the queue, so a
|
|
26
|
+
// subscriber blocked on the track's info() or group reads is unblocked rather
|
|
27
|
+
// than left waiting on a producer that will never be served.
|
|
28
|
+
//
|
|
29
|
+
// Once.set throws on a second settle, and the producer and each consumer handle close
|
|
30
|
+
// independently, so this has to be idempotent.
|
|
31
|
+
function closeState(state, abort) {
|
|
32
|
+
if (state.closed.peek() !== undefined)
|
|
33
|
+
return;
|
|
34
|
+
state.closed.set(abort ?? null);
|
|
35
|
+
state.requested.mutate((requests) => {
|
|
36
|
+
for (const request of requests)
|
|
37
|
+
request.reject(abort);
|
|
38
|
+
requests.length = 0;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// `register` is set on the subscribing (consumer) side: the fresh producer is cached in
|
|
42
|
+
// `state.tracks` so repeat subscriptions to the same track fan out from one upstream subscription
|
|
43
|
+
// instead of opening a new one, mirroring the Rust `broadcast::Consumer::track` weak-dedup. The
|
|
44
|
+
// consumer wire watches the producer's demand ({@link track.Producer.used}) and tears the upstream
|
|
45
|
+
// down once its last subscriber leaves, closing the producer, which evicts the cache entry below.
|
|
46
|
+
// The publishing side leaves `register` false: `state.tracks` there holds only the tracks the app
|
|
47
|
+
// inserted, and a dynamic serve stays one request per peer subscription.
|
|
48
|
+
function subscribe(state, name, options = {}, register = false) {
|
|
49
|
+
if (state.closed.peek() !== undefined) {
|
|
50
|
+
throw new Error(`broadcast is closed: ${state.closed.peek()}`);
|
|
51
|
+
}
|
|
52
|
+
const existing = state.tracks.get(name);
|
|
53
|
+
if (existing) {
|
|
54
|
+
if (existing.closed.peek() === undefined)
|
|
55
|
+
return existing.subscribe(options);
|
|
56
|
+
state.tracks.delete(name);
|
|
57
|
+
}
|
|
58
|
+
const producer = new track.Producer(name);
|
|
59
|
+
const subscriber = producer.subscribe(options);
|
|
60
|
+
if (register) {
|
|
61
|
+
state.tracks.set(name, producer);
|
|
62
|
+
// Drop the cache entry once the upstream closes (the wire tears it down when its last
|
|
63
|
+
// subscriber leaves), so a later subscribe re-opens it.
|
|
64
|
+
void producer.closed.then(() => {
|
|
65
|
+
if (state.tracks.get(name) === producer)
|
|
66
|
+
state.tracks.delete(name);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
state.requested.mutate((requested) => {
|
|
70
|
+
requested.push(hooks.makeRequest({ name, producer, sequences: state.sequences }));
|
|
71
|
+
});
|
|
72
|
+
return subscriber;
|
|
73
|
+
}
|
|
74
|
+
async function resolveTrackInfo(state, name) {
|
|
75
|
+
const existing = state.tracks.get(name);
|
|
76
|
+
if (existing && existing.closed.peek() === undefined) {
|
|
77
|
+
return existing.info();
|
|
78
|
+
}
|
|
79
|
+
if (state.closed.peek() !== undefined) {
|
|
80
|
+
return Promise.reject(new Error(`broadcast is closed: ${state.closed.peek()}`));
|
|
81
|
+
}
|
|
82
|
+
const producer = new track.Producer(name);
|
|
83
|
+
state.requested.mutate((requested) => {
|
|
84
|
+
requested.push(hooks.makeRequest({ name, producer, sequences: state.sequences }));
|
|
85
|
+
});
|
|
86
|
+
try {
|
|
87
|
+
return await producer.info();
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
producer.close();
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// Serve a group from the local retained window by subscribing and scanning to the
|
|
94
|
+
// requested sequence. The default for a produced broadcast; the consuming wire layer
|
|
95
|
+
// overrides it to fetch over the network (or to reject when the transport has no FETCH).
|
|
96
|
+
async function fetchGroup(state, name, sequence, options = {}) {
|
|
97
|
+
const subscriber = subscribe(state, name, { priority: options.priority });
|
|
98
|
+
try {
|
|
99
|
+
for (;;) {
|
|
100
|
+
const group = await subscriber.recvGroup();
|
|
101
|
+
if (!group)
|
|
102
|
+
throw new Error(`group not found: ${sequence}`);
|
|
103
|
+
if (group.sequence === sequence) {
|
|
104
|
+
// Close the subscription when the returned group finishes, not now: an
|
|
105
|
+
// in-progress group must keep receiving frames for its lifetime (mirrors
|
|
106
|
+
// Rust poll_fetch). Also fires if the caller closes the group early.
|
|
107
|
+
void group.closed.then(() => subscriber.close());
|
|
108
|
+
return group;
|
|
109
|
+
}
|
|
110
|
+
group.close();
|
|
111
|
+
if (group.sequence > sequence)
|
|
112
|
+
throw new Error(`group not found: ${sequence}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
subscriber.close();
|
|
117
|
+
throw err;
|
|
118
|
+
}
|
|
8
119
|
}
|
|
9
120
|
/**
|
|
10
|
-
*
|
|
121
|
+
* The write side of a broadcast.
|
|
11
122
|
*
|
|
12
123
|
* @public
|
|
13
124
|
*/
|
|
14
|
-
export class
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
125
|
+
export class Producer {
|
|
126
|
+
#state = new BroadcastState();
|
|
127
|
+
/**
|
|
128
|
+
* Settles once the broadcast closes: `null` on a clean close, or the abort {@link Error}.
|
|
129
|
+
* Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.
|
|
130
|
+
*/
|
|
131
|
+
get closed() {
|
|
132
|
+
return this.#state.closed;
|
|
133
|
+
}
|
|
134
|
+
/** A read handle for this broadcast. */
|
|
135
|
+
consume() {
|
|
136
|
+
return makeConsumer(this.#state);
|
|
137
|
+
}
|
|
138
|
+
/** Return the next track requested by a peer. */
|
|
139
|
+
async requested() {
|
|
140
|
+
for (;;) {
|
|
141
|
+
const request = dequeueRequest(this.#state);
|
|
142
|
+
if (request)
|
|
143
|
+
return request;
|
|
144
|
+
const closed = this.#state.closed.peek();
|
|
145
|
+
if (closed instanceof Error)
|
|
146
|
+
throw closed;
|
|
147
|
+
if (closed !== undefined)
|
|
148
|
+
return undefined;
|
|
149
|
+
await Signal.race(this.#state.requested, this.#state.closed);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** Insert a track that is served directly, without an on-demand request round-trip. */
|
|
153
|
+
insertTrack(track) {
|
|
154
|
+
if (this.#state.closed.peek() !== undefined) {
|
|
155
|
+
throw new Error(`broadcast is closed: ${this.#state.closed.peek()}`);
|
|
156
|
+
}
|
|
157
|
+
const existing = this.#state.tracks.get(track.name);
|
|
158
|
+
if (existing && existing.closed.peek() === undefined) {
|
|
159
|
+
throw new Error(`duplicate track: ${track.name}`);
|
|
160
|
+
}
|
|
161
|
+
this.#state.tracks.set(track.name, track);
|
|
162
|
+
void track.closed.then(() => {
|
|
163
|
+
if (this.#state.tracks.get(track.name) === track) {
|
|
164
|
+
this.#state.tracks.delete(track.name);
|
|
165
|
+
}
|
|
27
166
|
});
|
|
28
167
|
}
|
|
168
|
+
/** Create a track, insert it into the broadcast, and return its producer. */
|
|
169
|
+
createTrack(name, info = {}) {
|
|
170
|
+
const producer = new track.Producer(name).accept(info);
|
|
171
|
+
this.insertTrack(producer);
|
|
172
|
+
return producer;
|
|
173
|
+
}
|
|
174
|
+
/** Remove a statically inserted track by name. */
|
|
175
|
+
removeTrack(name) {
|
|
176
|
+
this.#state.tracks.delete(name);
|
|
177
|
+
}
|
|
178
|
+
/** Open a live subscription to a track. Used by the publishing wire layer. */
|
|
179
|
+
subscribe(name, options) {
|
|
180
|
+
return subscribe(this.#state, name, options);
|
|
181
|
+
}
|
|
182
|
+
/** Resolve a track's immutable info. Used by the publishing wire layer. */
|
|
183
|
+
resolveTrackInfo(name) {
|
|
184
|
+
return resolveTrackInfo(this.#state, name);
|
|
185
|
+
}
|
|
186
|
+
/** Fetch a single group from the local retained window. Used by track handles. */
|
|
187
|
+
fetchGroup(name, sequence, options) {
|
|
188
|
+
return fetchGroup(this.#state, name, sequence, options);
|
|
189
|
+
}
|
|
190
|
+
/** A lazy read handle for a track on this broadcast. */
|
|
191
|
+
track(name) {
|
|
192
|
+
return new track.Consumer(name, this);
|
|
193
|
+
}
|
|
194
|
+
/** Close the broadcast, optionally with an error to abort waiters. Idempotent. */
|
|
195
|
+
close(abort) {
|
|
196
|
+
closeState(this.#state, abort);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// Constructs a Consumer from within this module without exposing a public constructor
|
|
200
|
+
// that would leak the unexported BroadcastState. Assigned in the class's static block.
|
|
201
|
+
let makeConsumer;
|
|
202
|
+
/**
|
|
203
|
+
* The read side of a broadcast.
|
|
204
|
+
*
|
|
205
|
+
* Created internally: obtain one from {@link Producer.consume} or the connection's
|
|
206
|
+
* `consume(path)`. The wire layers subclass it to resolve tracks over the network.
|
|
207
|
+
*
|
|
208
|
+
* @public
|
|
209
|
+
*/
|
|
210
|
+
export class Consumer {
|
|
211
|
+
#state;
|
|
212
|
+
// Guards against a double close() on this handle over-decrementing the consumer count.
|
|
213
|
+
#closed = false;
|
|
214
|
+
constructor(state) {
|
|
215
|
+
this.#state = state ?? new BroadcastState();
|
|
216
|
+
this.#state.consumers++;
|
|
217
|
+
}
|
|
218
|
+
static {
|
|
219
|
+
makeConsumer = (state) => new Consumer(state);
|
|
220
|
+
}
|
|
29
221
|
/**
|
|
30
|
-
*
|
|
222
|
+
* Settles once the broadcast closes: `null` on a clean close, or the abort {@link Error}.
|
|
223
|
+
* Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.
|
|
224
|
+
*
|
|
225
|
+
* Shared by every {@link clone}: it settles once the last handle closes. The subscribing
|
|
226
|
+
* wire layer peeks it to evict a closed entry from its per-path consume cache.
|
|
31
227
|
*/
|
|
228
|
+
get closed() {
|
|
229
|
+
return this.#state.closed;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Return another handle to the same broadcast, reference-counted with this one.
|
|
233
|
+
*
|
|
234
|
+
* Both handles read the same tracks and share one {@link closed} state; the broadcast
|
|
235
|
+
* closes only once *every* handle has {@link close}d. Used by the connection's per-path
|
|
236
|
+
* consume cache to share one subscription across callers. Subclasses that resolve info over
|
|
237
|
+
* the wire override this to preserve their type (see the wire layer's consumed broadcast).
|
|
238
|
+
*/
|
|
239
|
+
clone() {
|
|
240
|
+
return new Consumer(this.shareState());
|
|
241
|
+
}
|
|
242
|
+
// Hand this consumer's backing state to a clone. Opaque (`never`) so the state type stays
|
|
243
|
+
// unexported; a subclass passes it straight back into its own `super(...)`.
|
|
244
|
+
shareState() {
|
|
245
|
+
return this.#state;
|
|
246
|
+
}
|
|
247
|
+
/** Get a lazy handle for a track on this broadcast. Repeat subscriptions dedupe onto one upstream subscription. */
|
|
248
|
+
track(name) {
|
|
249
|
+
return new track.Consumer(name, this);
|
|
250
|
+
}
|
|
251
|
+
/** Open a live subscription to a track. Used by the subscribing wire layer. Repeat subscriptions to the same track share one upstream subscription. */
|
|
252
|
+
subscribe(name, options) {
|
|
253
|
+
return subscribe(this.#state, name, options, true);
|
|
254
|
+
}
|
|
255
|
+
/** Return the next track requested by the local consumer. Used by the subscribing wire layer. */
|
|
32
256
|
async requested() {
|
|
33
257
|
for (;;) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const closed = this.state.closed.peek();
|
|
258
|
+
const request = dequeueRequest(this.#state);
|
|
259
|
+
if (request)
|
|
260
|
+
return request;
|
|
261
|
+
const closed = this.#state.closed.peek();
|
|
39
262
|
if (closed instanceof Error)
|
|
40
263
|
throw closed;
|
|
41
|
-
if (closed)
|
|
264
|
+
if (closed !== undefined)
|
|
42
265
|
return undefined;
|
|
43
|
-
await Signal.race(this
|
|
266
|
+
await Signal.race(this.#state.requested, this.#state.closed);
|
|
44
267
|
}
|
|
45
268
|
}
|
|
46
269
|
/**
|
|
47
|
-
*
|
|
270
|
+
* Resolve a track's immutable info. Used by track handles. This base resolves it from
|
|
271
|
+
* the local producers; the consuming wire layer overrides it to fetch over the wire.
|
|
48
272
|
*/
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
if (this.state.closed.peek()) {
|
|
52
|
-
throw new Error(`broadcast is closed: ${this.state.closed.peek()}`);
|
|
53
|
-
}
|
|
54
|
-
this.state.requested.mutate((requested) => {
|
|
55
|
-
requested.push({ track, priority });
|
|
56
|
-
// Sort the tracks by priority in ascending order (we will pop)
|
|
57
|
-
requested.sort((a, b) => a.priority - b.priority);
|
|
58
|
-
});
|
|
59
|
-
return track;
|
|
273
|
+
resolveTrackInfo(name) {
|
|
274
|
+
return resolveTrackInfo(this.#state, name);
|
|
60
275
|
}
|
|
61
276
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
277
|
+
* Fetch a single group by sequence. Used by track handles. This base serves from the
|
|
278
|
+
* local retained window; the consuming wire layer overrides it to fetch over the wire
|
|
279
|
+
* (or to reject when the transport has no FETCH).
|
|
280
|
+
*/
|
|
281
|
+
fetchGroup(name, sequence, options) {
|
|
282
|
+
return fetchGroup(this.#state, name, sequence, options);
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Release this handle. The broadcast is closed (optionally with an error to abort waiters)
|
|
286
|
+
* once this was the last live handle; while other {@link clone}s remain open it stays live.
|
|
65
287
|
*/
|
|
66
288
|
close(abort) {
|
|
67
|
-
this
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
289
|
+
if (this.#closed)
|
|
290
|
+
return;
|
|
291
|
+
this.#closed = true;
|
|
292
|
+
if (--this.#state.consumers > 0)
|
|
293
|
+
return;
|
|
294
|
+
closeState(this.#state, abort);
|
|
74
295
|
}
|
|
75
296
|
}
|
|
76
297
|
//# sourceMappingURL=broadcast.js.map
|
package/broadcast.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"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","sourcesContent":["import { Signal } from \"@moq/signals\";\nimport { Track } from \"./track.ts\";\n\n/** A track a subscriber asked for, along with its requested delivery priority. */\nexport interface TrackRequest {\n\ttrack: Track;\n\tpriority: number;\n}\n\n/** Reactive backing state for a {@link Broadcast}: requested tracks plus a closed flag. */\nexport class BroadcastState {\n\trequested = new Signal<TrackRequest[]>([]);\n\tclosed = new Signal<boolean | Error>(false);\n}\n\n/**\n * Handles writing and managing tracks in a broadcast.\n *\n * @public\n */\nexport class Broadcast {\n\t/** Reactive backing state. */\n\tstate = new BroadcastState();\n\n\t/** Resolves with the abort error (or undefined) once closed. */\n\treadonly closed: Promise<Error | undefined>;\n\n\tconstructor() {\n\t\tthis.closed = new Promise((resolve) => {\n\t\t\tconst dispose = this.state.closed.subscribe((closed) => {\n\t\t\t\tif (!closed) return;\n\t\t\t\tresolve(closed instanceof Error ? closed : undefined);\n\t\t\t\tdispose();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * A track requested over the network.\n\t */\n\tasync requested(): Promise<TrackRequest | undefined> {\n\t\tfor (;;) {\n\t\t\t// We use pop instead of shift because it's slightly more efficient.\n\t\t\tconst track = this.state.requested.peek().pop();\n\t\t\tif (track) return track;\n\n\t\t\tconst closed = this.state.closed.peek();\n\t\t\tif (closed instanceof Error) throw closed;\n\t\t\tif (closed) return undefined;\n\n\t\t\tawait Signal.race(this.state.requested, this.state.closed);\n\t\t}\n\t}\n\n\t/**\n\t * Populates the provided track over the network.\n\t */\n\tsubscribe(name: string, priority: number): Track {\n\t\tconst track = new Track(name);\n\n\t\tif (this.state.closed.peek()) {\n\t\t\tthrow new Error(`broadcast is closed: ${this.state.closed.peek()}`);\n\t\t}\n\t\tthis.state.requested.mutate((requested) => {\n\t\t\trequested.push({ track, priority });\n\t\t\t// Sort the tracks by priority in ascending order (we will pop)\n\t\t\trequested.sort((a, b) => a.priority - b.priority);\n\t\t});\n\n\t\treturn track;\n\t}\n\n\t/**\n\t * Closes the writer and all associated tracks.\n\t *\n\t * @param abort - If provided, throw this exception instead of returning undefined.\n\t */\n\tclose(abort?: Error) {\n\t\tthis.state.closed.set(abort ?? true);\n\t\tfor (const { track } of this.state.requested.peek()) {\n\t\t\ttrack.close(abort);\n\t\t}\n\t\tthis.state.requested.mutate((requested) => {\n\t\t\trequested.length = 0;\n\t\t});\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"broadcast.js","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAmB,IAAI,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE7D,OAAO,EAAE,KAAK,EAAsB,MAAM,eAAe,CAAC;AAC1D,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAEpC,0EAA0E;AAC1E,MAAM,cAAc;IACnB,SAAS,GAAG,IAAI,MAAM,CAAkB,EAAE,CAAC,CAAC;IAC5C,MAAM,GAAG,IAAI,IAAI,EAAgB,CAAC;IAClC,MAAM,GAAG,IAAI,GAAG,EAA0B,CAAC;IAC3C,SAAS,GAAG,IAAI,GAAG,EAAyB,CAAC;IAC7C,uFAAuF;IACvF,0FAA0F;IAC1F,SAAS,GAAG,CAAC,CAAC;CACd;AAED,SAAS,cAAc,CAAC,KAAqB;IAC5C,MAAM,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IACzC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;IAClD,OAAO,SAAS,CAAC,GAAG,EAAE,CAAC;AACxB,CAAC;AAED,+EAA+E;AAC/E,8EAA8E;AAC9E,6DAA6D;AAC7D,EAAE;AACF,sFAAsF;AACtF,+CAA+C;AAC/C,SAAS,UAAU,CAAC,KAAqB,EAAE,KAAa;IACvD,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS;QAAE,OAAO;IAC9C,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;IAChC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,EAAE;QACnC,KAAK,MAAM,OAAO,IAAI,QAAQ;YAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtD,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IACrB,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,wFAAwF;AACxF,kGAAkG;AAClG,gGAAgG;AAChG,mGAAmG;AACnG,kGAAkG;AAClG,kGAAkG;AAClG,yEAAyE;AACzE,SAAS,SAAS,CACjB,KAAqB,EACrB,IAAY,EACZ,OAAO,GAAuB,EAAE,EAChC,QAAQ,GAAG,KAAK;IAEhB,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,wBAAwB,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,QAAQ,EAAE,CAAC;QACd,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS;YAAE,OAAO,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QAC7E,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1C,MAAM,UAAU,GAAG,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IAE/C,IAAI,QAAQ,EAAE,CAAC;QACd,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACjC,sFAAsF;QACtF,wDAAwD;QACxD,KAAK,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAC9B,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ;gBAAE,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpE,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE;QACpC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,OAAO,UAAU,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,KAAqB,EAAE,IAAY;IAClE,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACtD,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;IACxB,CAAC;IAED,IAAI,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,wBAAwB,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1C,KAAK,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE;QACpC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACJ,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC9B,CAAC;YAAS,CAAC;QACV,QAAQ,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;AACF,CAAC;AAED,kFAAkF;AAClF,qFAAqF;AACrF,yFAAyF;AACzF,KAAK,UAAU,UAAU,CACxB,KAAqB,EACrB,IAAY,EACZ,QAAgB,EAChB,OAAO,GAA4B,EAAE;IAErC,MAAM,UAAU,GAAG,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC1E,IAAI,CAAC;QACJ,SAAS,CAAC;YACT,MAAM,KAAK,GAAG,MAAM,UAAU,CAAC,SAAS,EAAE,CAAC;YAC3C,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,EAAE,CAAC,CAAC;YAC5D,IAAI,KAAK,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACjC,uEAAuE;gBACvE,yEAAyE;gBACzE,qEAAqE;gBACrE,KAAK,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,CAAC;gBACjD,OAAO,KAAK,CAAC;YACd,CAAC;YAED,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,KAAK,CAAC,QAAQ,GAAG,QAAQ;gBAAE,MAAM,IAAI,KAAK,CAAC,oBAAoB,QAAQ,EAAE,CAAC,CAAC;QAChF,CAAC;IACF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACd,UAAU,CAAC,KAAK,EAAE,CAAC;QACnB,MAAM,GAAG,CAAC;IACX,CAAC;AACF,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,QAAQ;IACpB,MAAM,GAAG,IAAI,cAAc,EAAE,CAAC;IAE9B;;;OAGG;IACH,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED,wCAAwC;IACxC,OAAO;QACN,OAAO,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,iDAAiD;IACjD,KAAK,CAAC,SAAS;QACd,SAAS,CAAC;YACT,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,OAAO;gBAAE,OAAO,OAAO,CAAC;YAE5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACzC,IAAI,MAAM,YAAY,KAAK;gBAAE,MAAM,MAAM,CAAC;YAC1C,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YAE3C,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9D,CAAC;IACF,CAAC;IAED,uFAAuF;IACvF,WAAW,CAAC,KAAqB;QAChC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CAAC,wBAAwB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACtE,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpD,IAAI,QAAQ,IAAI,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;YACtD,MAAM,IAAI,KAAK,CAAC,oBAAoB,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAE1C,KAAK,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAC3B,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,EAAE,CAAC;gBAClD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACvC,CAAC;QACF,CAAC,CAAC,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,WAAW,CAAC,IAAY,EAAE,IAAI,GAAwB,EAAE;QACvD,MAAM,QAAQ,GAAG,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC3B,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,kDAAkD;IAClD,WAAW,CAAC,IAAY;QACvB,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,8EAA8E;IAC9E,SAAS,CAAC,IAAY,EAAE,OAA4B;QACnD,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,CAAC;IAED,2EAA2E;IAC3E,gBAAgB,CAAC,IAAY;QAC5B,OAAO,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED,kFAAkF;IAClF,UAAU,CAAC,IAAY,EAAE,QAAgB,EAAE,OAAiC;QAC3E,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED,wDAAwD;IACxD,KAAK,CAAC,IAAY;QACjB,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,kFAAkF;IAClF,KAAK,CAAC,KAAa;QAClB,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;CACD;AAED,sFAAsF;AACtF,uFAAuF;AACvF,IAAI,YAAiD,CAAC;AAEtD;;;;;;;GAOG;AACH,MAAM,OAAO,QAAQ;IACpB,MAAM,CAAiB;IAEvB,uFAAuF;IACvF,OAAO,GAAG,KAAK,CAAC;IAGhB,YAAsB,KAAsB;QAC3C,IAAI,CAAC,MAAM,GAAG,KAAK,IAAI,IAAI,cAAc,EAAE,CAAC;QAC5C,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;IACzB,CAAC;IAED;QACC,YAAY,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,QAAQ,CAAC,KAAc,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;OAMG;IACH,IAAI,MAAM;QACT,OAAO,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;IAC3B,CAAC;IAED;;;;;;;OAOG;IACH,KAAK;QACJ,OAAO,IAAI,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,0FAA0F;IAC1F,4EAA4E;IAClE,UAAU;QACnB,OAAO,IAAI,CAAC,MAAe,CAAC;IAC7B,CAAC;IAED,mHAAmH;IACnH,KAAK,CAAC,IAAY;QACjB,OAAO,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACvC,CAAC;IAED,uJAAuJ;IACvJ,SAAS,CAAC,IAAY,EAAE,OAA4B;QACnD,OAAO,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACpD,CAAC;IAED,iGAAiG;IACjG,KAAK,CAAC,SAAS;QACd,SAAS,CAAC;YACT,MAAM,OAAO,GAAG,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YAC5C,IAAI,OAAO;gBAAE,OAAO,OAAO,CAAC;YAE5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACzC,IAAI,MAAM,YAAY,KAAK;gBAAE,MAAM,MAAM,CAAC;YAC1C,IAAI,MAAM,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAC;YAE3C,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC9D,CAAC;IACF,CAAC;IAED;;;OAGG;IACH,gBAAgB,CAAC,IAAY;QAC5B,OAAO,gBAAgB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,UAAU,CAAC,IAAY,EAAE,QAAgB,EAAE,OAAiC;QAC3E,OAAO,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IACzD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAa;QAClB,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO;QACzB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,SAAS,GAAG,CAAC;YAAE,OAAO;QACxC,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;CACD","sourcesContent":["/**\n * Broadcast role handles: a named collection of tracks produced by a publisher.\n *\n * @module\n */\nimport { type GetPromise, Once, Signal } from \"@moq/signals\";\nimport type { Consumer as GroupConsumer } from \"./group.ts\";\nimport { hooks, type TrackSequence } from \"./internal.ts\";\nimport * as track from \"./track.ts\";\n\n/** Reactive backing state shared by broadcast producers and consumers. */\nclass BroadcastState {\n\trequested = new Signal<track.Request[]>([]);\n\tclosed = new Once<Error | null>();\n\ttracks = new Map<string, track.Producer>();\n\tsequences = new Map<string, TrackSequence>();\n\t// Live consumer handles sharing this state (see {@link Consumer.clone}). The broadcast\n\t// closes once the last one closes, so a shared consumer can be handed to several callers.\n\tconsumers = 0;\n}\n\nfunction dequeueRequest(state: BroadcastState): track.Request | undefined {\n\tconst requested = state.requested.peek();\n\trequested.sort((a, b) => a.priority - b.priority);\n\treturn requested.pop();\n}\n\n// Close the broadcast and reject any requests still pending in the queue, so a\n// subscriber blocked on the track's info() or group reads is unblocked rather\n// than left waiting on a producer that will never be served.\n//\n// Once.set throws on a second settle, and the producer and each consumer handle close\n// independently, so this has to be idempotent.\nfunction closeState(state: BroadcastState, abort?: Error) {\n\tif (state.closed.peek() !== undefined) return;\n\tstate.closed.set(abort ?? null);\n\tstate.requested.mutate((requests) => {\n\t\tfor (const request of requests) request.reject(abort);\n\t\trequests.length = 0;\n\t});\n}\n\n// `register` is set on the subscribing (consumer) side: the fresh producer is cached in\n// `state.tracks` so repeat subscriptions to the same track fan out from one upstream subscription\n// instead of opening a new one, mirroring the Rust `broadcast::Consumer::track` weak-dedup. The\n// consumer wire watches the producer's demand ({@link track.Producer.used}) and tears the upstream\n// down once its last subscriber leaves, closing the producer, which evicts the cache entry below.\n// The publishing side leaves `register` false: `state.tracks` there holds only the tracks the app\n// inserted, and a dynamic serve stays one request per peer subscription.\nfunction subscribe(\n\tstate: BroadcastState,\n\tname: string,\n\toptions: track.Subscription = {},\n\tregister = false,\n): track.Subscriber {\n\tif (state.closed.peek() !== undefined) {\n\t\tthrow new Error(`broadcast is closed: ${state.closed.peek()}`);\n\t}\n\n\tconst existing = state.tracks.get(name);\n\tif (existing) {\n\t\tif (existing.closed.peek() === undefined) return existing.subscribe(options);\n\t\tstate.tracks.delete(name);\n\t}\n\n\tconst producer = new track.Producer(name);\n\tconst subscriber = producer.subscribe(options);\n\n\tif (register) {\n\t\tstate.tracks.set(name, producer);\n\t\t// Drop the cache entry once the upstream closes (the wire tears it down when its last\n\t\t// subscriber leaves), so a later subscribe re-opens it.\n\t\tvoid producer.closed.then(() => {\n\t\t\tif (state.tracks.get(name) === producer) state.tracks.delete(name);\n\t\t});\n\t}\n\n\tstate.requested.mutate((requested) => {\n\t\trequested.push(hooks.makeRequest({ name, producer, sequences: state.sequences }));\n\t});\n\n\treturn subscriber;\n}\n\nasync function resolveTrackInfo(state: BroadcastState, name: string): Promise<track.Info> {\n\tconst existing = state.tracks.get(name);\n\tif (existing && existing.closed.peek() === undefined) {\n\t\treturn existing.info();\n\t}\n\n\tif (state.closed.peek() !== undefined) {\n\t\treturn Promise.reject(new Error(`broadcast is closed: ${state.closed.peek()}`));\n\t}\n\n\tconst producer = new track.Producer(name);\n\tstate.requested.mutate((requested) => {\n\t\trequested.push(hooks.makeRequest({ name, producer, sequences: state.sequences }));\n\t});\n\n\ttry {\n\t\treturn await producer.info();\n\t} finally {\n\t\tproducer.close();\n\t}\n}\n\n// Serve a group from the local retained window by subscribing and scanning to the\n// requested sequence. The default for a produced broadcast; the consuming wire layer\n// overrides it to fetch over the network (or to reject when the transport has no FETCH).\nasync function fetchGroup(\n\tstate: BroadcastState,\n\tname: string,\n\tsequence: number,\n\toptions: track.FetchGroupOptions = {},\n): Promise<GroupConsumer> {\n\tconst subscriber = subscribe(state, name, { priority: options.priority });\n\ttry {\n\t\tfor (;;) {\n\t\t\tconst group = await subscriber.recvGroup();\n\t\t\tif (!group) throw new Error(`group not found: ${sequence}`);\n\t\t\tif (group.sequence === sequence) {\n\t\t\t\t// Close the subscription when the returned group finishes, not now: an\n\t\t\t\t// in-progress group must keep receiving frames for its lifetime (mirrors\n\t\t\t\t// Rust poll_fetch). Also fires if the caller closes the group early.\n\t\t\t\tvoid group.closed.then(() => subscriber.close());\n\t\t\t\treturn group;\n\t\t\t}\n\n\t\t\tgroup.close();\n\t\t\tif (group.sequence > sequence) throw new Error(`group not found: ${sequence}`);\n\t\t}\n\t} catch (err) {\n\t\tsubscriber.close();\n\t\tthrow err;\n\t}\n}\n\n/**\n * The write side of a broadcast.\n *\n * @public\n */\nexport class Producer implements track.Broadcast {\n\t#state = new BroadcastState();\n\n\t/**\n\t * Settles once the broadcast closes: `null` on a clean close, or the abort {@link Error}.\n\t * Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.\n\t */\n\tget closed(): GetPromise<Error | null> {\n\t\treturn this.#state.closed;\n\t}\n\n\t/** A read handle for this broadcast. */\n\tconsume(): Consumer {\n\t\treturn makeConsumer(this.#state);\n\t}\n\n\t/** Return the next track requested by a peer. */\n\tasync requested(): Promise<track.Request | undefined> {\n\t\tfor (;;) {\n\t\t\tconst request = dequeueRequest(this.#state);\n\t\t\tif (request) return request;\n\n\t\t\tconst closed = this.#state.closed.peek();\n\t\t\tif (closed instanceof Error) throw closed;\n\t\t\tif (closed !== undefined) return undefined;\n\n\t\t\tawait Signal.race(this.#state.requested, this.#state.closed);\n\t\t}\n\t}\n\n\t/** Insert a track that is served directly, without an on-demand request round-trip. */\n\tinsertTrack(track: track.Producer): void {\n\t\tif (this.#state.closed.peek() !== undefined) {\n\t\t\tthrow new Error(`broadcast is closed: ${this.#state.closed.peek()}`);\n\t\t}\n\n\t\tconst existing = this.#state.tracks.get(track.name);\n\t\tif (existing && existing.closed.peek() === undefined) {\n\t\t\tthrow new Error(`duplicate track: ${track.name}`);\n\t\t}\n\n\t\tthis.#state.tracks.set(track.name, track);\n\n\t\tvoid track.closed.then(() => {\n\t\t\tif (this.#state.tracks.get(track.name) === track) {\n\t\t\t\tthis.#state.tracks.delete(track.name);\n\t\t\t}\n\t\t});\n\t}\n\n\t/** Create a track, insert it into the broadcast, and return its producer. */\n\tcreateTrack(name: string, info: Partial<track.Info> = {}): track.Producer {\n\t\tconst producer = new track.Producer(name).accept(info);\n\t\tthis.insertTrack(producer);\n\t\treturn producer;\n\t}\n\n\t/** Remove a statically inserted track by name. */\n\tremoveTrack(name: string): void {\n\t\tthis.#state.tracks.delete(name);\n\t}\n\n\t/** Open a live subscription to a track. Used by the publishing wire layer. */\n\tsubscribe(name: string, options?: track.Subscription): track.Subscriber {\n\t\treturn subscribe(this.#state, name, options);\n\t}\n\n\t/** Resolve a track's immutable info. Used by the publishing wire layer. */\n\tresolveTrackInfo(name: string): Promise<track.Info> {\n\t\treturn resolveTrackInfo(this.#state, name);\n\t}\n\n\t/** Fetch a single group from the local retained window. Used by track handles. */\n\tfetchGroup(name: string, sequence: number, options?: track.FetchGroupOptions): Promise<GroupConsumer> {\n\t\treturn fetchGroup(this.#state, name, sequence, options);\n\t}\n\n\t/** A lazy read handle for a track on this broadcast. */\n\ttrack(name: string): track.Consumer {\n\t\treturn new track.Consumer(name, this);\n\t}\n\n\t/** Close the broadcast, optionally with an error to abort waiters. Idempotent. */\n\tclose(abort?: Error) {\n\t\tcloseState(this.#state, abort);\n\t}\n}\n\n// Constructs a Consumer from within this module without exposing a public constructor\n// that would leak the unexported BroadcastState. Assigned in the class's static block.\nlet makeConsumer: (state: BroadcastState) => Consumer;\n\n/**\n * The read side of a broadcast.\n *\n * Created internally: obtain one from {@link Producer.consume} or the connection's\n * `consume(path)`. The wire layers subclass it to resolve tracks over the network.\n *\n * @public\n */\nexport class Consumer implements track.Broadcast {\n\t#state: BroadcastState;\n\n\t// Guards against a double close() on this handle over-decrementing the consumer count.\n\t#closed = false;\n\n\tprotected constructor(state?: never);\n\tprotected constructor(state?: BroadcastState) {\n\t\tthis.#state = state ?? new BroadcastState();\n\t\tthis.#state.consumers++;\n\t}\n\n\tstatic {\n\t\tmakeConsumer = (state) => new Consumer(state as never);\n\t}\n\n\t/**\n\t * Settles once the broadcast closes: `null` on a clean close, or the abort {@link Error}.\n\t * Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.\n\t *\n\t * Shared by every {@link clone}: it settles once the last handle closes. The subscribing\n\t * wire layer peeks it to evict a closed entry from its per-path consume cache.\n\t */\n\tget closed(): GetPromise<Error | null> {\n\t\treturn this.#state.closed;\n\t}\n\n\t/**\n\t * Return another handle to the same broadcast, reference-counted with this one.\n\t *\n\t * Both handles read the same tracks and share one {@link closed} state; the broadcast\n\t * closes only once *every* handle has {@link close}d. Used by the connection's per-path\n\t * consume cache to share one subscription across callers. Subclasses that resolve info over\n\t * the wire override this to preserve their type (see the wire layer's consumed broadcast).\n\t */\n\tclone(): Consumer {\n\t\treturn new Consumer(this.shareState());\n\t}\n\n\t// Hand this consumer's backing state to a clone. Opaque (`never`) so the state type stays\n\t// unexported; a subclass passes it straight back into its own `super(...)`.\n\tprotected shareState(): never {\n\t\treturn this.#state as never;\n\t}\n\n\t/** Get a lazy handle for a track on this broadcast. Repeat subscriptions dedupe onto one upstream subscription. */\n\ttrack(name: string): track.Consumer {\n\t\treturn new track.Consumer(name, this);\n\t}\n\n\t/** Open a live subscription to a track. Used by the subscribing wire layer. Repeat subscriptions to the same track share one upstream subscription. */\n\tsubscribe(name: string, options?: track.Subscription): track.Subscriber {\n\t\treturn subscribe(this.#state, name, options, true);\n\t}\n\n\t/** Return the next track requested by the local consumer. Used by the subscribing wire layer. */\n\tasync requested(): Promise<track.Request | undefined> {\n\t\tfor (;;) {\n\t\t\tconst request = dequeueRequest(this.#state);\n\t\t\tif (request) return request;\n\n\t\t\tconst closed = this.#state.closed.peek();\n\t\t\tif (closed instanceof Error) throw closed;\n\t\t\tif (closed !== undefined) return undefined;\n\n\t\t\tawait Signal.race(this.#state.requested, this.#state.closed);\n\t\t}\n\t}\n\n\t/**\n\t * Resolve a track's immutable info. Used by track handles. This base resolves it from\n\t * the local producers; the consuming wire layer overrides it to fetch over the wire.\n\t */\n\tresolveTrackInfo(name: string): Promise<track.Info> {\n\t\treturn resolveTrackInfo(this.#state, name);\n\t}\n\n\t/**\n\t * Fetch a single group by sequence. Used by track handles. This base serves from the\n\t * local retained window; the consuming wire layer overrides it to fetch over the wire\n\t * (or to reject when the transport has no FETCH).\n\t */\n\tfetchGroup(name: string, sequence: number, options?: track.FetchGroupOptions): Promise<GroupConsumer> {\n\t\treturn fetchGroup(this.#state, name, sequence, options);\n\t}\n\n\t/**\n\t * Release this handle. The broadcast is closed (optionally with an error to abort waiters)\n\t * once this was the last live handle; while other {@link clone}s remain open it stays live.\n\t */\n\tclose(abort?: Error) {\n\t\tif (this.#closed) return;\n\t\tthis.#closed = true;\n\t\tif (--this.#state.consumers > 0) return;\n\t\tcloseState(this.#state, abort);\n\t}\n}\n"]}
|
package/connection/accept.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type { Established } from "./established";
|
|
2
2
|
/** Options for {@link accept}. */
|
|
3
3
|
export interface AcceptProps {
|
|
4
|
+
/** Version to select during SETUP negotiation (for non-ALPN paths). */
|
|
4
5
|
version?: number;
|
|
6
|
+
/**
|
|
7
|
+
* Whether this server supports broadcast discovery; see {@link Established.discovery}.
|
|
8
|
+
* Defaults to true.
|
|
9
|
+
*/
|
|
10
|
+
discovery?: boolean;
|
|
5
11
|
}
|
|
6
12
|
/**
|
|
7
13
|
* Server-side handshake: accepts a transport and performs the server half of the SETUP exchange.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accept.d.ts","sourceRoot":"","sources":["../../src/connection/accept.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,kCAAkC;AAClC,MAAM,WAAW,WAAW;
|
|
1
|
+
{"version":3,"file":"accept.d.ts","sourceRoot":"","sources":["../../src/connection/accept.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGpD,kCAAkC;AAClC,MAAM,WAAW,WAAW;IAC3B,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAgCzG"}
|
package/connection/accept.js
CHANGED
|
@@ -14,26 +14,36 @@ import { exchangeSetup } from "./handshake.js";
|
|
|
14
14
|
export async function accept(transport, url, props) {
|
|
15
15
|
// @ts-expect-error - TODO: add protocol to WebTransport
|
|
16
16
|
const protocol = transport.protocol;
|
|
17
|
+
const discovery = props?.discovery ?? true;
|
|
18
|
+
if (protocol === Ietf.ALPN.DRAFT_20) {
|
|
19
|
+
return acceptAlpn(transport, url, Ietf.Version.DRAFT_20, discovery);
|
|
20
|
+
}
|
|
17
21
|
if (protocol === Ietf.ALPN.DRAFT_19) {
|
|
18
|
-
return acceptAlpn(transport, url, Ietf.Version.DRAFT_19);
|
|
22
|
+
return acceptAlpn(transport, url, Ietf.Version.DRAFT_19, discovery);
|
|
19
23
|
}
|
|
20
24
|
else if (protocol === Ietf.ALPN.DRAFT_18) {
|
|
21
|
-
return acceptAlpn(transport, url, Ietf.Version.DRAFT_18);
|
|
25
|
+
return acceptAlpn(transport, url, Ietf.Version.DRAFT_18, discovery);
|
|
22
26
|
}
|
|
23
27
|
else if (protocol === Ietf.ALPN.DRAFT_17) {
|
|
24
|
-
return acceptAlpn(transport, url, Ietf.Version.DRAFT_17);
|
|
28
|
+
return acceptAlpn(transport, url, Ietf.Version.DRAFT_17, discovery);
|
|
25
29
|
}
|
|
26
30
|
else if (protocol === Ietf.ALPN.DRAFT_16) {
|
|
27
|
-
return acceptSetup(transport, url, Ietf.Version.DRAFT_16);
|
|
31
|
+
return acceptSetup(transport, url, Ietf.Version.DRAFT_16, discovery);
|
|
28
32
|
}
|
|
29
33
|
else if (protocol === Ietf.ALPN.DRAFT_15) {
|
|
30
|
-
return acceptSetup(transport, url, Ietf.Version.DRAFT_15);
|
|
34
|
+
return acceptSetup(transport, url, Ietf.Version.DRAFT_15, discovery);
|
|
35
|
+
}
|
|
36
|
+
else if (protocol === Lite.ALPN_06_WIP) {
|
|
37
|
+
return new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_06, discovery });
|
|
38
|
+
}
|
|
39
|
+
else if (protocol === Lite.ALPN_05) {
|
|
40
|
+
return new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_05, discovery });
|
|
31
41
|
}
|
|
32
42
|
else if (protocol === Lite.ALPN_04) {
|
|
33
|
-
return new Lite.Connection(url, transport, Lite.Version.DRAFT_04,
|
|
43
|
+
return new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_04, discovery });
|
|
34
44
|
}
|
|
35
45
|
else if (protocol === Lite.ALPN_03) {
|
|
36
|
-
return new Lite.Connection(url, transport, Lite.Version.DRAFT_03,
|
|
46
|
+
return new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_03, discovery });
|
|
37
47
|
}
|
|
38
48
|
else if (protocol === Lite.ALPN || protocol === "" || protocol === undefined) {
|
|
39
49
|
return acceptNegotiated(transport, url, props);
|
|
@@ -46,12 +56,16 @@ export async function accept(transport, url, props) {
|
|
|
46
56
|
* Draft-17+ accept: ALPN already pinned the version. SETUP is exchanged over
|
|
47
57
|
* a pair of uni streams using stream type 0x2F00.
|
|
48
58
|
*/
|
|
49
|
-
async function acceptAlpn(transport, url, version) {
|
|
50
|
-
const
|
|
59
|
+
async function acceptAlpn(transport, url, version, discovery) {
|
|
60
|
+
const { control, solicit, cluster } = await exchangeSetup(transport, version, "moq-lite-js");
|
|
51
61
|
return new Ietf.Connection({
|
|
62
|
+
discovery,
|
|
63
|
+
client: false,
|
|
52
64
|
url,
|
|
53
65
|
quic: transport,
|
|
54
|
-
control
|
|
66
|
+
control,
|
|
67
|
+
solicit,
|
|
68
|
+
cluster,
|
|
55
69
|
// v17+ uses NativeSession which manages its own request IDs; maxRequestId is unused.
|
|
56
70
|
maxRequestId: 0n,
|
|
57
71
|
version,
|
|
@@ -61,7 +75,7 @@ async function acceptAlpn(transport, url, version) {
|
|
|
61
75
|
* Legacy accept (draft-15/16): ALPN pinned the version, but the SETUP message
|
|
62
76
|
* is still exchanged over a bidi stream wrapped in the moq-lite compat envelope.
|
|
63
77
|
*/
|
|
64
|
-
async function acceptSetup(transport, url, version) {
|
|
78
|
+
async function acceptSetup(transport, url, version, discovery) {
|
|
65
79
|
// Accept bidi, read ClientSetup, write ServerSetup
|
|
66
80
|
const stream = await Stream.accept(transport);
|
|
67
81
|
if (!stream)
|
|
@@ -70,24 +84,29 @@ async function acceptSetup(transport, url, version) {
|
|
|
70
84
|
if (clientCompat !== Lite.StreamId.ClientCompat) {
|
|
71
85
|
throw new Error(`unexpected client message type: 0x${clientCompat.toString(16)}`);
|
|
72
86
|
}
|
|
73
|
-
await Ietf.ClientSetup.decode(stream.reader, version);
|
|
87
|
+
const client = await Ietf.ClientSetup.decode(stream.reader, version);
|
|
74
88
|
await stream.writer.u53(Lite.StreamId.ServerCompat);
|
|
75
89
|
const encoder = new TextEncoder();
|
|
76
90
|
const params = new Ietf.SetupOptions();
|
|
77
91
|
params.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);
|
|
78
92
|
params.setBytes(Ietf.SetupOption.Implementation, encoder.encode("moq-lite-js"));
|
|
93
|
+
Ietf.solicitIntoSetup(params);
|
|
79
94
|
const server = new Ietf.ServerSetup({ version, parameters: params });
|
|
80
95
|
await server.encode(stream.writer, version);
|
|
81
96
|
const maxRequestId = 42069n;
|
|
82
97
|
return new Ietf.Connection({
|
|
98
|
+
discovery,
|
|
99
|
+
client: false,
|
|
83
100
|
url,
|
|
84
101
|
quic: transport,
|
|
85
102
|
control: stream,
|
|
86
103
|
maxRequestId,
|
|
87
104
|
version,
|
|
105
|
+
solicit: Ietf.solicitFromSetup(client.parameters),
|
|
88
106
|
});
|
|
89
107
|
}
|
|
90
108
|
async function acceptNegotiated(transport, url, props) {
|
|
109
|
+
const discovery = props?.discovery ?? true;
|
|
91
110
|
const setupVersion = Ietf.Version.DRAFT_14;
|
|
92
111
|
const stream = await Stream.accept(transport);
|
|
93
112
|
if (!stream)
|
|
@@ -115,19 +134,29 @@ async function acceptNegotiated(transport, url, props) {
|
|
|
115
134
|
const params = new Ietf.SetupOptions();
|
|
116
135
|
params.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);
|
|
117
136
|
params.setBytes(Ietf.SetupOption.Implementation, encoder.encode("moq-lite-js"));
|
|
137
|
+
Ietf.solicitIntoSetup(params);
|
|
118
138
|
const server = new Ietf.ServerSetup({ version: selectedVersion, parameters: params });
|
|
119
139
|
await server.encode(stream.writer, setupVersion);
|
|
120
140
|
if (Object.values(Lite.Version).includes(selectedVersion)) {
|
|
121
|
-
return new Lite.Connection(
|
|
141
|
+
return new Lite.Connection({
|
|
142
|
+
url,
|
|
143
|
+
quic: transport,
|
|
144
|
+
version: selectedVersion,
|
|
145
|
+
session: stream,
|
|
146
|
+
discovery,
|
|
147
|
+
});
|
|
122
148
|
}
|
|
123
149
|
else if (Object.values(Ietf.Version).includes(selectedVersion)) {
|
|
124
150
|
const maxRequestId = client.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;
|
|
125
151
|
return new Ietf.Connection({
|
|
152
|
+
discovery,
|
|
153
|
+
client: false,
|
|
126
154
|
url,
|
|
127
155
|
quic: transport,
|
|
128
156
|
control: stream,
|
|
129
157
|
maxRequestId,
|
|
130
158
|
version: selectedVersion,
|
|
159
|
+
solicit: Ietf.solicitFromSetup(client.parameters),
|
|
131
160
|
});
|
|
132
161
|
}
|
|
133
162
|
else {
|
package/connection/accept.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accept.js","sourceRoot":"","sources":["../../src/connection/accept.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Ietf from \"../ietf/index.ts\";\nimport * as Lite from \"../lite/index.ts\";\nimport { Stream } from \"../stream.ts\";\nimport type { Established } from \"./established.ts\";\nimport { exchangeSetup } from \"./handshake.ts\";\n\n/** Options for {@link accept}. */\nexport interface AcceptProps {\n\t// Version to select during SETUP negotiation (for non-ALPN paths).\n\tversion?: number;\n}\n\n/**\n * Server-side handshake: accepts a transport and performs the server half of the SETUP exchange.\n *\n * @param transport - The WebTransport session to accept\n * @param url - The URL of the connection\n * @param props - Optional configuration\n * @returns A promise that resolves to a Connection instance\n */\nexport async function accept(transport: WebTransport, url: URL, props?: AcceptProps): Promise<Established> {\n\t// @ts-expect-error - TODO: add protocol to WebTransport\n\tconst protocol: string | undefined = transport.protocol;\n\n\tif (protocol === Ietf.ALPN.DRAFT_19) {\n\t\treturn acceptAlpn(transport, url, Ietf.Version.DRAFT_19);\n\t} else if (protocol === Ietf.ALPN.DRAFT_18) {\n\t\treturn acceptAlpn(transport, url, Ietf.Version.DRAFT_18);\n\t} else if (protocol === Ietf.ALPN.DRAFT_17) {\n\t\treturn acceptAlpn(transport, url, Ietf.Version.DRAFT_17);\n\t} else if (protocol === Ietf.ALPN.DRAFT_16) {\n\t\treturn acceptSetup(transport, url, Ietf.Version.DRAFT_16);\n\t} else if (protocol === Ietf.ALPN.DRAFT_15) {\n\t\treturn acceptSetup(transport, url, Ietf.Version.DRAFT_15);\n\t} else if (protocol === Lite.ALPN_04) {\n\t\treturn new Lite.Connection(url, transport, Lite.Version.DRAFT_04, undefined);\n\t} else if (protocol === Lite.ALPN_03) {\n\t\treturn new Lite.Connection(url, transport, Lite.Version.DRAFT_03, undefined);\n\t} else if (protocol === Lite.ALPN || protocol === \"\" || protocol === undefined) {\n\t\treturn acceptNegotiated(transport, url, props);\n\t} else {\n\t\tthrow new Error(`unsupported WebTransport protocol: ${protocol}`);\n\t}\n}\n\n/**\n * Draft-17+ accept: ALPN already pinned the version. SETUP is exchanged over\n * a pair of uni streams using stream type 0x2F00.\n */\nasync function acceptAlpn(transport: WebTransport, url: URL, version: Ietf.IetfVersion): Promise<Established> {\n\tconst controlStream = await exchangeSetup(transport, version, \"moq-lite-js\");\n\n\treturn new Ietf.Connection({\n\t\turl,\n\t\tquic: transport,\n\t\tcontrol: controlStream,\n\t\t// v17+ uses NativeSession which manages its own request IDs; maxRequestId is unused.\n\t\tmaxRequestId: 0n,\n\t\tversion,\n\t});\n}\n\n/**\n * Legacy accept (draft-15/16): ALPN pinned the version, but the SETUP message\n * is still exchanged over a bidi stream wrapped in the moq-lite compat envelope.\n */\nasync function acceptSetup(transport: WebTransport, url: URL, version: Ietf.IetfVersion): Promise<Established> {\n\t// Accept bidi, read ClientSetup, write ServerSetup\n\tconst stream = await Stream.accept(transport);\n\tif (!stream) throw new Error(\"no incoming bidi stream for SETUP\");\n\n\tconst clientCompat = await stream.reader.u53();\n\tif (clientCompat !== Lite.StreamId.ClientCompat) {\n\t\tthrow new Error(`unexpected client message type: 0x${clientCompat.toString(16)}`);\n\t}\n\n\tawait Ietf.ClientSetup.decode(stream.reader, version);\n\n\tawait stream.writer.u53(Lite.StreamId.ServerCompat);\n\n\tconst encoder = new TextEncoder();\n\tconst params = new Ietf.SetupOptions();\n\tparams.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);\n\tparams.setBytes(Ietf.SetupOption.Implementation, encoder.encode(\"moq-lite-js\"));\n\n\tconst server = new Ietf.ServerSetup({ version, parameters: params });\n\tawait server.encode(stream.writer, version);\n\n\tconst maxRequestId = 42069n;\n\n\treturn new Ietf.Connection({\n\t\turl,\n\t\tquic: transport,\n\t\tcontrol: stream,\n\t\tmaxRequestId,\n\t\tversion,\n\t});\n}\n\nasync function acceptNegotiated(transport: WebTransport, url: URL, props?: AcceptProps): Promise<Established> {\n\tconst setupVersion = Ietf.Version.DRAFT_14;\n\n\tconst stream = await Stream.accept(transport);\n\tif (!stream) throw new Error(\"no incoming bidi stream for SETUP\");\n\n\tconst clientCompat = await stream.reader.u53();\n\tif (clientCompat !== Lite.StreamId.ClientCompat) {\n\t\tthrow new Error(`unexpected client message type: 0x${clientCompat.toString(16)}`);\n\t}\n\n\tconst client = await Ietf.ClientSetup.decode(stream.reader, setupVersion);\n\n\t// Pick the requested version, or first matching version from client's list\n\tconst allVersions = [...Object.values(Lite.Version), ...Object.values(Ietf.Version)] as number[];\n\tlet selectedVersion: number;\n\tif (props?.version !== undefined) {\n\t\tselectedVersion = props.version;\n\t} else {\n\t\tconst match = client.versions.find((v) => allVersions.includes(v));\n\t\tif (match === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`no common version found; client offered: ${client.versions.map((v) => v.toString(16)).join(\", \")}`,\n\t\t\t);\n\t\t}\n\t\tselectedVersion = match;\n\t}\n\n\tawait stream.writer.u53(Lite.StreamId.ServerCompat);\n\n\tconst encoder = new TextEncoder();\n\tconst params = new Ietf.SetupOptions();\n\tparams.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);\n\tparams.setBytes(Ietf.SetupOption.Implementation, encoder.encode(\"moq-lite-js\"));\n\n\tconst server = new Ietf.ServerSetup({ version: selectedVersion, parameters: params });\n\tawait server.encode(stream.writer, setupVersion);\n\n\tif (Object.values(Lite.Version).includes(selectedVersion as Lite.Version)) {\n\t\treturn new Lite.Connection(url, transport, selectedVersion as Lite.Version, stream);\n\t} else if (Object.values(Ietf.Version).includes(selectedVersion as Ietf.Version)) {\n\t\tconst maxRequestId = client.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;\n\t\treturn new Ietf.Connection({\n\t\t\turl,\n\t\t\tquic: transport,\n\t\t\tcontrol: stream,\n\t\t\tmaxRequestId,\n\t\t\tversion: selectedVersion as Ietf.IetfVersion,\n\t\t});\n\t} else {\n\t\tthrow new Error(`unsupported version: ${selectedVersion.toString(16)}`);\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"accept.js","sourceRoot":"","sources":["../../src/connection/accept.ts"],"names":[],"mappings":"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","sourcesContent":["import * as Ietf from \"../ietf/index.ts\";\nimport * as Lite from \"../lite/index.ts\";\nimport { Stream } from \"../stream.ts\";\nimport type { Established } from \"./established.ts\";\nimport { exchangeSetup } from \"./handshake.ts\";\n\n/** Options for {@link accept}. */\nexport interface AcceptProps {\n\t/** Version to select during SETUP negotiation (for non-ALPN paths). */\n\tversion?: number;\n\n\t/**\n\t * Whether this server supports broadcast discovery; see {@link Established.discovery}.\n\t * Defaults to true.\n\t */\n\tdiscovery?: boolean;\n}\n\n/**\n * Server-side handshake: accepts a transport and performs the server half of the SETUP exchange.\n *\n * @param transport - The WebTransport session to accept\n * @param url - The URL of the connection\n * @param props - Optional configuration\n * @returns A promise that resolves to a Connection instance\n */\nexport async function accept(transport: WebTransport, url: URL, props?: AcceptProps): Promise<Established> {\n\t// @ts-expect-error - TODO: add protocol to WebTransport\n\tconst protocol: string | undefined = transport.protocol;\n\n\tconst discovery = props?.discovery ?? true;\n\n\tif (protocol === Ietf.ALPN.DRAFT_20) {\n\t\treturn acceptAlpn(transport, url, Ietf.Version.DRAFT_20, discovery);\n\t}\n\tif (protocol === Ietf.ALPN.DRAFT_19) {\n\t\treturn acceptAlpn(transport, url, Ietf.Version.DRAFT_19, discovery);\n\t} else if (protocol === Ietf.ALPN.DRAFT_18) {\n\t\treturn acceptAlpn(transport, url, Ietf.Version.DRAFT_18, discovery);\n\t} else if (protocol === Ietf.ALPN.DRAFT_17) {\n\t\treturn acceptAlpn(transport, url, Ietf.Version.DRAFT_17, discovery);\n\t} else if (protocol === Ietf.ALPN.DRAFT_16) {\n\t\treturn acceptSetup(transport, url, Ietf.Version.DRAFT_16, discovery);\n\t} else if (protocol === Ietf.ALPN.DRAFT_15) {\n\t\treturn acceptSetup(transport, url, Ietf.Version.DRAFT_15, discovery);\n\t} else if (protocol === Lite.ALPN_06_WIP) {\n\t\treturn new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_06, discovery });\n\t} else if (protocol === Lite.ALPN_05) {\n\t\treturn new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_05, discovery });\n\t} else if (protocol === Lite.ALPN_04) {\n\t\treturn new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_04, discovery });\n\t} else if (protocol === Lite.ALPN_03) {\n\t\treturn new Lite.Connection({ url, quic: transport, version: Lite.Version.DRAFT_03, discovery });\n\t} else if (protocol === Lite.ALPN || protocol === \"\" || protocol === undefined) {\n\t\treturn acceptNegotiated(transport, url, props);\n\t} else {\n\t\tthrow new Error(`unsupported WebTransport protocol: ${protocol}`);\n\t}\n}\n\n/**\n * Draft-17+ accept: ALPN already pinned the version. SETUP is exchanged over\n * a pair of uni streams using stream type 0x2F00.\n */\nasync function acceptAlpn(\n\ttransport: WebTransport,\n\turl: URL,\n\tversion: Ietf.IetfVersion,\n\tdiscovery: boolean,\n): Promise<Established> {\n\tconst { control, solicit, cluster } = await exchangeSetup(transport, version, \"moq-lite-js\");\n\n\treturn new Ietf.Connection({\n\t\tdiscovery,\n\t\tclient: false,\n\t\turl,\n\t\tquic: transport,\n\t\tcontrol,\n\t\tsolicit,\n\t\tcluster,\n\t\t// v17+ uses NativeSession which manages its own request IDs; maxRequestId is unused.\n\t\tmaxRequestId: 0n,\n\t\tversion,\n\t});\n}\n\n/**\n * Legacy accept (draft-15/16): ALPN pinned the version, but the SETUP message\n * is still exchanged over a bidi stream wrapped in the moq-lite compat envelope.\n */\nasync function acceptSetup(\n\ttransport: WebTransport,\n\turl: URL,\n\tversion: Ietf.IetfVersion,\n\tdiscovery: boolean,\n): Promise<Established> {\n\t// Accept bidi, read ClientSetup, write ServerSetup\n\tconst stream = await Stream.accept(transport);\n\tif (!stream) throw new Error(\"no incoming bidi stream for SETUP\");\n\n\tconst clientCompat = await stream.reader.u53();\n\tif (clientCompat !== Lite.StreamId.ClientCompat) {\n\t\tthrow new Error(`unexpected client message type: 0x${clientCompat.toString(16)}`);\n\t}\n\n\tconst client = await Ietf.ClientSetup.decode(stream.reader, version);\n\n\tawait stream.writer.u53(Lite.StreamId.ServerCompat);\n\n\tconst encoder = new TextEncoder();\n\tconst params = new Ietf.SetupOptions();\n\tparams.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);\n\tparams.setBytes(Ietf.SetupOption.Implementation, encoder.encode(\"moq-lite-js\"));\n\tIetf.solicitIntoSetup(params);\n\n\tconst server = new Ietf.ServerSetup({ version, parameters: params });\n\tawait server.encode(stream.writer, version);\n\n\tconst maxRequestId = 42069n;\n\n\treturn new Ietf.Connection({\n\t\tdiscovery,\n\t\tclient: false,\n\t\turl,\n\t\tquic: transport,\n\t\tcontrol: stream,\n\t\tmaxRequestId,\n\t\tversion,\n\t\tsolicit: Ietf.solicitFromSetup(client.parameters),\n\t});\n}\n\nasync function acceptNegotiated(transport: WebTransport, url: URL, props?: AcceptProps): Promise<Established> {\n\tconst discovery = props?.discovery ?? true;\n\tconst setupVersion = Ietf.Version.DRAFT_14;\n\n\tconst stream = await Stream.accept(transport);\n\tif (!stream) throw new Error(\"no incoming bidi stream for SETUP\");\n\n\tconst clientCompat = await stream.reader.u53();\n\tif (clientCompat !== Lite.StreamId.ClientCompat) {\n\t\tthrow new Error(`unexpected client message type: 0x${clientCompat.toString(16)}`);\n\t}\n\n\tconst client = await Ietf.ClientSetup.decode(stream.reader, setupVersion);\n\n\t// Pick the requested version, or first matching version from client's list\n\tconst allVersions = [...Object.values(Lite.Version), ...Object.values(Ietf.Version)] as number[];\n\tlet selectedVersion: number;\n\tif (props?.version !== undefined) {\n\t\tselectedVersion = props.version;\n\t} else {\n\t\tconst match = client.versions.find((v) => allVersions.includes(v));\n\t\tif (match === undefined) {\n\t\t\tthrow new Error(\n\t\t\t\t`no common version found; client offered: ${client.versions.map((v) => v.toString(16)).join(\", \")}`,\n\t\t\t);\n\t\t}\n\t\tselectedVersion = match;\n\t}\n\n\tawait stream.writer.u53(Lite.StreamId.ServerCompat);\n\n\tconst encoder = new TextEncoder();\n\tconst params = new Ietf.SetupOptions();\n\tparams.setVarint(Ietf.SetupOption.MaxRequestId, 42069n);\n\tparams.setBytes(Ietf.SetupOption.Implementation, encoder.encode(\"moq-lite-js\"));\n\tIetf.solicitIntoSetup(params);\n\n\tconst server = new Ietf.ServerSetup({ version: selectedVersion, parameters: params });\n\tawait server.encode(stream.writer, setupVersion);\n\n\tif (Object.values(Lite.Version).includes(selectedVersion as Lite.Version)) {\n\t\treturn new Lite.Connection({\n\t\t\turl,\n\t\t\tquic: transport,\n\t\t\tversion: selectedVersion as Lite.Version,\n\t\t\tsession: stream,\n\t\t\tdiscovery,\n\t\t});\n\t} else if (Object.values(Ietf.Version).includes(selectedVersion as Ietf.Version)) {\n\t\tconst maxRequestId = client.parameters.getVarint(Ietf.SetupOption.MaxRequestId) ?? 0n;\n\t\treturn new Ietf.Connection({\n\t\t\tdiscovery,\n\t\t\tclient: false,\n\t\t\turl,\n\t\t\tquic: transport,\n\t\t\tcontrol: stream,\n\t\t\tmaxRequestId,\n\t\t\tversion: selectedVersion as Ietf.IetfVersion,\n\t\t\tsolicit: Ietf.solicitFromSetup(client.parameters),\n\t\t});\n\t} else {\n\t\tthrow new Error(`unsupported version: ${selectedVersion.toString(16)}`);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Returns whether a browser user agent has a usable WebTransport implementation. */
|
|
2
|
+
export declare function isWebTransportUserAgentSupported(userAgent: string): boolean;
|
|
3
|
+
/** Returns whether this runtime can connect with WebTransport. */
|
|
4
|
+
export declare function isWebTransportSupported(): boolean;
|
|
5
|
+
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/connection/browser.ts"],"names":[],"mappings":"AAEA,qFAAqF;AACrF,wBAAgB,gCAAgC,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAoB3E;AAED,kEAAkE;AAClE,wBAAgB,uBAAuB,IAAI,OAAO,CAIjD"}
|