@norskvideo/moq-net 0.1.8 → 0.2.1

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 (286) hide show
  1. package/README.md +2 -2
  2. package/announce.d.ts +7 -0
  3. package/announce.d.ts.map +1 -0
  4. package/announce.js +8 -0
  5. package/announce.js.map +1 -0
  6. package/announced.d.ts +49 -91
  7. package/announced.d.ts.map +1 -1
  8. package/announced.js +21 -156
  9. package/announced.js.map +1 -1
  10. package/bandwidth.d.ts +163 -0
  11. package/bandwidth.d.ts.map +1 -0
  12. package/bandwidth.js +304 -0
  13. package/bandwidth.js.map +1 -0
  14. package/bandwidth_api.d.ts +7 -0
  15. package/bandwidth_api.d.ts.map +1 -0
  16. package/bandwidth_api.js +8 -0
  17. package/bandwidth_api.js.map +1 -0
  18. package/broadcast.d.ts +44 -35
  19. package/broadcast.d.ts.map +1 -1
  20. package/broadcast.js +104 -60
  21. package/broadcast.js.map +1 -1
  22. package/connection/accept.d.ts +16 -1
  23. package/connection/accept.d.ts.map +1 -1
  24. package/connection/accept.js +52 -28
  25. package/connection/accept.js.map +1 -1
  26. package/connection/browser.d.ts.map +1 -1
  27. package/connection/browser.js +9 -7
  28. package/connection/browser.js.map +1 -1
  29. package/connection/connect.d.ts +30 -6
  30. package/connection/connect.d.ts.map +1 -1
  31. package/connection/connect.js +110 -54
  32. package/connection/connect.js.map +1 -1
  33. package/connection/established.d.ts +17 -21
  34. package/connection/established.d.ts.map +1 -1
  35. package/connection/established.js.map +1 -1
  36. package/connection/forward.d.ts +2 -0
  37. package/connection/forward.d.ts.map +1 -0
  38. package/connection/forward.js +173 -0
  39. package/connection/forward.js.map +1 -0
  40. package/connection/handshake.d.ts +1 -0
  41. package/connection/handshake.d.ts.map +1 -1
  42. package/connection/handshake.js +5 -2
  43. package/connection/handshake.js.map +1 -1
  44. package/connection/index.d.ts +5 -5
  45. package/connection/index.d.ts.map +1 -1
  46. package/connection/index.js +4 -5
  47. package/connection/index.js.map +1 -1
  48. package/connection/pool.d.ts +186 -0
  49. package/connection/pool.d.ts.map +1 -0
  50. package/connection/pool.js +361 -0
  51. package/connection/pool.js.map +1 -0
  52. package/connection/reload.d.ts +14 -93
  53. package/connection/reload.d.ts.map +1 -1
  54. package/connection/reload.js +217 -81
  55. package/connection/reload.js.map +1 -1
  56. package/connection/stats.d.ts +3 -26
  57. package/connection/stats.d.ts.map +1 -1
  58. package/connection/stats.js.map +1 -1
  59. package/connection/transport.d.ts +0 -7
  60. package/connection/transport.d.ts.map +1 -1
  61. package/consume.d.ts +1 -43
  62. package/consume.d.ts.map +1 -1
  63. package/consume.js +1 -1
  64. package/consume.js.map +1 -1
  65. package/error.d.ts +180 -29
  66. package/error.d.ts.map +1 -1
  67. package/error.js +331 -16
  68. package/error.js.map +1 -1
  69. package/errors.d.ts +7 -0
  70. package/errors.d.ts.map +1 -0
  71. package/errors.js +8 -0
  72. package/errors.js.map +1 -0
  73. package/group.d.ts +9 -43
  74. package/group.d.ts.map +1 -1
  75. package/group.js +284 -69
  76. package/group.js.map +1 -1
  77. package/hop.d.ts +115 -0
  78. package/hop.d.ts.map +1 -0
  79. package/hop.js +119 -0
  80. package/hop.js.map +1 -0
  81. package/ietf/adapter.d.ts +5 -1
  82. package/ietf/adapter.d.ts.map +1 -1
  83. package/ietf/adapter.js +105 -60
  84. package/ietf/adapter.js.map +1 -1
  85. package/ietf/aliases.d.ts +1 -78
  86. package/ietf/aliases.d.ts.map +1 -1
  87. package/ietf/cluster.d.ts +9 -123
  88. package/ietf/cluster.d.ts.map +1 -1
  89. package/ietf/cluster.js +84 -44
  90. package/ietf/cluster.js.map +1 -1
  91. package/ietf/connection.d.ts +6 -72
  92. package/ietf/connection.d.ts.map +1 -1
  93. package/ietf/connection.js +62 -56
  94. package/ietf/connection.js.map +1 -1
  95. package/ietf/error.d.ts +11 -0
  96. package/ietf/error.d.ts.map +1 -0
  97. package/ietf/error.js +193 -0
  98. package/ietf/error.js.map +1 -0
  99. package/ietf/fetch.d.ts +7 -20
  100. package/ietf/fetch.d.ts.map +1 -1
  101. package/ietf/fetch.js +52 -22
  102. package/ietf/fetch.js.map +1 -1
  103. package/ietf/filter.d.ts +2 -0
  104. package/ietf/filter.d.ts.map +1 -1
  105. package/ietf/filter.js +10 -1
  106. package/ietf/filter.js.map +1 -1
  107. package/ietf/goaway.d.ts.map +1 -1
  108. package/ietf/goaway.js +22 -5
  109. package/ietf/goaway.js.map +1 -1
  110. package/ietf/hidden.d.ts +2 -0
  111. package/ietf/hidden.d.ts.map +1 -0
  112. package/ietf/hidden.js +30 -0
  113. package/ietf/hidden.js.map +1 -0
  114. package/ietf/index.d.ts +2 -0
  115. package/ietf/index.d.ts.map +1 -1
  116. package/ietf/index.js +2 -0
  117. package/ietf/index.js.map +1 -1
  118. package/ietf/object.d.ts +15 -8
  119. package/ietf/object.d.ts.map +1 -1
  120. package/ietf/object.js +51 -36
  121. package/ietf/object.js.map +1 -1
  122. package/ietf/parameters.d.ts +14 -2
  123. package/ietf/parameters.d.ts.map +1 -1
  124. package/ietf/parameters.js +97 -29
  125. package/ietf/parameters.js.map +1 -1
  126. package/ietf/properties.d.ts +1 -0
  127. package/ietf/properties.d.ts.map +1 -1
  128. package/ietf/properties.js +14 -0
  129. package/ietf/properties.js.map +1 -1
  130. package/ietf/publish.d.ts +19 -2
  131. package/ietf/publish.d.ts.map +1 -1
  132. package/ietf/publish.js +40 -6
  133. package/ietf/publish.js.map +1 -1
  134. package/ietf/publish_namespace.d.ts +25 -0
  135. package/ietf/publish_namespace.d.ts.map +1 -1
  136. package/ietf/publish_namespace.js +63 -0
  137. package/ietf/publish_namespace.js.map +1 -1
  138. package/ietf/publisher.d.ts +1 -82
  139. package/ietf/publisher.d.ts.map +1 -1
  140. package/ietf/publisher.js +483 -235
  141. package/ietf/publisher.js.map +1 -1
  142. package/ietf/solicit.d.ts +1 -40
  143. package/ietf/solicit.d.ts.map +1 -1
  144. package/ietf/subscribe.d.ts +8 -6
  145. package/ietf/subscribe.d.ts.map +1 -1
  146. package/ietf/subscribe.js +33 -27
  147. package/ietf/subscribe.js.map +1 -1
  148. package/ietf/subscribe_namespace.d.ts +8 -2
  149. package/ietf/subscribe_namespace.d.ts.map +1 -1
  150. package/ietf/subscribe_namespace.js +18 -8
  151. package/ietf/subscribe_namespace.js.map +1 -1
  152. package/ietf/subscriber.d.ts +1 -65
  153. package/ietf/subscriber.d.ts.map +1 -1
  154. package/ietf/subscriber.js +337 -123
  155. package/ietf/subscriber.js.map +1 -1
  156. package/ietf/token.d.ts +2 -0
  157. package/ietf/token.d.ts.map +1 -0
  158. package/ietf/token.js +99 -0
  159. package/ietf/token.js.map +1 -0
  160. package/ietf/track.d.ts +4 -0
  161. package/ietf/track.d.ts.map +1 -1
  162. package/ietf/track.js +6 -20
  163. package/ietf/track.js.map +1 -1
  164. package/ietf/version.d.ts +12 -1
  165. package/ietf/version.d.ts.map +1 -1
  166. package/ietf/version.js +13 -0
  167. package/ietf/version.js.map +1 -1
  168. package/index.d.ts +12 -7
  169. package/index.d.ts.map +1 -1
  170. package/index.js +10 -5
  171. package/index.js.map +1 -1
  172. package/internal.d.ts +115 -1
  173. package/internal.d.ts.map +1 -1
  174. package/internal.js +108 -0
  175. package/internal.js.map +1 -1
  176. package/lite/announce.d.ts +60 -10
  177. package/lite/announce.d.ts.map +1 -1
  178. package/lite/announce.js +176 -31
  179. package/lite/announce.js.map +1 -1
  180. package/lite/connection.d.ts +9 -59
  181. package/lite/connection.d.ts.map +1 -1
  182. package/lite/connection.js +34 -35
  183. package/lite/connection.js.map +1 -1
  184. package/lite/datagram.d.ts +3 -2
  185. package/lite/datagram.d.ts.map +1 -1
  186. package/lite/datagram.js +7 -8
  187. package/lite/datagram.js.map +1 -1
  188. package/lite/fetch.d.ts +15 -1
  189. package/lite/fetch.d.ts.map +1 -1
  190. package/lite/fetch.js +39 -7
  191. package/lite/fetch.js.map +1 -1
  192. package/lite/goaway.d.ts.map +1 -1
  193. package/lite/goaway.js +7 -1
  194. package/lite/goaway.js.map +1 -1
  195. package/lite/group.d.ts +30 -12
  196. package/lite/group.d.ts.map +1 -1
  197. package/lite/group.js +68 -26
  198. package/lite/group.js.map +1 -1
  199. package/lite/message.d.ts +2 -2
  200. package/lite/message.d.ts.map +1 -1
  201. package/lite/message.js +14 -5
  202. package/lite/message.js.map +1 -1
  203. package/lite/priority.d.ts +1 -61
  204. package/lite/priority.d.ts.map +1 -1
  205. package/lite/priority.js +4 -5
  206. package/lite/priority.js.map +1 -1
  207. package/lite/publisher.d.ts +1 -69
  208. package/lite/publisher.d.ts.map +1 -1
  209. package/lite/publisher.js +630 -262
  210. package/lite/publisher.js.map +1 -1
  211. package/lite/setup.d.ts +8 -8
  212. package/lite/setup.d.ts.map +1 -1
  213. package/lite/setup.js +33 -30
  214. package/lite/setup.js.map +1 -1
  215. package/lite/subscribe.d.ts +71 -17
  216. package/lite/subscribe.d.ts.map +1 -1
  217. package/lite/subscribe.js +205 -53
  218. package/lite/subscribe.js.map +1 -1
  219. package/lite/subscriber.d.ts +15 -56
  220. package/lite/subscriber.d.ts.map +1 -1
  221. package/lite/subscriber.js +451 -247
  222. package/lite/subscriber.js.map +1 -1
  223. package/lite/track.d.ts +4 -10
  224. package/lite/track.d.ts.map +1 -1
  225. package/lite/track.js +34 -29
  226. package/lite/track.js.map +1 -1
  227. package/lite/version.d.ts +42 -6
  228. package/lite/version.d.ts.map +1 -1
  229. package/lite/version.js +126 -10
  230. package/lite/version.js.map +1 -1
  231. package/origin.d.ts +256 -29
  232. package/origin.d.ts.map +1 -1
  233. package/origin.js +1427 -37
  234. package/origin.js.map +1 -1
  235. package/package.json +8 -3
  236. package/path.d.ts +25 -7
  237. package/path.d.ts.map +1 -1
  238. package/path.js +5 -3
  239. package/path.js.map +1 -1
  240. package/stream.d.ts +73 -14
  241. package/stream.d.ts.map +1 -1
  242. package/stream.js +372 -141
  243. package/stream.js.map +1 -1
  244. package/tail.d.ts +18 -0
  245. package/tail.d.ts.map +1 -0
  246. package/tail.js +167 -0
  247. package/tail.js.map +1 -0
  248. package/time.d.ts +15 -2
  249. package/time.d.ts.map +1 -1
  250. package/time.js +28 -9
  251. package/time.js.map +1 -1
  252. package/track.d.ts +211 -83
  253. package/track.d.ts.map +1 -1
  254. package/track.js +816 -205
  255. package/track.js.map +1 -1
  256. package/util/abort.d.ts +2 -0
  257. package/util/abort.d.ts.map +1 -0
  258. package/util/abort.js +20 -0
  259. package/util/abort.js.map +1 -0
  260. package/util/log.d.ts +5 -0
  261. package/util/log.d.ts.map +1 -0
  262. package/util/log.js +17 -0
  263. package/util/log.js.map +1 -0
  264. package/util/u64.d.ts +39 -0
  265. package/util/u64.d.ts.map +1 -0
  266. package/util/u64.js +83 -0
  267. package/util/u64.js.map +1 -0
  268. package/util/varint.d.ts +29 -0
  269. package/util/varint.d.ts.map +1 -0
  270. package/util/varint.js +198 -0
  271. package/util/varint.js.map +1 -0
  272. package/varint.d.ts +10 -6
  273. package/varint.d.ts.map +1 -1
  274. package/varint.js +40 -237
  275. package/varint.js.map +1 -1
  276. package/wire.d.ts +80 -0
  277. package/wire.d.ts.map +1 -0
  278. package/wire.js +32 -0
  279. package/wire.js.map +1 -0
  280. package/zod.d.ts +1 -1
  281. package/zod.d.ts.map +1 -1
  282. package/zod.js.map +1 -1
  283. package/mock.d.ts +0 -66
  284. package/mock.d.ts.map +0 -1
  285. package/mock.js +0 -243
  286. package/mock.js.map +0 -1
package/error.js CHANGED
@@ -1,41 +1,209 @@
1
1
  /* @ts-self-types="./error.d.ts" */
2
2
  /**
3
- * Errors, including the code a peer reports when it resets a stream.
3
+ * Errors, including the code a peer reports when it resets a stream or closes the session.
4
4
  *
5
5
  * @module
6
6
  */
7
+ import { sharedStreamCode } from "./ietf/error.js";
8
+ import { TimeoutError } from "./util/timeout.js";
7
9
  /**
8
- * An error the peer reported by resetting a stream, carrying the raw code it sent.
10
+ * Codes a peer sends when terminating the session, mirroring the Rust `Session`.
9
11
  *
10
- * The codes are not standardized, so this deliberately does not translate one into a local
11
- * error: the number means whatever the peer's implementation says it means. A read or write
12
- * rejects with this on every transport, so branch on {@link code} rather than feature-detecting
12
+ * Specified by moq-lite, which reuses moq-transport's codes unchanged. Call `SessionCode(code)`
13
+ * to construct an application code in the 64+ range. {@link StreamCode} is the other registry,
14
+ * and the two are disjoint, so the same integer means different things in each.
15
+ *
16
+ * Codes 32-47 are reserved: nothing is sent there, and the draft gives a received one no
17
+ * meaning, so treat anything not listed below as an unspecified error rather than guessing.
18
+ *
19
+ * @public
20
+ */
21
+ export const SessionCode = Object.freeze(Object.assign((code) => applicationCode(code), {
22
+ /** Ending the session normally, with no error. */
23
+ Cancel: 0x0,
24
+ /** Something went wrong that isn't worth a dedicated code. */
25
+ Internal: 0x1,
26
+ /** The credentials don't grant the requested path or operation. Retrying will fail again. */
27
+ Unauthorized: 0x2,
28
+ /** A protocol rule was broken; the session is unusable. */
29
+ ProtocolViolation: 0x3,
30
+ /** A key-value pair was malformed or repeated more than allowed. */
31
+ KeyValueFormatting: 0x6,
32
+ /** The peer did not close within the GOAWAY drain deadline. */
33
+ GoawayTimeout: 0x10,
34
+ /** A control message took too long. */
35
+ Timeout: 0x11,
36
+ /** No version could be negotiated. */
37
+ Version: 0x15,
38
+ }));
39
+ /**
40
+ * Codes a peer sends when resetting a stream, mirroring the Rust `Stream`.
41
+ *
42
+ * The counterpart to {@link SessionCode}, and a disjoint space: a stream reset of 0 is
43
+ * {@link StreamCode.Internal}, not a cancellation ({@link StreamCode.Cancel} is 1). Call
44
+ * `StreamCode(code)` to construct an application code in the 64+ range.
45
+ *
46
+ * Conditions the shared codes don't cover are assigned in moq-lite's own 48-63 range, so a
47
+ * received one is the named code. 32 through 47 is reserved: nothing is sent there and a
48
+ * received one is an unspecified error.
49
+ *
50
+ * @public
51
+ */
52
+ export const StreamCode = Object.freeze(Object.assign((code) => applicationCode(code), {
53
+ /** Something went wrong that isn't worth a dedicated code. */
54
+ Internal: 0x0,
55
+ /** The sender is done with this stream, not failing. A routine unsubscribe. */
56
+ Cancel: 0x1,
57
+ /** The content missed its delivery deadline. */
58
+ DeliveryTimeout: 0x2,
59
+ /** The session ended, taking this stream with it. */
60
+ SessionClosed: 0x3,
61
+ /** The session is going away (a GOAWAY was received). */
62
+ GoingAway: 0x4,
63
+ /** The reader fell too far behind and content was dropped to catch up. */
64
+ TooFarBehind: 0x5,
65
+ /** The track's content could not be parsed. */
66
+ MalformedTrack: 0x12,
67
+ /** The peer took too long to answer a control request. */
68
+ ControlTimeout: 0x31,
69
+ /** The requested broadcast or track does not exist at the peer. */
70
+ NotFound: 0x33,
71
+ /** The group was superseded by a newer one and dropped. */
72
+ Old: 0x34,
73
+ /** The group was dropped under memory pressure, so it can be re-fetched. */
74
+ Evicted: 0x35,
75
+ /** A frame declared a payload larger than the receiver accepts. */
76
+ FrameTooLarge: 0x38,
77
+ /** The publisher could serve this request but has no capacity for it now. */
78
+ NoCapacity: 0x30,
79
+ /** A group grew past its cache budget and was aborted. */
80
+ GroupTooLarge: 0x32,
81
+ }));
82
+ function applicationCode(code) {
83
+ if (!Number.isInteger(code) || code < 64 || code > 0xffffffff) {
84
+ throw new RangeError(`invalid application error code: ${code}`);
85
+ }
86
+ return code;
87
+ }
88
+ /**
89
+ * An error the peer reported by closing the session, carrying its {@link SessionCode}.
90
+ *
91
+ * This surfaces on every transport, so catch this type rather than feature-detecting
13
92
  * `WebTransportError`, which a non-browser runtime never defines and the WebSocket fallback
14
93
  * never throws.
15
94
  *
16
- * Code 0 is what a transport sends when a stream is dropped or aborted with no code of its own.
95
+ * ```ts
96
+ * connection.error.subscribe((err) => {
97
+ * if (err instanceof Session && err.code === SessionCode.Unauthorized) {
98
+ * console.warn("server rejected the session");
99
+ * }
100
+ * });
101
+ * ```
102
+ *
103
+ * @public
104
+ */
105
+ export class Session extends Error {
106
+ /** The session code the peer sent, verbatim. */
107
+ code;
108
+ constructor(code, options) {
109
+ super(options?.reason ? `remote error: ${code} (${options.reason})` : `remote error: ${code}`, options);
110
+ this.name = "Session";
111
+ this.code = code;
112
+ }
113
+ }
114
+ /**
115
+ * An error carrying a {@link StreamCode}: the code a peer sent when it reset a stream, or the
116
+ * one this side sends when it resets one.
117
+ *
118
+ * This surfaces on every transport, so catch this type rather than feature-detecting
119
+ * `WebTransportError`, which a non-browser runtime never defines and the WebSocket fallback
120
+ * never throws. Local conditions with a code of their own subclass it ({@link TooFarBehind},
121
+ * {@link FrameTooLarge}, {@link GroupTooLarge}, {@link NotFound}), so the same `code` check catches a condition
122
+ * whether it was raised here or reported by the peer.
17
123
  *
18
124
  * ```ts
19
125
  * try {
20
126
  * frame = await group.readFrame();
21
127
  * } catch (err) {
22
- * // Whatever this peer's code 2 means to it.
23
- * if (err instanceof RemoteError && err.code === 2) return;
128
+ * if (err instanceof Stream && err.code === StreamCode.Cancel) return;
24
129
  * throw err;
25
130
  * }
26
131
  * ```
27
132
  *
28
133
  * @public
29
134
  */
30
- export class RemoteError extends Error {
31
- /** The code the peer sent, verbatim. */
135
+ export class Stream extends Error {
136
+ /** The stream code; moq-lite resets forward it verbatim. */
32
137
  code;
33
138
  constructor(code, options) {
34
- super(`remote error: ${code}`, options);
35
- this.name = "RemoteError";
139
+ super(options?.message ??
140
+ (options?.reason ? `remote error: ${code} (${options.reason})` : `remote error: ${code}`), options);
141
+ this.name = "Stream";
36
142
  this.code = code;
37
143
  }
38
144
  }
145
+ /**
146
+ * The reader asked for a frame the group never held, so the stream has a gap.
147
+ *
148
+ * Raised locally by a frame read, and decoded from a moq-lite peer's `TOO_FAR_BEHIND` reset, since a gap
149
+ * reads the same either way.
150
+ *
151
+ * @public
152
+ */
153
+ export class TooFarBehind extends Stream {
154
+ constructor(options) {
155
+ super(StreamCode.TooFarBehind, {
156
+ ...options,
157
+ message: "lagged: frames were evicted before being read",
158
+ });
159
+ this.name = "TooFarBehind";
160
+ }
161
+ }
162
+ /**
163
+ * A frame is larger than a group can cache, so appending it would exceed the budget by itself.
164
+ *
165
+ * Raised locally by a frame write, and decoded from a moq-lite peer's `FRAME_TOO_LARGE` reset.
166
+ * Mirrors the Rust `Error::FrameTooLarge`, which rejects the same frame before touching any state.
167
+ *
168
+ * @public
169
+ */
170
+ export class FrameTooLarge extends Stream {
171
+ constructor(options) {
172
+ super(StreamCode.FrameTooLarge, {
173
+ ...options,
174
+ message: "frame too large: larger than a group can cache",
175
+ });
176
+ this.name = "FrameTooLarge";
177
+ }
178
+ }
179
+ /**
180
+ * A write grew the group past its cache budget, so the group is aborted.
181
+ *
182
+ * Raised locally by a frame write, and decoded from a moq-lite peer's `GROUP_TOO_LARGE`
183
+ * reset. Mirrors the Rust `Error::GroupTooLarge`.
184
+ *
185
+ * @public
186
+ */
187
+ export class GroupTooLarge extends Stream {
188
+ constructor(options) {
189
+ super(StreamCode.GroupTooLarge, {
190
+ ...options,
191
+ message: "group too large: exceeded the cache budget",
192
+ });
193
+ this.name = "GroupTooLarge";
194
+ }
195
+ }
196
+ /**
197
+ * The requested broadcast or track is not served here.
198
+ *
199
+ * @public
200
+ */
201
+ export class NotFound extends Stream {
202
+ constructor(what, options) {
203
+ super(StreamCode.NotFound, { ...options, message: `not found: ${what}` });
204
+ this.name = "NotFound";
205
+ }
206
+ }
39
207
  /**
40
208
  * A peer broke the protocol in a way the spec says must end the session.
41
209
  *
@@ -43,7 +211,7 @@ export class RemoteError extends Error {
43
211
  * to close. The dispatch that owns the session watches for it and closes, so a nonconforming
44
212
  * peer cannot repeat the violation on the next stream.
45
213
  *
46
- * @internal
214
+ * @public
47
215
  */
48
216
  export class ProtocolViolation extends Error {
49
217
  constructor(message, options) {
@@ -51,6 +219,8 @@ export class ProtocolViolation extends Error {
51
219
  this.name = "ProtocolViolation";
52
220
  }
53
221
  }
222
+ /** Package-internal compatibility names used by the wire implementation. */
223
+ export { Session as SessionError, Stream as StreamError, TooFarBehind as Lagged };
54
224
  function streamCode(err) {
55
225
  if (typeof err !== "object" || err === null)
56
226
  return undefined;
@@ -60,20 +230,165 @@ function streamCode(err) {
60
230
  return streamErrorCode;
61
231
  }
62
232
  /**
63
- * Decode a transport failure into a {@link RemoteError} error when it carries a stream reset code,
233
+ * Which {@link StreamCode} to send when resetting a stream because of `err`.
234
+ *
235
+ * The counterpart to {@link fromTransport}, and the pair has to agree: a code we send for a
236
+ * condition is the code we read that condition back from, or two peers disagree about what it
237
+ * means. Mirrors the Rust `From<&Error> for Stream`.
238
+ *
239
+ * Lossy on purpose. An error describes what went wrong here, while the registry is what the peer
240
+ * can act on, so anything without a code of its own degrades to {@link StreamCode.Internal}
241
+ * rather than inventing one. A {@link Session} lands there too: the two registries are
242
+ * disjoint, so forwarding its code onto a stream would mistranslate it.
243
+ *
244
+ * On an IETF stream the code has to be one the negotiated draft assigns the same meaning to,
245
+ * so a condition it does not register degrades to INTERNAL_ERROR as well. See
246
+ * {@link sharedStreamCode}.
247
+ *
248
+ * @internal
249
+ */
250
+ export function toStreamCode(err, options) {
251
+ const code = localStreamCode(err);
252
+ if (options?.version === undefined)
253
+ return code;
254
+ return sharedStreamCode(code, options.version) ? code : StreamCode.Internal;
255
+ }
256
+ /** The moq-lite code for a local failure, before any draft has a say. */
257
+ function localStreamCode(err) {
258
+ if (err instanceof Stream)
259
+ return err.code;
260
+ if (err instanceof TimeoutError)
261
+ return StreamCode.DeliveryTimeout;
262
+ // Session-scoped: the peer learns which rule it broke from the session close, not from here.
263
+ if (err instanceof ProtocolViolation)
264
+ return StreamCode.SessionClosed;
265
+ return StreamCode.Internal;
266
+ }
267
+ /**
268
+ * The {@link StreamCode.ControlTimeout} error for a control request the peer never answered.
269
+ *
270
+ * A control request that outlives its deadline is not late content, so it does not go out as
271
+ * {@link StreamCode.DeliveryTimeout} the way a bare {@link TimeoutError} would. Callers that
272
+ * bound a request's response build this instead.
273
+ *
274
+ * @internal
275
+ */
276
+ export function controlTimeout(cause) {
277
+ const message = cause instanceof Error && cause.message ? cause.message : "control request timed out";
278
+ return new Stream(StreamCode.ControlTimeout, { cause, message });
279
+ }
280
+ /**
281
+ * Decode a transport failure into a {@link Stream} when it carries a stream reset code,
64
282
  * otherwise pass it through.
65
283
  *
66
284
  * Native WebTransport rejects with a `WebTransportError`; the WebSocket fallback mints an error
67
285
  * with the same `source`/`streamErrorCode` fields. Reading the fields rather than the class
68
286
  * covers both, and works in a runtime with no `WebTransportError` at all.
69
287
  *
288
+ * On moq-lite, a code with a local class decodes back into it, so a peer's condition is caught by
289
+ * the same `instanceof` as one raised here.
290
+ *
291
+ * On an IETF stream a code keeps its value unless moq-lite claims that number for something
292
+ * the draft does not: `0x4` is GOING_AWAY here but UNKNOWN_OBJECT_STATUS on draft-16 and 17,
293
+ * and reading one back as the other would retire a session that is not going anywhere. Those
294
+ * read as {@link StreamCode.Internal} with the wire value kept in the message, since
295
+ * {@link StreamCode} is one numeric space and cannot hold a foreign code without lying about
296
+ * it. A code moq-lite names nothing for survives intact: nothing can misread it.
297
+ *
70
298
  * @internal Called at the transport boundary so the raw error never reaches an application.
71
299
  */
72
- export function fromTransport(err) {
300
+ export function fromTransport(err, options) {
73
301
  const code = streamCode(err);
74
302
  if (code === undefined)
75
303
  return error(err);
76
- return new RemoteError(code, { cause: err });
304
+ if (options?.version !== undefined && !sharedStreamCode(code, options.version) && claimedLocally(code)) {
305
+ return new Stream(StreamCode.Internal, { cause: err, message: `remote error: ${code}` });
306
+ }
307
+ if (code === StreamCode.TooFarBehind)
308
+ return new TooFarBehind({ cause: err });
309
+ if (code === StreamCode.FrameTooLarge)
310
+ return new FrameTooLarge({ cause: err });
311
+ if (code === StreamCode.GroupTooLarge)
312
+ return new GroupTooLarge({ cause: err });
313
+ return new Stream(code, { cause: err });
314
+ }
315
+ /** The codes moq-lite names, which a foreign one must not borrow. */
316
+ const NAMED_CODES = new Set(Object.values(StreamCode));
317
+ /**
318
+ * Whether moq-lite could give `code` a meaning of its own: one of its named codes, or
319
+ * anywhere in the 64+ range, where `StreamCode(code)` mints an application code and an
320
+ * application compares against its own. moq-transport registers nothing above 0x12 and has
321
+ * no application range at all, so a foreign code landing there is never what it looks like.
322
+ */
323
+ function claimedLocally(code) {
324
+ return code >= 64 || NAMED_CODES.has(code);
325
+ }
326
+ const legacyWebTransportErrors = new WeakSet();
327
+ /**
328
+ * Build the `reason` to hand `abort()` / `cancel()` so the transport puts `code` on the wire.
329
+ *
330
+ * Both the WebTransport spec and the WebSocket fallback take the reset code from a
331
+ * `WebTransportError`'s `streamErrorCode` and send 0 for anything else, a plain `Error`
332
+ * included. Since 0 is {@link StreamCode.Internal}, cancelling with a bare `Error` tells the
333
+ * peer we failed rather than that we are done.
334
+ *
335
+ * The native constructor exists exactly where native WebTransport does, which is where the
336
+ * fallback isn't used, so mint a matching shape elsewhere rather than feature-detect a global
337
+ * that will not be there.
338
+ *
339
+ * @internal
340
+ */
341
+ export function toTransport(code, message) {
342
+ const Native = globalThis.WebTransportError;
343
+ if (Native) {
344
+ const Legacy = Native;
345
+ if (legacyWebTransportErrors.has(Native))
346
+ return new Legacy({ message, streamErrorCode: code });
347
+ try {
348
+ return new Native(message, { source: "stream", streamErrorCode: code });
349
+ }
350
+ catch (err) {
351
+ // Chromium still implements the previous single-dictionary constructor.
352
+ if (!(err instanceof TypeError))
353
+ throw err;
354
+ legacyWebTransportErrors.add(Native);
355
+ return new Legacy({ message, streamErrorCode: code });
356
+ }
357
+ }
358
+ return Object.assign(new Error(message), { source: "stream", streamErrorCode: code });
359
+ }
360
+ /**
361
+ * Decode a session close into its terminal error: `null` for a clean close
362
+ * ({@link SessionCode.Cancel}), otherwise a {@link Session} carrying the peer's code.
363
+ *
364
+ * @internal Applied to the transport's `closed` info so the code survives to the application.
365
+ */
366
+ export function fromClose(info) {
367
+ const code = (info.closeCode ?? SessionCode.Cancel);
368
+ if (code === SessionCode.Cancel)
369
+ return null;
370
+ return new Session(code, { reason: info.reason });
371
+ }
372
+ /**
373
+ * The session's close as the error it ends everything with, carrying the peer's code. A clean
374
+ * close code is still an error here: whatever the close cut off did not end.
375
+ *
376
+ * @internal
377
+ */
378
+ export function closeError(quic) {
379
+ return quic.closed.then((info) => fromClose(info) ?? new Session(SessionCode.Cancel, { reason: info.reason }), (err) => error(err));
380
+ }
381
+ /**
382
+ * Report a failure the session's close caused as the session's own error, which carries the
383
+ * peer's close code; any other failure passes through.
384
+ *
385
+ * @internal
386
+ */
387
+ export async function sessionCause(quic, err) {
388
+ const source = typeof err === "object" && err !== null ? err.source : undefined;
389
+ if (quic && source === "session")
390
+ return closeError(quic);
391
+ return error(err);
77
392
  }
78
393
  /**
79
394
  * Coerce an unknown thrown value into an `Error`.
package/error.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACrC,wCAAwC;IAC/B,IAAI,CAAS;IAEtB,YAAY,IAAY,EAAE,OAA6B;QACtD,KAAK,CAAC,iBAAiB,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;QAC1B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAClB,CAAC;CACD;AAED;;;;;;;;GAQG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC3C,YAAY,OAAe,EAAE,OAA6B;QACzD,KAAK,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IACjC,CAAC;CACD;AAKD,SAAS,UAAU,CAAC,GAAY;IAC/B,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IAE9D,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,GAAG,GAAsB,CAAC;IAC3D,IAAI,MAAM,KAAK,QAAQ,IAAI,OAAO,eAAe,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAEjF,OAAO,eAAe,CAAC;AACxB,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,aAAa,CAAC,GAAY;IACzC,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,KAAK,CAAC,GAAY;IACjC,OAAO,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,MAAM,CAAC,GAAY;IAClC,MAAM,CAAC,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;IAErB,sFAAsF;IACtF,yEAAyE;IACzE,IAAI,OAAO,iBAAiB,KAAK,WAAW,IAAI,CAAC,YAAY,iBAAiB,EAAE,CAAC;QAChF,MAAM,KAAK,GAAG,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,CAAC,eAAe,KAAK,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,eAAe,EAAE,CAAC,CAAC;QACxE,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC/B,OAAO,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,KAAK,MAAM,GAAG,CAAC,CAAC,CAAC,sBAAsB,MAAM,EAAE,CAAC;IAChF,CAAC;IAED,OAAO,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,IAAI,IAAI,eAAe,CAAC;AAC/C,CAAC","sourcesContent":["/**\n * Errors, including the code a peer reports when it resets a stream.\n *\n * @module\n */\n\n/**\n * An error the peer reported by resetting a stream, carrying the raw code it sent.\n *\n * The codes are not standardized, so this deliberately does not translate one into a local\n * error: the number means whatever the peer's implementation says it means. A read or write\n * rejects with this on every transport, so branch on {@link code} rather than feature-detecting\n * `WebTransportError`, which a non-browser runtime never defines and the WebSocket fallback\n * never throws.\n *\n * Code 0 is what a transport sends when a stream is dropped or aborted with no code of its own.\n *\n * ```ts\n * try {\n * frame = await group.readFrame();\n * } catch (err) {\n * // Whatever this peer's code 2 means to it.\n * if (err instanceof RemoteError && err.code === 2) return;\n * throw err;\n * }\n * ```\n *\n * @public\n */\nexport class RemoteError extends Error {\n\t/** The code the peer sent, verbatim. */\n\treadonly code: number;\n\n\tconstructor(code: number, options?: { cause?: unknown }) {\n\t\tsuper(`remote error: ${code}`, options);\n\t\tthis.name = \"RemoteError\";\n\t\tthis.code = code;\n\t}\n}\n\n/**\n * A peer broke the protocol in a way the spec says must end the session.\n *\n * Thrown where the violation is detected, rather than handled there: a decoder has no session\n * to close. The dispatch that owns the session watches for it and closes, so a nonconforming\n * peer cannot repeat the violation on the next stream.\n *\n * @internal\n */\nexport class ProtocolViolation extends Error {\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t\tthis.name = \"ProtocolViolation\";\n\t}\n}\n\n/** The WebTransport-shaped fields a stream reset code arrives in. */\ntype StreamErrorLike = { source?: unknown; streamErrorCode?: unknown };\n\nfunction streamCode(err: unknown): number | undefined {\n\tif (typeof err !== \"object\" || err === null) return undefined;\n\n\tconst { source, streamErrorCode } = err as StreamErrorLike;\n\tif (source !== \"stream\" || typeof streamErrorCode !== \"number\") return undefined;\n\n\treturn streamErrorCode;\n}\n\n/**\n * Decode a transport failure into a {@link RemoteError} error when it carries a stream reset code,\n * otherwise pass it through.\n *\n * Native WebTransport rejects with a `WebTransportError`; the WebSocket fallback mints an error\n * with the same `source`/`streamErrorCode` fields. Reading the fields rather than the class\n * covers both, and works in a runtime with no `WebTransportError` at all.\n *\n * @internal Called at the transport boundary so the raw error never reaches an application.\n */\nexport function fromTransport(err: unknown): Error {\n\tconst code = streamCode(err);\n\tif (code === undefined) return error(err);\n\treturn new RemoteError(code, { cause: err });\n}\n\n/**\n * Coerce an unknown thrown value into an `Error`.\n *\n * @internal\n */\nexport function error(err: unknown): Error {\n\treturn err instanceof Error ? err : new Error(String(err));\n}\n\n/**\n * Format an error into a non-empty, human-readable string for logging.\n *\n * Safari always leaves `WebTransportError.message` blank, so a bare `err.message` degrades to\n * an empty string and the reason is lost. This falls back to the error type name and appends\n * the WebTransport `source` and application `streamErrorCode`, so the log line always says\n * something.\n */\nexport function reason(err: unknown): string {\n\tconst e = error(err);\n\n\t// WebTransportError carries the failure origin and the peer's application error code,\n\t// often the only identifying detail since WebKit leaves `message` empty.\n\tif (typeof WebTransportError !== \"undefined\" && e instanceof WebTransportError) {\n\t\tconst parts = [`source=${e.source}`];\n\t\tif (e.streamErrorCode !== null) parts.push(`code=${e.streamErrorCode}`);\n\t\tconst detail = parts.join(\" \");\n\t\treturn e.message ? `${e.message} (${detail})` : `WebTransportError: ${detail}`;\n\t}\n\n\treturn e.message || e.name || \"unknown error\";\n}\n"]}
1
+ {"version":3,"file":"error.js","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Errors, including the code a peer reports when it resets a stream or closes the session.\n *\n * @module\n */\n\nimport { sharedStreamCode } from \"./ietf/error.ts\";\nimport type { IetfVersion } from \"./ietf/version.ts\";\nimport { TimeoutError } from \"./util/timeout.ts\";\n\n/** The nominal brand for session termination codes. */\ndeclare const SESSION_CODE: unique symbol;\n\n/** A code from the session termination registry. */\nexport type SessionCode = number & { readonly [SESSION_CODE]: true };\n\n/**\n * Codes a peer sends when terminating the session, mirroring the Rust `Session`.\n *\n * Specified by moq-lite, which reuses moq-transport's codes unchanged. Call `SessionCode(code)`\n * to construct an application code in the 64+ range. {@link StreamCode} is the other registry,\n * and the two are disjoint, so the same integer means different things in each.\n *\n * Codes 32-47 are reserved: nothing is sent there, and the draft gives a received one no\n * meaning, so treat anything not listed below as an unspecified error rather than guessing.\n *\n * @public\n */\nexport const SessionCode = Object.freeze(\n\tObject.assign((code: number): SessionCode => applicationCode(code) as SessionCode, {\n\t\t/** Ending the session normally, with no error. */\n\t\tCancel: 0x0 as SessionCode,\n\t\t/** Something went wrong that isn't worth a dedicated code. */\n\t\tInternal: 0x1 as SessionCode,\n\t\t/** The credentials don't grant the requested path or operation. Retrying will fail again. */\n\t\tUnauthorized: 0x2 as SessionCode,\n\t\t/** A protocol rule was broken; the session is unusable. */\n\t\tProtocolViolation: 0x3 as SessionCode,\n\t\t/** A key-value pair was malformed or repeated more than allowed. */\n\t\tKeyValueFormatting: 0x6 as SessionCode,\n\t\t/** The peer did not close within the GOAWAY drain deadline. */\n\t\tGoawayTimeout: 0x10 as SessionCode,\n\t\t/** A control message took too long. */\n\t\tTimeout: 0x11 as SessionCode,\n\t\t/** No version could be negotiated. */\n\t\tVersion: 0x15 as SessionCode,\n\t} as const),\n);\n\n/** The nominal brand for stream reset codes. */\ndeclare const STREAM_CODE: unique symbol;\n\n/** A code from the stream reset registry. */\nexport type StreamCode = number & { readonly [STREAM_CODE]: true };\n\n/**\n * Codes a peer sends when resetting a stream, mirroring the Rust `Stream`.\n *\n * The counterpart to {@link SessionCode}, and a disjoint space: a stream reset of 0 is\n * {@link StreamCode.Internal}, not a cancellation ({@link StreamCode.Cancel} is 1). Call\n * `StreamCode(code)` to construct an application code in the 64+ range.\n *\n * Conditions the shared codes don't cover are assigned in moq-lite's own 48-63 range, so a\n * received one is the named code. 32 through 47 is reserved: nothing is sent there and a\n * received one is an unspecified error.\n *\n * @public\n */\nexport const StreamCode = Object.freeze(\n\tObject.assign((code: number): StreamCode => applicationCode(code) as StreamCode, {\n\t\t/** Something went wrong that isn't worth a dedicated code. */\n\t\tInternal: 0x0 as StreamCode,\n\t\t/** The sender is done with this stream, not failing. A routine unsubscribe. */\n\t\tCancel: 0x1 as StreamCode,\n\t\t/** The content missed its delivery deadline. */\n\t\tDeliveryTimeout: 0x2 as StreamCode,\n\t\t/** The session ended, taking this stream with it. */\n\t\tSessionClosed: 0x3 as StreamCode,\n\t\t/** The session is going away (a GOAWAY was received). */\n\t\tGoingAway: 0x4 as StreamCode,\n\t\t/** The reader fell too far behind and content was dropped to catch up. */\n\t\tTooFarBehind: 0x5 as StreamCode,\n\t\t/** The track's content could not be parsed. */\n\t\tMalformedTrack: 0x12 as StreamCode,\n\t\t/** The peer took too long to answer a control request. */\n\t\tControlTimeout: 0x31 as StreamCode,\n\t\t/** The requested broadcast or track does not exist at the peer. */\n\t\tNotFound: 0x33 as StreamCode,\n\t\t/** The group was superseded by a newer one and dropped. */\n\t\tOld: 0x34 as StreamCode,\n\t\t/** The group was dropped under memory pressure, so it can be re-fetched. */\n\t\tEvicted: 0x35 as StreamCode,\n\t\t/** A frame declared a payload larger than the receiver accepts. */\n\t\tFrameTooLarge: 0x38 as StreamCode,\n\t\t/** The publisher could serve this request but has no capacity for it now. */\n\t\tNoCapacity: 0x30 as StreamCode,\n\t\t/** A group grew past its cache budget and was aborted. */\n\t\tGroupTooLarge: 0x32 as StreamCode,\n\t} as const),\n);\n\nfunction applicationCode(code: number): number {\n\tif (!Number.isInteger(code) || code < 64 || code > 0xffffffff) {\n\t\tthrow new RangeError(`invalid application error code: ${code}`);\n\t}\n\treturn code;\n}\n\n/**\n * An error the peer reported by closing the session, carrying its {@link SessionCode}.\n *\n * This surfaces on every transport, so catch this type rather than feature-detecting\n * `WebTransportError`, which a non-browser runtime never defines and the WebSocket fallback\n * never throws.\n *\n * ```ts\n * connection.error.subscribe((err) => {\n * if (err instanceof Session && err.code === SessionCode.Unauthorized) {\n * console.warn(\"server rejected the session\");\n * }\n * });\n * ```\n *\n * @public\n */\nexport class Session extends Error {\n\t/** The session code the peer sent, verbatim. */\n\treadonly code: SessionCode;\n\n\tconstructor(code: SessionCode, options?: { cause?: unknown; reason?: string }) {\n\t\tsuper(options?.reason ? `remote error: ${code} (${options.reason})` : `remote error: ${code}`, options);\n\t\tthis.name = \"Session\";\n\t\tthis.code = code;\n\t}\n}\n\n/** Options for a {@link Stream}. */\nexport interface StreamOptions {\n\t/** The failure this one wraps. */\n\tcause?: unknown;\n\t/** The peer's human-readable reason, appended to the default message. */\n\treason?: string;\n\t/** Replaces the default `remote error: <code>` text, for a condition raised locally. */\n\tmessage?: string;\n}\n\n/**\n * An error carrying a {@link StreamCode}: the code a peer sent when it reset a stream, or the\n * one this side sends when it resets one.\n *\n * This surfaces on every transport, so catch this type rather than feature-detecting\n * `WebTransportError`, which a non-browser runtime never defines and the WebSocket fallback\n * never throws. Local conditions with a code of their own subclass it ({@link TooFarBehind},\n * {@link FrameTooLarge}, {@link GroupTooLarge}, {@link NotFound}), so the same `code` check catches a condition\n * whether it was raised here or reported by the peer.\n *\n * ```ts\n * try {\n * frame = await group.readFrame();\n * } catch (err) {\n * if (err instanceof Stream && err.code === StreamCode.Cancel) return;\n * throw err;\n * }\n * ```\n *\n * @public\n */\nexport class Stream extends Error {\n\t/** The stream code; moq-lite resets forward it verbatim. */\n\treadonly code: StreamCode;\n\n\tconstructor(code: StreamCode, options?: StreamOptions) {\n\t\tsuper(\n\t\t\toptions?.message ??\n\t\t\t\t(options?.reason ? `remote error: ${code} (${options.reason})` : `remote error: ${code}`),\n\t\t\toptions,\n\t\t);\n\t\tthis.name = \"Stream\";\n\t\tthis.code = code;\n\t}\n}\n\n/**\n * The reader asked for a frame the group never held, so the stream has a gap.\n *\n * Raised locally by a frame read, and decoded from a moq-lite peer's `TOO_FAR_BEHIND` reset, since a gap\n * reads the same either way.\n *\n * @public\n */\nexport class TooFarBehind extends Stream {\n\tconstructor(options?: { cause?: unknown }) {\n\t\tsuper(StreamCode.TooFarBehind, {\n\t\t\t...options,\n\t\t\tmessage: \"lagged: frames were evicted before being read\",\n\t\t});\n\t\tthis.name = \"TooFarBehind\";\n\t}\n}\n\n/**\n * A frame is larger than a group can cache, so appending it would exceed the budget by itself.\n *\n * Raised locally by a frame write, and decoded from a moq-lite peer's `FRAME_TOO_LARGE` reset.\n * Mirrors the Rust `Error::FrameTooLarge`, which rejects the same frame before touching any state.\n *\n * @public\n */\nexport class FrameTooLarge extends Stream {\n\tconstructor(options?: { cause?: unknown }) {\n\t\tsuper(StreamCode.FrameTooLarge, {\n\t\t\t...options,\n\t\t\tmessage: \"frame too large: larger than a group can cache\",\n\t\t});\n\t\tthis.name = \"FrameTooLarge\";\n\t}\n}\n\n/**\n * A write grew the group past its cache budget, so the group is aborted.\n *\n * Raised locally by a frame write, and decoded from a moq-lite peer's `GROUP_TOO_LARGE`\n * reset. Mirrors the Rust `Error::GroupTooLarge`.\n *\n * @public\n */\nexport class GroupTooLarge extends Stream {\n\tconstructor(options?: { cause?: unknown }) {\n\t\tsuper(StreamCode.GroupTooLarge, {\n\t\t\t...options,\n\t\t\tmessage: \"group too large: exceeded the cache budget\",\n\t\t});\n\t\tthis.name = \"GroupTooLarge\";\n\t}\n}\n\n/**\n * The requested broadcast or track is not served here.\n *\n * @public\n */\nexport class NotFound extends Stream {\n\tconstructor(what: string, options?: { cause?: unknown }) {\n\t\tsuper(StreamCode.NotFound, { ...options, message: `not found: ${what}` });\n\t\tthis.name = \"NotFound\";\n\t}\n}\n\n/**\n * A peer broke the protocol in a way the spec says must end the session.\n *\n * Thrown where the violation is detected, rather than handled there: a decoder has no session\n * to close. The dispatch that owns the session watches for it and closes, so a nonconforming\n * peer cannot repeat the violation on the next stream.\n *\n * @public\n */\nexport class ProtocolViolation extends Error {\n\tconstructor(message: string, options?: { cause?: unknown }) {\n\t\tsuper(message, options);\n\t\tthis.name = \"ProtocolViolation\";\n\t}\n}\n\n/** Package-internal compatibility names used by the wire implementation. */\nexport { Session as SessionError, Stream as StreamError, TooFarBehind as Lagged };\n/** Package-internal compatibility name used by the wire implementation. */\nexport type StreamErrorOptions = StreamOptions;\n\n/** The WebTransport-shaped fields a stream reset code arrives in. */\ntype StreamErrorLike = { source?: unknown; streamErrorCode?: unknown };\n\nfunction streamCode(err: unknown): StreamCode | undefined {\n\tif (typeof err !== \"object\" || err === null) return undefined;\n\n\tconst { source, streamErrorCode } = err as StreamErrorLike;\n\tif (source !== \"stream\" || typeof streamErrorCode !== \"number\") return undefined;\n\n\treturn streamErrorCode as StreamCode;\n}\n\n/** Transport context for encoding and decoding errors. @internal */\ninterface TransportErrorOptions {\n\t/** The negotiated IETF draft, or absent for moq-lite. */\n\tversion?: IetfVersion;\n}\n\n/**\n * Which {@link StreamCode} to send when resetting a stream because of `err`.\n *\n * The counterpart to {@link fromTransport}, and the pair has to agree: a code we send for a\n * condition is the code we read that condition back from, or two peers disagree about what it\n * means. Mirrors the Rust `From<&Error> for Stream`.\n *\n * Lossy on purpose. An error describes what went wrong here, while the registry is what the peer\n * can act on, so anything without a code of its own degrades to {@link StreamCode.Internal}\n * rather than inventing one. A {@link Session} lands there too: the two registries are\n * disjoint, so forwarding its code onto a stream would mistranslate it.\n *\n * On an IETF stream the code has to be one the negotiated draft assigns the same meaning to,\n * so a condition it does not register degrades to INTERNAL_ERROR as well. See\n * {@link sharedStreamCode}.\n *\n * @internal\n */\nexport function toStreamCode(err: unknown, options?: TransportErrorOptions): StreamCode {\n\tconst code = localStreamCode(err);\n\tif (options?.version === undefined) return code;\n\treturn sharedStreamCode(code, options.version) ? code : StreamCode.Internal;\n}\n\n/** The moq-lite code for a local failure, before any draft has a say. */\nfunction localStreamCode(err: unknown): StreamCode {\n\tif (err instanceof Stream) return err.code;\n\tif (err instanceof TimeoutError) return StreamCode.DeliveryTimeout;\n\t// Session-scoped: the peer learns which rule it broke from the session close, not from here.\n\tif (err instanceof ProtocolViolation) return StreamCode.SessionClosed;\n\treturn StreamCode.Internal;\n}\n\n/**\n * The {@link StreamCode.ControlTimeout} error for a control request the peer never answered.\n *\n * A control request that outlives its deadline is not late content, so it does not go out as\n * {@link StreamCode.DeliveryTimeout} the way a bare {@link TimeoutError} would. Callers that\n * bound a request's response build this instead.\n *\n * @internal\n */\nexport function controlTimeout(cause: unknown): Stream {\n\tconst message = cause instanceof Error && cause.message ? cause.message : \"control request timed out\";\n\treturn new Stream(StreamCode.ControlTimeout, { cause, message });\n}\n\n/**\n * Decode a transport failure into a {@link Stream} when it carries a stream reset code,\n * otherwise pass it through.\n *\n * Native WebTransport rejects with a `WebTransportError`; the WebSocket fallback mints an error\n * with the same `source`/`streamErrorCode` fields. Reading the fields rather than the class\n * covers both, and works in a runtime with no `WebTransportError` at all.\n *\n * On moq-lite, a code with a local class decodes back into it, so a peer's condition is caught by\n * the same `instanceof` as one raised here.\n *\n * On an IETF stream a code keeps its value unless moq-lite claims that number for something\n * the draft does not: `0x4` is GOING_AWAY here but UNKNOWN_OBJECT_STATUS on draft-16 and 17,\n * and reading one back as the other would retire a session that is not going anywhere. Those\n * read as {@link StreamCode.Internal} with the wire value kept in the message, since\n * {@link StreamCode} is one numeric space and cannot hold a foreign code without lying about\n * it. A code moq-lite names nothing for survives intact: nothing can misread it.\n *\n * @internal Called at the transport boundary so the raw error never reaches an application.\n */\nexport function fromTransport(err: unknown, options?: TransportErrorOptions): Error {\n\tconst code = streamCode(err);\n\tif (code === undefined) return error(err);\n\tif (options?.version !== undefined && !sharedStreamCode(code, options.version) && claimedLocally(code)) {\n\t\treturn new Stream(StreamCode.Internal, { cause: err, message: `remote error: ${code}` });\n\t}\n\tif (code === StreamCode.TooFarBehind) return new TooFarBehind({ cause: err });\n\tif (code === StreamCode.FrameTooLarge) return new FrameTooLarge({ cause: err });\n\tif (code === StreamCode.GroupTooLarge) return new GroupTooLarge({ cause: err });\n\treturn new Stream(code, { cause: err });\n}\n\n/** The codes moq-lite names, which a foreign one must not borrow. */\nconst NAMED_CODES: ReadonlySet<number> = new Set(Object.values(StreamCode));\n\n/**\n * Whether moq-lite could give `code` a meaning of its own: one of its named codes, or\n * anywhere in the 64+ range, where `StreamCode(code)` mints an application code and an\n * application compares against its own. moq-transport registers nothing above 0x12 and has\n * no application range at all, so a foreign code landing there is never what it looks like.\n */\nfunction claimedLocally(code: number): boolean {\n\treturn code >= 64 || NAMED_CODES.has(code);\n}\n\nconst legacyWebTransportErrors = new WeakSet<object>();\n\n/**\n * Build the `reason` to hand `abort()` / `cancel()` so the transport puts `code` on the wire.\n *\n * Both the WebTransport spec and the WebSocket fallback take the reset code from a\n * `WebTransportError`'s `streamErrorCode` and send 0 for anything else, a plain `Error`\n * included. Since 0 is {@link StreamCode.Internal}, cancelling with a bare `Error` tells the\n * peer we failed rather than that we are done.\n *\n * The native constructor exists exactly where native WebTransport does, which is where the\n * fallback isn't used, so mint a matching shape elsewhere rather than feature-detect a global\n * that will not be there.\n *\n * @internal\n */\nexport function toTransport(code: StreamCode, message: string): Error {\n\tconst Native = (globalThis as { WebTransportError?: typeof WebTransportError }).WebTransportError;\n\tif (Native) {\n\t\tconst Legacy = Native as unknown as new (init: { message: string; streamErrorCode: number }) => Error;\n\t\tif (legacyWebTransportErrors.has(Native)) return new Legacy({ message, streamErrorCode: code });\n\n\t\ttry {\n\t\t\treturn new Native(message, { source: \"stream\", streamErrorCode: code });\n\t\t} catch (err) {\n\t\t\t// Chromium still implements the previous single-dictionary constructor.\n\t\t\tif (!(err instanceof TypeError)) throw err;\n\t\t\tlegacyWebTransportErrors.add(Native);\n\t\t\treturn new Legacy({ message, streamErrorCode: code });\n\t\t}\n\t}\n\n\treturn Object.assign(new Error(message), { source: \"stream\" as const, streamErrorCode: code });\n}\n\n/**\n * Decode a session close into its terminal error: `null` for a clean close\n * ({@link SessionCode.Cancel}), otherwise a {@link Session} carrying the peer's code.\n *\n * @internal Applied to the transport's `closed` info so the code survives to the application.\n */\nexport function fromClose(info: WebTransportCloseInfo): Session | null {\n\tconst code = (info.closeCode ?? SessionCode.Cancel) as SessionCode;\n\tif (code === SessionCode.Cancel) return null;\n\treturn new Session(code, { reason: info.reason });\n}\n\n/**\n * The session's close as the error it ends everything with, carrying the peer's code. A clean\n * close code is still an error here: whatever the close cut off did not end.\n *\n * @internal\n */\nexport function closeError(quic: WebTransport): Promise<Error> {\n\treturn quic.closed.then(\n\t\t(info) => fromClose(info) ?? new Session(SessionCode.Cancel, { reason: info.reason }),\n\t\t(err: unknown) => error(err),\n\t);\n}\n\n/**\n * Report a failure the session's close caused as the session's own error, which carries the\n * peer's close code; any other failure passes through.\n *\n * @internal\n */\nexport async function sessionCause(quic: WebTransport | undefined, err: unknown): Promise<Error> {\n\tconst source = typeof err === \"object\" && err !== null ? (err as { source?: unknown }).source : undefined;\n\tif (quic && source === \"session\") return closeError(quic);\n\treturn error(err);\n}\n\n/**\n * Coerce an unknown thrown value into an `Error`.\n *\n * @internal\n */\nexport function error(err: unknown): Error {\n\treturn err instanceof Error ? err : new Error(String(err));\n}\n\n/**\n * Format an error into a non-empty, human-readable string for logging.\n *\n * Safari always leaves `WebTransportError.message` blank, so a bare `err.message` degrades to\n * an empty string and the reason is lost. This falls back to the error type name and appends\n * the WebTransport `source` and application `streamErrorCode`, so the log line always says\n * something.\n */\nexport function reason(err: unknown): string {\n\tconst e = error(err);\n\n\t// WebTransportError carries the failure origin and the peer's application error code,\n\t// often the only identifying detail since WebKit leaves `message` empty.\n\tif (typeof WebTransportError !== \"undefined\" && e instanceof WebTransportError) {\n\t\tconst parts = [`source=${e.source}`];\n\t\tif (e.streamErrorCode !== null) parts.push(`code=${e.streamErrorCode}`);\n\t\tconst detail = parts.join(\" \");\n\t\treturn e.message ? `${e.message} (${detail})` : `WebTransportError: ${detail}`;\n\t}\n\n\treturn e.message || e.name || \"unknown error\";\n}\n"]}
package/errors.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Session and stream error types.
3
+ *
4
+ * @module
5
+ */
6
+ export { FrameTooLarge, GroupTooLarge, NotFound, ProtocolViolation, Session, Stream, type StreamOptions, TooFarBehind, } from "./error";
7
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACN,aAAa,EACb,aAAa,EACb,QAAQ,EACR,iBAAiB,EACjB,OAAO,EACP,MAAM,EACN,KAAK,aAAa,EAClB,YAAY,GACZ,MAAM,YAAY,CAAC"}
package/errors.js ADDED
@@ -0,0 +1,8 @@
1
+ /* @ts-self-types="./errors.d.ts" */
2
+ /**
3
+ * Session and stream error types.
4
+ *
5
+ * @module
6
+ */
7
+ export { FrameTooLarge, GroupTooLarge, NotFound, ProtocolViolation, Session, Stream, TooFarBehind, } from "./error.js";
8
+ //# sourceMappingURL=errors.js.map
package/errors.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EACN,aAAa,EACb,aAAa,EACb,QAAQ,EACR,iBAAiB,EACjB,OAAO,EACP,MAAM,EAEN,YAAY,GACZ,MAAM,YAAY,CAAC","sourcesContent":["/**\n * Session and stream error types.\n *\n * @module\n */\nexport {\n\tFrameTooLarge,\n\tGroupTooLarge,\n\tNotFound,\n\tProtocolViolation,\n\tSession,\n\tStream,\n\ttype StreamOptions,\n\tTooFarBehind,\n} from \"./error.ts\";\n"]}
package/group.d.ts CHANGED
@@ -3,12 +3,12 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { type GetPromise, type Getter } from "@norskvideo/moq-signals";
6
+ import { type GetPromise } from "@norskvideo/moq-signals";
7
7
  import { Timestamp } from "./time";
8
- /** Maximum bytes of frames cached in a group before old frames are evicted from the front. */
8
+ /** Maximum bytes of frames in a group. A write that would exceed this aborts with {@link GroupTooLarge}. */
9
9
  export declare const MAX_GROUP_CACHE_BYTES: number;
10
- /** Maximum number of frames cached in a group before old frames are evicted from the front. */
11
- export declare const MAX_GROUP_FRAMES = 1024;
10
+ /** Maximum number of frames in a group. 8192 is the largest legal group; the 8193rd write is {@link GroupTooLarge}. */
11
+ export declare const MAX_GROUP_FRAMES = 8192;
12
12
  /**
13
13
  * A frame buffered in a group: its presentation {@link Timestamp} and payload bytes.
14
14
  *
@@ -29,9 +29,8 @@ export interface ReadOptions {
29
29
  /**
30
30
  * The lowest sequence number the caller wants; defaults to the whole group.
31
31
  *
32
- * Frames below it are discarded instead of returned, and frames evicted below it are not
33
- * a gap: the read resumes at the next retained frame rather than throwing {@link Lagged}.
34
- * An eviction at or above it still throws, because the caller asked for that frame.
32
+ * Frames below it are discarded instead of returned. The group still starts at frame 0,
33
+ * so this is a reader filter, not a missing prefix.
35
34
  */
36
35
  from?: number;
37
36
  }
@@ -40,13 +39,6 @@ export interface Info {
40
39
  /** Sequence number of this group within its track. */
41
40
  sequence: number;
42
41
  }
43
- /**
44
- * Thrown by a frame read when the reader fell behind the group's eviction window: frames
45
- * it had not yet read were dropped to stay under the cache cap, so the stream has a gap.
46
- */
47
- export declare class Lagged extends Error {
48
- constructor();
49
- }
50
42
  /**
51
43
  * The write side of an ordered stream of frames within a track.
52
44
  *
@@ -64,31 +56,6 @@ export declare class Producer {
64
56
  get closed(): GetPromise<Error | null>;
65
57
  /** A read handle for this group. */
66
58
  consume(): Consumer;
67
- /**
68
- * Create an independent read handle that receives every frame written here.
69
- *
70
- * Frames written so far are replayed synchronously; later writes and close are teed
71
- * in as they happen.
72
- *
73
- * @internal Track fan-out and fetch coalescing only. Use {@link consume} instead.
74
- */
75
- mirror(): Consumer;
76
- /**
77
- * Whether any mirror reader is currently attached.
78
- *
79
- * Pairs with {@link unused}. Fetch coalescing watches it to cancel a download once every reader
80
- * has gone: a group can stay open indefinitely (a catalog track, a JSON stream), so it can't
81
- * rely on the stream ending on its own.
82
- *
83
- * @internal Track fan-out and fetch coalescing only.
84
- */
85
- get used(): Getter<boolean>;
86
- /**
87
- * Resolves once no mirror reader remains (or the group closes).
88
- *
89
- * @internal Track fan-out and fetch coalescing only.
90
- */
91
- unused(): Promise<void>;
92
59
  /** Writes a frame to the group. */
93
60
  writeFrame(frame: Frame): void;
94
61
  /** Write a string as a single UTF-8 encoded frame, stamped with {@link Timestamp.now}. */
@@ -116,7 +83,8 @@ export declare class Consumer {
116
83
  readonly sequence: number;
117
84
  private constructor();
118
85
  /**
119
- * Settles once the group closes: `null` on a clean close, or the abort {@link Error}.
86
+ * Settles when this consumer reaches a terminal state: `null` after a clean close has no
87
+ * unread or in-flight frames, or the terminal {@link Error}.
120
88
  * Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.
121
89
  */
122
90
  get closed(): GetPromise<Error | null>;
@@ -124,10 +92,8 @@ export declare class Consumer {
124
92
  get done(): boolean;
125
93
  /** True once the group has been closed, regardless of whether buffered frames remain unread. Synchronous complement to the {@link closed} promise. */
126
94
  get isClosed(): boolean;
127
- /** True if frames were evicted from the front of this group before being read. */
128
- get skipped(): boolean;
129
95
  /**
130
- * How many frames the group has held, including any already read or evicted.
96
+ * How many frames the group has held, including any already read.
131
97
  *
132
98
  * It is also the next frame's sequence number, so `frameCount - 1` names the newest
133
99
  * frame written so far. A publisher snapshots it to resolve the track's live edge.
package/group.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../src/group.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"group.d.ts","sourceRoot":"","sources":["../src/group.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAgB,KAAK,UAAU,EAA6B,MAAM,cAAc,CAAC;AAGxF,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAEtC,4GAA4G;AAC5G,eAAO,MAAM,qBAAqB,QAAmB,CAAC;AAEtD,uHAAuH;AACvH,eAAO,MAAM,gBAAgB,OAAO,CAAC;AAErC;;;;;GAKG;AACH,MAAM,WAAW,KAAK;IACrB,yBAAyB;IACzB,OAAO,EAAE,UAAU,CAAC;IACpB;;;OAGG;IACH,SAAS,EAAE,SAAS,CAAC;CACrB;AAED,+CAA+C;AAC/C,MAAM,WAAW,WAAW;IAC3B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;CACd;AAED,gCAAgC;AAChC,MAAM,WAAW,IAAI;IACpB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;CACjB;AAqCD;;;;GAIG;AACH,qBAAa,QAAQ;;IACpB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAc1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAWD;;;OAGG;IACH,IAAI,MAAM,IAAI,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,CAErC;IAED,oCAAoC;IACpC,OAAO,IAAI,QAAQ,CAElB;IAmED,mCAAmC;IACnC,UAAU,CAAC,KAAK,EAAE,KAAK,QAkCtB;IAED,0FAA0F;IAC1F,WAAW,CAAC,GAAG,EAAE,MAAM,QAEtB;IAED,wFAAwF;IACxF,SAAS,CAAC,IAAI,EAAE,OAAO,QAEtB;IAED,sFAAsF;IACtF,SAAS,CAAC,IAAI,EAAE,OAAO,QAEtB;IAED,2CAA2C;IAC3C,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,mEAAmE;IACnE,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,QAUlB;CACD;AA2ED;;;;;;;GAOG;AACH,qBAAa,QAAQ;;IACpB,sDAAsD;IACtD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAc1B,OAAO,eAGN;IAED;;;;OAIG;IACH,IAAI,MAAM,IAAI,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,CAOrC;IAuHD,sGAAsG;IACtG,IAAI,IAAI,IAAI,OAAO,CAMlB;IAED,sJAAsJ;IACtJ,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;;;OAKG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;;;;;OAMG;IACH,YAAY,IAAI,KAAK,GAAG,SAAS,CAKhC;IAED,+FAA+F;IAC/F,oBAAoB,IAAI,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC,GAAG,SAAS,CAMjE;IAED,uDAAuD;IACjD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAW9B;IAED;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAE5C;IA+BD;;;OAGG;IACG,iBAAiB,CAAC,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC,GAAG,SAAS,CAAC,CAIlG;IAED,sEAAsE;IAChE,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAG9C;IAED,2DAA2D;IACrD,QAAQ,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAG7C;IAED,0EAA0E;IACpE,QAAQ,IAAI,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC,CAG7C;IAED,+EAA+E;IAC/E,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,QAGlB;CACD"}