@norskvideo/moq-net 0.1.7 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/announce.d.ts +7 -0
- package/announce.d.ts.map +1 -0
- package/announce.js +8 -0
- package/announce.js.map +1 -0
- package/announced.d.ts +49 -91
- package/announced.d.ts.map +1 -1
- package/announced.js +21 -156
- package/announced.js.map +1 -1
- package/bandwidth.d.ts +163 -0
- package/bandwidth.d.ts.map +1 -0
- package/bandwidth.js +304 -0
- package/bandwidth.js.map +1 -0
- package/bandwidth_api.d.ts +7 -0
- package/bandwidth_api.d.ts.map +1 -0
- package/bandwidth_api.js +8 -0
- package/bandwidth_api.js.map +1 -0
- package/broadcast.d.ts +44 -35
- package/broadcast.d.ts.map +1 -1
- package/broadcast.js +104 -60
- package/broadcast.js.map +1 -1
- package/connection/accept.d.ts +16 -1
- package/connection/accept.d.ts.map +1 -1
- package/connection/accept.js +52 -28
- package/connection/accept.js.map +1 -1
- package/connection/browser.d.ts.map +1 -1
- package/connection/browser.js +9 -7
- package/connection/browser.js.map +1 -1
- package/connection/connect.d.ts +30 -6
- package/connection/connect.d.ts.map +1 -1
- package/connection/connect.js +110 -54
- package/connection/connect.js.map +1 -1
- package/connection/established.d.ts +17 -21
- package/connection/established.d.ts.map +1 -1
- package/connection/established.js.map +1 -1
- package/connection/forward.d.ts +2 -0
- package/connection/forward.d.ts.map +1 -0
- package/connection/forward.js +173 -0
- package/connection/forward.js.map +1 -0
- package/connection/handshake.d.ts +1 -0
- package/connection/handshake.d.ts.map +1 -1
- package/connection/handshake.js +5 -2
- package/connection/handshake.js.map +1 -1
- package/connection/index.d.ts +5 -5
- package/connection/index.d.ts.map +1 -1
- package/connection/index.js +4 -5
- package/connection/index.js.map +1 -1
- package/connection/pool.d.ts +186 -0
- package/connection/pool.d.ts.map +1 -0
- package/connection/pool.js +361 -0
- package/connection/pool.js.map +1 -0
- package/connection/reload.d.ts +14 -93
- package/connection/reload.d.ts.map +1 -1
- package/connection/reload.js +217 -81
- package/connection/reload.js.map +1 -1
- package/connection/stats.d.ts +3 -26
- package/connection/stats.d.ts.map +1 -1
- package/connection/stats.js.map +1 -1
- package/connection/transport.d.ts +0 -7
- package/connection/transport.d.ts.map +1 -1
- package/consume.d.ts +1 -43
- package/consume.d.ts.map +1 -1
- package/consume.js +1 -1
- package/consume.js.map +1 -1
- package/error.d.ts +180 -29
- package/error.d.ts.map +1 -1
- package/error.js +331 -16
- package/error.js.map +1 -1
- package/errors.d.ts +7 -0
- package/errors.d.ts.map +1 -0
- package/errors.js +8 -0
- package/errors.js.map +1 -0
- package/group.d.ts +9 -43
- package/group.d.ts.map +1 -1
- package/group.js +284 -69
- package/group.js.map +1 -1
- package/hop.d.ts +115 -0
- package/hop.d.ts.map +1 -0
- package/hop.js +119 -0
- package/hop.js.map +1 -0
- package/ietf/adapter.d.ts +5 -1
- package/ietf/adapter.d.ts.map +1 -1
- package/ietf/adapter.js +105 -60
- package/ietf/adapter.js.map +1 -1
- package/ietf/aliases.d.ts +1 -78
- package/ietf/aliases.d.ts.map +1 -1
- package/ietf/cluster.d.ts +9 -123
- package/ietf/cluster.d.ts.map +1 -1
- package/ietf/cluster.js +84 -44
- package/ietf/cluster.js.map +1 -1
- package/ietf/connection.d.ts +6 -72
- package/ietf/connection.d.ts.map +1 -1
- package/ietf/connection.js +62 -56
- package/ietf/connection.js.map +1 -1
- package/ietf/error.d.ts +11 -0
- package/ietf/error.d.ts.map +1 -0
- package/ietf/error.js +193 -0
- package/ietf/error.js.map +1 -0
- package/ietf/fetch.d.ts +7 -20
- package/ietf/fetch.d.ts.map +1 -1
- package/ietf/fetch.js +52 -22
- package/ietf/fetch.js.map +1 -1
- package/ietf/filter.d.ts +2 -0
- package/ietf/filter.d.ts.map +1 -1
- package/ietf/filter.js +10 -1
- package/ietf/filter.js.map +1 -1
- package/ietf/goaway.d.ts.map +1 -1
- package/ietf/goaway.js +22 -5
- package/ietf/goaway.js.map +1 -1
- package/ietf/hidden.d.ts +2 -0
- package/ietf/hidden.d.ts.map +1 -0
- package/ietf/hidden.js +30 -0
- package/ietf/hidden.js.map +1 -0
- package/ietf/index.d.ts +2 -0
- package/ietf/index.d.ts.map +1 -1
- package/ietf/index.js +2 -0
- package/ietf/index.js.map +1 -1
- package/ietf/object.d.ts +15 -8
- package/ietf/object.d.ts.map +1 -1
- package/ietf/object.js +51 -36
- package/ietf/object.js.map +1 -1
- package/ietf/parameters.d.ts +14 -2
- package/ietf/parameters.d.ts.map +1 -1
- package/ietf/parameters.js +97 -29
- package/ietf/parameters.js.map +1 -1
- package/ietf/properties.d.ts +1 -0
- package/ietf/properties.d.ts.map +1 -1
- package/ietf/properties.js +14 -0
- package/ietf/properties.js.map +1 -1
- package/ietf/publish.d.ts +19 -2
- package/ietf/publish.d.ts.map +1 -1
- package/ietf/publish.js +40 -6
- package/ietf/publish.js.map +1 -1
- package/ietf/publish_namespace.d.ts +25 -0
- package/ietf/publish_namespace.d.ts.map +1 -1
- package/ietf/publish_namespace.js +63 -0
- package/ietf/publish_namespace.js.map +1 -1
- package/ietf/publisher.d.ts +1 -82
- package/ietf/publisher.d.ts.map +1 -1
- package/ietf/publisher.js +483 -235
- package/ietf/publisher.js.map +1 -1
- package/ietf/solicit.d.ts +1 -40
- package/ietf/solicit.d.ts.map +1 -1
- package/ietf/subscribe.d.ts +8 -6
- package/ietf/subscribe.d.ts.map +1 -1
- package/ietf/subscribe.js +33 -27
- package/ietf/subscribe.js.map +1 -1
- package/ietf/subscribe_namespace.d.ts +8 -2
- package/ietf/subscribe_namespace.d.ts.map +1 -1
- package/ietf/subscribe_namespace.js +18 -8
- package/ietf/subscribe_namespace.js.map +1 -1
- package/ietf/subscriber.d.ts +1 -65
- package/ietf/subscriber.d.ts.map +1 -1
- package/ietf/subscriber.js +337 -123
- package/ietf/subscriber.js.map +1 -1
- package/ietf/token.d.ts +2 -0
- package/ietf/token.d.ts.map +1 -0
- package/ietf/token.js +99 -0
- package/ietf/token.js.map +1 -0
- package/ietf/track.d.ts +4 -0
- package/ietf/track.d.ts.map +1 -1
- package/ietf/track.js +6 -20
- package/ietf/track.js.map +1 -1
- package/ietf/version.d.ts +12 -1
- package/ietf/version.d.ts.map +1 -1
- package/ietf/version.js +13 -0
- package/ietf/version.js.map +1 -1
- package/index.d.ts +12 -7
- package/index.d.ts.map +1 -1
- package/index.js +10 -5
- package/index.js.map +1 -1
- package/internal.d.ts +115 -1
- package/internal.d.ts.map +1 -1
- package/internal.js +108 -0
- package/internal.js.map +1 -1
- package/lite/announce.d.ts +60 -10
- package/lite/announce.d.ts.map +1 -1
- package/lite/announce.js +176 -31
- package/lite/announce.js.map +1 -1
- package/lite/connection.d.ts +9 -59
- package/lite/connection.d.ts.map +1 -1
- package/lite/connection.js +34 -35
- package/lite/connection.js.map +1 -1
- package/lite/datagram.d.ts +3 -2
- package/lite/datagram.d.ts.map +1 -1
- package/lite/datagram.js +7 -8
- package/lite/datagram.js.map +1 -1
- package/lite/fetch.d.ts +15 -1
- package/lite/fetch.d.ts.map +1 -1
- package/lite/fetch.js +39 -7
- package/lite/fetch.js.map +1 -1
- package/lite/goaway.d.ts.map +1 -1
- package/lite/goaway.js +7 -1
- package/lite/goaway.js.map +1 -1
- package/lite/group.d.ts +30 -12
- package/lite/group.d.ts.map +1 -1
- package/lite/group.js +68 -26
- package/lite/group.js.map +1 -1
- package/lite/message.d.ts +2 -2
- package/lite/message.d.ts.map +1 -1
- package/lite/message.js +14 -5
- package/lite/message.js.map +1 -1
- package/lite/priority.d.ts +1 -61
- package/lite/priority.d.ts.map +1 -1
- package/lite/priority.js +4 -5
- package/lite/priority.js.map +1 -1
- package/lite/publisher.d.ts +1 -69
- package/lite/publisher.d.ts.map +1 -1
- package/lite/publisher.js +630 -262
- package/lite/publisher.js.map +1 -1
- package/lite/setup.d.ts +8 -8
- package/lite/setup.d.ts.map +1 -1
- package/lite/setup.js +33 -30
- package/lite/setup.js.map +1 -1
- package/lite/subscribe.d.ts +71 -17
- package/lite/subscribe.d.ts.map +1 -1
- package/lite/subscribe.js +205 -53
- package/lite/subscribe.js.map +1 -1
- package/lite/subscriber.d.ts +15 -56
- package/lite/subscriber.d.ts.map +1 -1
- package/lite/subscriber.js +451 -247
- package/lite/subscriber.js.map +1 -1
- package/lite/track.d.ts +4 -10
- package/lite/track.d.ts.map +1 -1
- package/lite/track.js +34 -29
- package/lite/track.js.map +1 -1
- package/lite/version.d.ts +42 -6
- package/lite/version.d.ts.map +1 -1
- package/lite/version.js +126 -10
- package/lite/version.js.map +1 -1
- package/origin.d.ts +256 -29
- package/origin.d.ts.map +1 -1
- package/origin.js +1427 -37
- package/origin.js.map +1 -1
- package/package.json +8 -3
- package/path.d.ts +25 -7
- package/path.d.ts.map +1 -1
- package/path.js +5 -3
- package/path.js.map +1 -1
- package/stream.d.ts +73 -14
- package/stream.d.ts.map +1 -1
- package/stream.js +372 -141
- package/stream.js.map +1 -1
- package/tail.d.ts +18 -0
- package/tail.d.ts.map +1 -0
- package/tail.js +167 -0
- package/tail.js.map +1 -0
- package/time.d.ts +15 -2
- package/time.d.ts.map +1 -1
- package/time.js +28 -9
- package/time.js.map +1 -1
- package/track.d.ts +211 -83
- package/track.d.ts.map +1 -1
- package/track.js +816 -205
- package/track.js.map +1 -1
- package/util/abort.d.ts +2 -0
- package/util/abort.d.ts.map +1 -0
- package/util/abort.js +20 -0
- package/util/abort.js.map +1 -0
- package/util/log.d.ts +5 -0
- package/util/log.d.ts.map +1 -0
- package/util/log.js +17 -0
- package/util/log.js.map +1 -0
- package/util/u64.d.ts +39 -0
- package/util/u64.d.ts.map +1 -0
- package/util/u64.js +83 -0
- package/util/u64.js.map +1 -0
- package/util/varint.d.ts +29 -0
- package/util/varint.d.ts.map +1 -0
- package/util/varint.js +198 -0
- package/util/varint.js.map +1 -0
- package/varint.d.ts +10 -6
- package/varint.d.ts.map +1 -1
- package/varint.js +40 -237
- package/varint.js.map +1 -1
- package/wire.d.ts +80 -0
- package/wire.d.ts.map +1 -0
- package/wire.js +32 -0
- package/wire.js.map +1 -0
- package/zod.d.ts +1 -1
- package/zod.d.ts.map +1 -1
- package/zod.js.map +1 -1
- package/mock.d.ts +0 -66
- package/mock.d.ts.map +0 -1
- package/mock.js +0 -243
- 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
|
-
*
|
|
10
|
+
* Codes a peer sends when terminating the session, mirroring the Rust `Session`.
|
|
9
11
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
31
|
-
/** The code
|
|
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(
|
|
35
|
-
|
|
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
|
-
* @
|
|
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
|
-
*
|
|
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
|
-
|
|
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
package/errors.d.ts.map
ADDED
|
@@ -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
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
|
|
6
|
+
import { type GetPromise } from "@norskvideo/moq-signals";
|
|
7
7
|
import { Timestamp } from "./time";
|
|
8
|
-
/** Maximum bytes of frames
|
|
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
|
|
11
|
-
export declare const MAX_GROUP_FRAMES =
|
|
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
|
|
33
|
-
*
|
|
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
|
|
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
|
|
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":"AAAA;;;;GAIG;AACH,OAAO,
|
|
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"}
|