@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.
Files changed (310) hide show
  1. package/README.md +1 -0
  2. package/announced.d.ts +111 -21
  3. package/announced.d.ts.map +1 -1
  4. package/announced.js +213 -40
  5. package/announced.js.map +1 -1
  6. package/broadcast.d.ts +80 -25
  7. package/broadcast.d.ts.map +1 -1
  8. package/broadcast.js +270 -49
  9. package/broadcast.js.map +1 -1
  10. package/connection/accept.d.ts +6 -0
  11. package/connection/accept.d.ts.map +1 -1
  12. package/connection/accept.js +42 -13
  13. package/connection/accept.js.map +1 -1
  14. package/connection/browser.d.ts +5 -0
  15. package/connection/browser.d.ts.map +1 -0
  16. package/connection/browser.js +31 -0
  17. package/connection/browser.js.map +1 -0
  18. package/connection/connect.d.ts +43 -1
  19. package/connection/connect.d.ts.map +1 -1
  20. package/connection/connect.js +118 -123
  21. package/connection/connect.js.map +1 -1
  22. package/connection/established.d.ts +43 -16
  23. package/connection/established.d.ts.map +1 -1
  24. package/connection/established.js.map +1 -1
  25. package/connection/handshake.d.ts +12 -1
  26. package/connection/handshake.d.ts.map +1 -1
  27. package/connection/handshake.js +30 -14
  28. package/connection/handshake.js.map +1 -1
  29. package/connection/index.d.ts +8 -0
  30. package/connection/index.d.ts.map +1 -1
  31. package/connection/index.js +6 -0
  32. package/connection/index.js.map +1 -1
  33. package/connection/reload.d.ts +66 -9
  34. package/connection/reload.d.ts.map +1 -1
  35. package/connection/reload.js +192 -66
  36. package/connection/reload.js.map +1 -1
  37. package/connection/stats.d.ts +92 -0
  38. package/connection/stats.d.ts.map +1 -0
  39. package/connection/stats.js +35 -0
  40. package/connection/stats.js.map +1 -0
  41. package/connection/transport.d.ts +10 -0
  42. package/connection/transport.d.ts.map +1 -0
  43. package/connection/transport.js +12 -0
  44. package/connection/transport.js.map +1 -0
  45. package/consume.d.ts +44 -0
  46. package/consume.d.ts.map +1 -0
  47. package/consume.js +60 -0
  48. package/consume.js.map +1 -0
  49. package/datagram.d.ts +23 -0
  50. package/datagram.d.ts.map +1 -0
  51. package/datagram.js +3 -0
  52. package/datagram.js.map +1 -0
  53. package/error.d.ts +76 -0
  54. package/error.d.ts.map +1 -0
  55. package/error.js +107 -0
  56. package/error.js.map +1 -0
  57. package/group.d.ts +143 -38
  58. package/group.d.ts.map +1 -1
  59. package/group.js +270 -68
  60. package/group.js.map +1 -1
  61. package/ietf/adapter.d.ts +2 -2
  62. package/ietf/adapter.d.ts.map +1 -1
  63. package/ietf/adapter.js +20 -10
  64. package/ietf/adapter.js.map +1 -1
  65. package/ietf/aliases.d.ts +73 -5
  66. package/ietf/aliases.d.ts.map +1 -1
  67. package/ietf/aliases.js +121 -14
  68. package/ietf/aliases.js.map +1 -1
  69. package/ietf/cluster.d.ts +141 -0
  70. package/ietf/cluster.d.ts.map +1 -0
  71. package/ietf/cluster.js +204 -0
  72. package/ietf/cluster.js.map +1 -0
  73. package/ietf/connection.d.ts +39 -6
  74. package/ietf/connection.d.ts.map +1 -1
  75. package/ietf/connection.js +73 -12
  76. package/ietf/connection.js.map +1 -1
  77. package/ietf/fetch.d.ts +17 -0
  78. package/ietf/fetch.d.ts.map +1 -1
  79. package/ietf/fetch.js +21 -0
  80. package/ietf/fetch.js.map +1 -1
  81. package/ietf/filter.d.ts +87 -0
  82. package/ietf/filter.d.ts.map +1 -0
  83. package/ietf/filter.js +292 -0
  84. package/ietf/filter.js.map +1 -0
  85. package/ietf/index.d.ts +2 -0
  86. package/ietf/index.d.ts.map +1 -1
  87. package/ietf/index.js +3 -0
  88. package/ietf/index.js.map +1 -1
  89. package/ietf/namespace.d.ts +6 -0
  90. package/ietf/namespace.d.ts.map +1 -1
  91. package/ietf/namespace.js +27 -2
  92. package/ietf/namespace.js.map +1 -1
  93. package/ietf/object.d.ts +54 -6
  94. package/ietf/object.d.ts.map +1 -1
  95. package/ietf/object.js +178 -25
  96. package/ietf/object.js.map +1 -1
  97. package/ietf/parameters.d.ts +35 -10
  98. package/ietf/parameters.d.ts.map +1 -1
  99. package/ietf/parameters.js +236 -45
  100. package/ietf/parameters.js.map +1 -1
  101. package/ietf/priority.d.ts +5 -0
  102. package/ietf/priority.d.ts.map +1 -0
  103. package/ietf/priority.js +10 -0
  104. package/ietf/priority.js.map +1 -0
  105. package/ietf/properties.d.ts +13 -2
  106. package/ietf/properties.d.ts.map +1 -1
  107. package/ietf/properties.js +65 -8
  108. package/ietf/properties.js.map +1 -1
  109. package/ietf/publish.d.ts.map +1 -1
  110. package/ietf/publish.js +13 -3
  111. package/ietf/publish.js.map +1 -1
  112. package/ietf/publish_namespace.d.ts +16 -2
  113. package/ietf/publish_namespace.d.ts.map +1 -1
  114. package/ietf/publish_namespace.js +25 -6
  115. package/ietf/publish_namespace.js.map +1 -1
  116. package/ietf/publisher.d.ts +40 -7
  117. package/ietf/publisher.d.ts.map +1 -1
  118. package/ietf/publisher.js +751 -134
  119. package/ietf/publisher.js.map +1 -1
  120. package/ietf/request.js +1 -1
  121. package/ietf/request.js.map +1 -1
  122. package/ietf/solicit.d.ts +41 -0
  123. package/ietf/solicit.d.ts.map +1 -0
  124. package/ietf/solicit.js +47 -0
  125. package/ietf/solicit.js.map +1 -0
  126. package/ietf/subscribe.d.ts +41 -2
  127. package/ietf/subscribe.d.ts.map +1 -1
  128. package/ietf/subscribe.js +103 -22
  129. package/ietf/subscribe.js.map +1 -1
  130. package/ietf/subscribe_namespace.d.ts +21 -7
  131. package/ietf/subscribe_namespace.d.ts.map +1 -1
  132. package/ietf/subscribe_namespace.js +31 -14
  133. package/ietf/subscribe_namespace.js.map +1 -1
  134. package/ietf/subscriber.d.ts +20 -7
  135. package/ietf/subscriber.d.ts.map +1 -1
  136. package/ietf/subscriber.js +512 -142
  137. package/ietf/subscriber.js.map +1 -1
  138. package/ietf/version.d.ts +7 -1
  139. package/ietf/version.d.ts.map +1 -1
  140. package/ietf/version.js +7 -0
  141. package/ietf/version.js.map +1 -1
  142. package/index.d.ts +10 -5
  143. package/index.d.ts.map +1 -1
  144. package/index.js +10 -5
  145. package/index.js.map +1 -1
  146. package/internal.d.ts +29 -0
  147. package/internal.d.ts.map +1 -0
  148. package/internal.js +8 -0
  149. package/internal.js.map +1 -0
  150. package/lite/announce.d.ts +62 -15
  151. package/lite/announce.d.ts.map +1 -1
  152. package/lite/announce.js +230 -94
  153. package/lite/announce.js.map +1 -1
  154. package/lite/connection.d.ts +69 -20
  155. package/lite/connection.d.ts.map +1 -1
  156. package/lite/connection.js +128 -81
  157. package/lite/connection.js.map +1 -1
  158. package/lite/datagram.d.ts +23 -0
  159. package/lite/datagram.d.ts.map +1 -0
  160. package/lite/datagram.js +57 -0
  161. package/lite/datagram.js.map +1 -0
  162. package/lite/datagram_stream.d.ts +8 -0
  163. package/lite/datagram_stream.d.ts.map +1 -0
  164. package/lite/datagram_stream.js +44 -0
  165. package/lite/datagram_stream.js.map +1 -0
  166. package/lite/fetch.d.ts.map +1 -1
  167. package/lite/fetch.js +2 -2
  168. package/lite/fetch.js.map +1 -1
  169. package/lite/index.d.ts +3 -0
  170. package/lite/index.d.ts.map +1 -1
  171. package/lite/index.js +3 -0
  172. package/lite/index.js.map +1 -1
  173. package/lite/priority.d.ts +67 -0
  174. package/lite/priority.d.ts.map +1 -0
  175. package/lite/priority.js +114 -0
  176. package/lite/priority.js.map +1 -0
  177. package/lite/probe.d.ts +18 -2
  178. package/lite/probe.d.ts.map +1 -1
  179. package/lite/probe.js +24 -22
  180. package/lite/probe.js.map +1 -1
  181. package/lite/publisher.d.ts +14 -7
  182. package/lite/publisher.d.ts.map +1 -1
  183. package/lite/publisher.js +406 -84
  184. package/lite/publisher.js.map +1 -1
  185. package/lite/setup.d.ts +83 -6
  186. package/lite/setup.d.ts.map +1 -1
  187. package/lite/setup.js +203 -31
  188. package/lite/setup.js.map +1 -1
  189. package/lite/stream.d.ts +12 -3
  190. package/lite/stream.d.ts.map +1 -1
  191. package/lite/stream.js +7 -4
  192. package/lite/stream.js.map +1 -1
  193. package/lite/subscribe.d.ts +37 -23
  194. package/lite/subscribe.d.ts.map +1 -1
  195. package/lite/subscribe.js +70 -55
  196. package/lite/subscribe.js.map +1 -1
  197. package/lite/subscriber.d.ts +29 -23
  198. package/lite/subscriber.d.ts.map +1 -1
  199. package/lite/subscriber.js +689 -112
  200. package/lite/subscriber.js.map +1 -1
  201. package/lite/track.d.ts +25 -13
  202. package/lite/track.d.ts.map +1 -1
  203. package/lite/track.js +60 -35
  204. package/lite/track.js.map +1 -1
  205. package/lite/version.d.ts +39 -4
  206. package/lite/version.d.ts.map +1 -1
  207. package/lite/version.js +139 -17
  208. package/lite/version.js.map +1 -1
  209. package/mock.d.ts +53 -3
  210. package/mock.d.ts.map +1 -1
  211. package/mock.js +110 -21
  212. package/mock.js.map +1 -1
  213. package/{lite/origin.d.ts → origin.d.ts} +25 -1
  214. package/origin.d.ts.map +1 -0
  215. package/{lite/origin.js → origin.js} +25 -1
  216. package/origin.js.map +1 -0
  217. package/package.json +6 -4
  218. package/path.d.ts +67 -0
  219. package/path.d.ts.map +1 -1
  220. package/path.js +128 -0
  221. package/path.js.map +1 -1
  222. package/stream.d.ts +94 -4
  223. package/stream.d.ts.map +1 -1
  224. package/stream.js +128 -17
  225. package/stream.js.map +1 -1
  226. package/time.d.ts +109 -86
  227. package/time.d.ts.map +1 -1
  228. package/time.js +80 -30
  229. package/time.js.map +1 -1
  230. package/track.d.ts +298 -47
  231. package/track.d.ts.map +1 -1
  232. package/track.js +721 -109
  233. package/track.js.map +1 -1
  234. package/util/timeout.d.ts +5 -0
  235. package/util/timeout.d.ts.map +1 -0
  236. package/util/timeout.js +18 -0
  237. package/util/timeout.js.map +1 -0
  238. package/util/utf8.d.ts +3 -0
  239. package/util/utf8.d.ts.map +1 -0
  240. package/util/utf8.js +7 -0
  241. package/util/utf8.js.map +1 -0
  242. package/varint.d.ts +14 -3
  243. package/varint.d.ts.map +1 -1
  244. package/varint.js +23 -12
  245. package/varint.js.map +1 -1
  246. package/zod.d.ts +5 -5
  247. package/zod.d.ts.map +1 -1
  248. package/zod.js.map +1 -1
  249. package/bandwidth.d.ts +0 -11
  250. package/bandwidth.d.ts.map +0 -1
  251. package/bandwidth.js +0 -7
  252. package/bandwidth.js.map +0 -1
  253. package/connection/cert.test.d.ts +0 -2
  254. package/connection/cert.test.d.ts.map +0 -1
  255. package/connection/cert.test.js +0 -26
  256. package/connection/cert.test.js.map +0 -1
  257. package/connection/reload.test.d.ts +0 -2
  258. package/connection/reload.test.d.ts.map +0 -1
  259. package/connection/reload.test.js +0 -39
  260. package/connection/reload.test.js.map +0 -1
  261. package/group.test.d.ts +0 -2
  262. package/group.test.d.ts.map +0 -1
  263. package/group.test.js +0 -66
  264. package/group.test.js.map +0 -1
  265. package/ietf/aliases.test.d.ts +0 -2
  266. package/ietf/aliases.test.d.ts.map +0 -1
  267. package/ietf/aliases.test.js +0 -30
  268. package/ietf/aliases.test.js.map +0 -1
  269. package/ietf/ietf.test.d.ts +0 -2
  270. package/ietf/ietf.test.d.ts.map +0 -1
  271. package/ietf/ietf.test.js +0 -873
  272. package/ietf/ietf.test.js.map +0 -1
  273. package/integration.test.d.ts +0 -2
  274. package/integration.test.d.ts.map +0 -1
  275. package/integration.test.js +0 -90
  276. package/integration.test.js.map +0 -1
  277. package/lite/origin.d.ts.map +0 -1
  278. package/lite/origin.js.map +0 -1
  279. package/lite/subscriber.test.d.ts +0 -2
  280. package/lite/subscriber.test.d.ts.map +0 -1
  281. package/lite/subscriber.test.js +0 -30
  282. package/lite/subscriber.test.js.map +0 -1
  283. package/lite/track.test.d.ts +0 -2
  284. package/lite/track.test.d.ts.map +0 -1
  285. package/lite/track.test.js +0 -53
  286. package/lite/track.test.js.map +0 -1
  287. package/path.test.d.ts +0 -2
  288. package/path.test.d.ts.map +0 -1
  289. package/path.test.js +0 -169
  290. package/path.test.js.map +0 -1
  291. package/stream.test.d.ts +0 -2
  292. package/stream.test.d.ts.map +0 -1
  293. package/stream.test.js +0 -210
  294. package/stream.test.js.map +0 -1
  295. package/track.test.d.ts +0 -2
  296. package/track.test.d.ts.map +0 -1
  297. package/track.test.js +0 -56
  298. package/track.test.js.map +0 -1
  299. package/util/error.d.ts +0 -3
  300. package/util/error.d.ts.map +0 -1
  301. package/util/error.js +0 -9
  302. package/util/error.js.map +0 -1
  303. package/util/index.d.ts +0 -2
  304. package/util/index.d.ts.map +0 -1
  305. package/util/index.js +0 -3
  306. package/util/index.js.map +0 -1
  307. package/varint.test.d.ts +0 -2
  308. package/varint.test.d.ts.map +0 -1
  309. package/varint.test.js +0 -106
  310. package/varint.test.js.map +0 -1
package/broadcast.js CHANGED
@@ -1,76 +1,297 @@
1
1
  /* @ts-self-types="./broadcast.d.ts" */
2
- import { Signal } from "@norskvideo/moq-signals";
3
- import { Track } from "./track.js";
4
- /** Reactive backing state for a {@link Broadcast}: requested tracks plus a closed flag. */
5
- export class BroadcastState {
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 Signal(false);
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
- * Handles writing and managing tracks in a broadcast.
121
+ * The write side of a broadcast.
11
122
  *
12
123
  * @public
13
124
  */
14
- export class Broadcast {
15
- /** Reactive backing state. */
16
- state = new BroadcastState();
17
- /** Resolves with the abort error (or undefined) once closed. */
18
- closed;
19
- constructor() {
20
- this.closed = new Promise((resolve) => {
21
- const dispose = this.state.closed.subscribe((closed) => {
22
- if (!closed)
23
- return;
24
- resolve(closed instanceof Error ? closed : undefined);
25
- dispose();
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
- * A track requested over the network.
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
- // We use pop instead of shift because it's slightly more efficient.
35
- const track = this.state.requested.peek().pop();
36
- if (track)
37
- return track;
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.state.requested, this.state.closed);
266
+ await Signal.race(this.#state.requested, this.#state.closed);
44
267
  }
45
268
  }
46
269
  /**
47
- * Populates the provided track over the network.
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
- subscribe(name, priority) {
50
- const track = new Track(name);
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
- * Closes the writer and all associated tracks.
63
- *
64
- * @param abort - If provided, throw this exception instead of returning undefined.
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.state.closed.set(abort ?? true);
68
- for (const { track } of this.state.requested.peek()) {
69
- track.close(abort);
70
- }
71
- this.state.requested.mutate((requested) => {
72
- requested.length = 0;
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":"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","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"]}
@@ -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;IAE3B,OAAO,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAAC,SAAS,EAAE,YAAY,EAAE,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAuBzG"}
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"}
@@ -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, undefined);
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, undefined);
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 controlStream = await exchangeSetup(transport, version, "moq-lite-js");
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: controlStream,
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(url, transport, selectedVersion, stream);
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 {
@@ -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"}