@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/lite/group.js
CHANGED
|
@@ -1,46 +1,88 @@
|
|
|
1
1
|
/* @ts-self-types="./group.d.ts" */
|
|
2
|
+
import { race } from "@norskvideo/moq-signals";
|
|
3
|
+
import * as Time from "../time.js";
|
|
2
4
|
import * as Message from "./message.js";
|
|
5
|
+
import { hasFrameBounds } from "./version.js";
|
|
3
6
|
export class Group {
|
|
4
7
|
subscribe;
|
|
5
8
|
sequence;
|
|
6
|
-
|
|
9
|
+
/**
|
|
10
|
+
* The index of the first frame on this stream within the group.
|
|
11
|
+
*
|
|
12
|
+
* 0 (the common case) means the stream carries the group from its beginning. A
|
|
13
|
+
* higher value means the leading frames are not here, either because the
|
|
14
|
+
* subscription started partway into the group or because the publisher only holds
|
|
15
|
+
* the tail. Lite-06+; older versions always start at 0.
|
|
16
|
+
*/
|
|
17
|
+
frameStart;
|
|
18
|
+
constructor({ subscribe, sequence, frameStart = 0, }) {
|
|
7
19
|
this.subscribe = subscribe;
|
|
8
20
|
this.sequence = sequence;
|
|
21
|
+
this.frameStart = frameStart;
|
|
9
22
|
}
|
|
10
|
-
async #encode(w) {
|
|
23
|
+
async #encode(w, version) {
|
|
11
24
|
await w.u62(this.subscribe);
|
|
12
25
|
await w.u53(this.sequence);
|
|
26
|
+
if (hasFrameBounds(version)) {
|
|
27
|
+
await w.u53(this.frameStart);
|
|
28
|
+
}
|
|
29
|
+
else if (this.frameStart !== 0) {
|
|
30
|
+
// The peer would number the frames from 0 and silently misalign the group.
|
|
31
|
+
throw new Error("frame offsets not supported for this version");
|
|
32
|
+
}
|
|
13
33
|
}
|
|
14
|
-
static async #decode(r) {
|
|
15
|
-
|
|
34
|
+
static async #decode(r, version) {
|
|
35
|
+
const subscribe = await r.u62();
|
|
36
|
+
const sequence = await r.u53();
|
|
37
|
+
const frameStart = hasFrameBounds(version) ? await r.u53() : 0;
|
|
38
|
+
return new Group({ subscribe, sequence, frameStart });
|
|
16
39
|
}
|
|
17
|
-
async encode(w) {
|
|
18
|
-
return Message.encode(w, this.#encode
|
|
40
|
+
async encode(w, version) {
|
|
41
|
+
return Message.encode(w, (w) => this.#encode(w, version));
|
|
19
42
|
}
|
|
20
|
-
static async decode(r) {
|
|
21
|
-
return Message.decode(r, Group.#decode);
|
|
43
|
+
static async decode(r, version) {
|
|
44
|
+
return Message.decode(r, (r) => Group.#decode(r, version));
|
|
22
45
|
}
|
|
23
|
-
static async decodeMaybe(r) {
|
|
24
|
-
return Message.decodeMaybe(r, Group.#decode);
|
|
46
|
+
static async decodeMaybe(r, version) {
|
|
47
|
+
return Message.decodeMaybe(r, (r) => Group.#decode(r, version));
|
|
25
48
|
}
|
|
26
49
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
return
|
|
50
|
+
/** Decode an unsigned zigzag varint back to a signed delta (mirrors Rust `VarInt::to_zigzag`). */
|
|
51
|
+
function unzigzag(v) {
|
|
52
|
+
return (v >> 1n) ^ -(v & 1n);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* A synchronous decode for one frame of a group or FETCH response stream.
|
|
56
|
+
*
|
|
57
|
+
* A non-zero `scale` means every frame is prefixed with a zigzag-delta timestamp (the lite-05
|
|
58
|
+
* FRAME format), decoded into a Timestamp at that scale. Scale 0 (pre-lite-05) carries no
|
|
59
|
+
* timestamp, so frames are wall-clock stamped on arrival.
|
|
60
|
+
*/
|
|
61
|
+
export function frameDecoder(scale) {
|
|
62
|
+
if (scale === 0) {
|
|
63
|
+
return (c) => ({ payload: c.read(c.u53()), timestamp: Time.Timestamp.now() });
|
|
41
64
|
}
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
const timescale = Time.Timescale(scale);
|
|
66
|
+
let prevTs = 0n;
|
|
67
|
+
return (c) => {
|
|
68
|
+
const delta = unzigzag(c.u62());
|
|
69
|
+
const payload = c.read(c.u53());
|
|
70
|
+
// After the last read, so a decode that ran short and gets retried adds the delta once.
|
|
71
|
+
prevTs += delta;
|
|
72
|
+
return { payload, timestamp: new Time.Timestamp(Number(prevTs), timescale) };
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
/** Write a group stream's frames into `producer` until the stream ends or the producer closes. */
|
|
76
|
+
export async function readFrames(stream, producer, scale) {
|
|
77
|
+
const decode = frameDecoder(scale);
|
|
78
|
+
for (;;) {
|
|
79
|
+
// Every frame already buffered is written without an await, so the reader wakes once
|
|
80
|
+
// per batch rather than once per frame. Only the group's own stream ends it: a track
|
|
81
|
+
// that closes first has already closed (or aborted) this group through its cache.
|
|
82
|
+
const frame = stream.tryDecode(decode) ?? (await race([stream.decodeMaybe(decode), producer.closed]));
|
|
83
|
+
if (!frame || frame instanceof Error)
|
|
84
|
+
return;
|
|
85
|
+
producer.writeFrame(frame);
|
|
44
86
|
}
|
|
45
87
|
}
|
|
46
88
|
//# sourceMappingURL=group.js.map
|
package/lite/group.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"group.js","sourceRoot":"","sources":["../../src/lite/group.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"group.js","sourceRoot":"","sources":["../../src/lite/group.ts"],"names":[],"mappings":"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","sourcesContent":["import { race } from \"@moq/signals\";\nimport type * as netGroup from \"../group.ts\";\nimport type { Cursor, Reader, Writer } from \"../stream.ts\";\nimport * as Time from \"../time.ts\";\nimport * as Message from \"./message.ts\";\nimport { hasFrameBounds, type Version } from \"./version.ts\";\n\nexport class Group {\n\tsubscribe: bigint;\n\tsequence: number;\n\n\t/**\n\t * The index of the first frame on this stream within the group.\n\t *\n\t * 0 (the common case) means the stream carries the group from its beginning. A\n\t * higher value means the leading frames are not here, either because the\n\t * subscription started partway into the group or because the publisher only holds\n\t * the tail. Lite-06+; older versions always start at 0.\n\t */\n\tframeStart: number;\n\n\tconstructor({\n\t\tsubscribe,\n\t\tsequence,\n\t\tframeStart = 0,\n\t}: {\n\t\tsubscribe: bigint;\n\t\tsequence: number;\n\t\tframeStart?: number;\n\t}) {\n\t\tthis.subscribe = subscribe;\n\t\tthis.sequence = sequence;\n\t\tthis.frameStart = frameStart;\n\t}\n\n\tasync #encode(w: Writer, version: Version) {\n\t\tawait w.u62(this.subscribe);\n\t\tawait w.u53(this.sequence);\n\t\tif (hasFrameBounds(version)) {\n\t\t\tawait w.u53(this.frameStart);\n\t\t} else if (this.frameStart !== 0) {\n\t\t\t// The peer would number the frames from 0 and silently misalign the group.\n\t\t\tthrow new Error(\"frame offsets not supported for this version\");\n\t\t}\n\t}\n\n\tstatic async #decode(r: Reader, version: Version): Promise<Group> {\n\t\tconst subscribe = await r.u62();\n\t\tconst sequence = await r.u53();\n\t\tconst frameStart = hasFrameBounds(version) ? await r.u53() : 0;\n\t\treturn new Group({ subscribe, sequence, frameStart });\n\t}\n\n\tasync encode(w: Writer, version: Version): Promise<void> {\n\t\treturn Message.encode(w, (w) => this.#encode(w, version));\n\t}\n\n\tstatic async decode(r: Reader, version: Version): Promise<Group> {\n\t\treturn Message.decode(r, (r) => Group.#decode(r, version));\n\t}\n\n\tstatic async decodeMaybe(r: Reader, version: Version): Promise<Group | undefined> {\n\t\treturn Message.decodeMaybe(r, (r) => Group.#decode(r, version));\n\t}\n}\n\n/** Decode an unsigned zigzag varint back to a signed delta (mirrors Rust `VarInt::to_zigzag`). */\nfunction unzigzag(v: bigint): bigint {\n\treturn (v >> 1n) ^ -(v & 1n);\n}\n\n/**\n * A synchronous decode for one frame of a group or FETCH response stream.\n *\n * A non-zero `scale` means every frame is prefixed with a zigzag-delta timestamp (the lite-05\n * FRAME format), decoded into a Timestamp at that scale. Scale 0 (pre-lite-05) carries no\n * timestamp, so frames are wall-clock stamped on arrival.\n */\nexport function frameDecoder(scale: number): (c: Cursor) => netGroup.Frame {\n\tif (scale === 0) {\n\t\treturn (c) => ({ payload: c.read(c.u53()), timestamp: Time.Timestamp.now() });\n\t}\n\n\tconst timescale = Time.Timescale(scale);\n\tlet prevTs = 0n;\n\treturn (c) => {\n\t\tconst delta = unzigzag(c.u62());\n\t\tconst payload = c.read(c.u53());\n\t\t// After the last read, so a decode that ran short and gets retried adds the delta once.\n\t\tprevTs += delta;\n\t\treturn { payload, timestamp: new Time.Timestamp(Number(prevTs), timescale) };\n\t};\n}\n\n/** Write a group stream's frames into `producer` until the stream ends or the producer closes. */\nexport async function readFrames(stream: Reader, producer: netGroup.Producer, scale: number): Promise<void> {\n\tconst decode = frameDecoder(scale);\n\n\tfor (;;) {\n\t\t// Every frame already buffered is written without an await, so the reader wakes once\n\t\t// per batch rather than once per frame. Only the group's own stream ends it: a track\n\t\t// that closes first has already closed (or aborted) this group through its cache.\n\t\tconst frame = stream.tryDecode(decode) ?? (await race([stream.decodeMaybe(decode), producer.closed]));\n\t\tif (!frame || frame instanceof Error) return;\n\t\tproducer.writeFrame(frame);\n\t}\n}\n"]}
|
package/lite/message.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Reader, Writer } from "../stream";
|
|
2
|
-
export declare function encode(writer: Writer, f: (w: Writer) => Promise<void
|
|
3
|
-
export declare function decode<T>(reader: Reader, f: (r: Reader) => Promise<T
|
|
2
|
+
export declare function encode(writer: Writer, f: (w: Writer) => Promise<void>, max?: number): Promise<void>;
|
|
3
|
+
export declare function decode<T>(reader: Reader, f: (r: Reader) => Promise<T>, max?: number): Promise<T>;
|
|
4
4
|
export declare function decodeMaybe<T>(reader: Reader, f: (r: Reader) => Promise<T>): Promise<T | undefined>;
|
|
5
5
|
//# sourceMappingURL=message.d.ts.map
|
package/lite/message.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/lite/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["../../src/lite/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAM9C,wBAAsB,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,SAAW,iBA0C3F;AAGD,wBAAsB,MAAM,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,SAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAgBxG;AAED,wBAAsB,WAAW,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAGzG"}
|
package/lite/message.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
/* @ts-self-types="./message.d.ts" */
|
|
2
2
|
import { Reader, Writer } from "../stream.js";
|
|
3
|
-
|
|
3
|
+
/** The largest message body either side accepts, matching the Rust implementation. */
|
|
4
|
+
const MAX_SIZE = 64 * 1024 * 1024;
|
|
5
|
+
// Encodes a message with a varint size prefix, refusing a body the peer would reject.
|
|
6
|
+
export async function encode(writer, f, max = MAX_SIZE) {
|
|
4
7
|
let scratch = new Uint8Array();
|
|
5
8
|
const temp = new Writer(new WritableStream({
|
|
6
9
|
write(chunk) {
|
|
@@ -22,20 +25,26 @@ export async function encode(writer, f) {
|
|
|
22
25
|
scratch.set(chunk, needed - chunk.byteLength);
|
|
23
26
|
}
|
|
24
27
|
},
|
|
25
|
-
}));
|
|
28
|
+
}), writer.version);
|
|
26
29
|
await f(temp);
|
|
27
30
|
temp.close();
|
|
28
31
|
await temp.closed;
|
|
32
|
+
if (scratch.byteLength > max) {
|
|
33
|
+
throw new Error(`message too large: ${scratch.byteLength} bytes (max ${max})`);
|
|
34
|
+
}
|
|
29
35
|
await writer.u53(scratch.byteLength);
|
|
30
36
|
if (scratch.byteLength > 0) {
|
|
31
37
|
await writer.write(scratch);
|
|
32
38
|
}
|
|
33
39
|
}
|
|
34
|
-
// Reads a message with a varint size prefix.
|
|
35
|
-
export async function decode(reader, f) {
|
|
40
|
+
// Reads a message with a varint size prefix, refusing an oversized body before reading it.
|
|
41
|
+
export async function decode(reader, f, max = MAX_SIZE) {
|
|
36
42
|
const size = await reader.u53();
|
|
43
|
+
if (size > max) {
|
|
44
|
+
throw new Error(`message too large: ${size} bytes (max ${max})`);
|
|
45
|
+
}
|
|
37
46
|
const data = await reader.read(size);
|
|
38
|
-
const limit = new Reader(undefined, data);
|
|
47
|
+
const limit = new Reader(undefined, data, reader.version);
|
|
39
48
|
const msg = await f(limit);
|
|
40
49
|
// Check that we consumed exactly the right number of bytes
|
|
41
50
|
if (!(await limit.done())) {
|
package/lite/message.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../src/lite/message.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,MAAc,EAAE,CAA+B;
|
|
1
|
+
{"version":3,"file":"message.js","sourceRoot":"","sources":["../../src/lite/message.ts"],"names":[],"mappings":"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","sourcesContent":["import { Reader, Writer } from \"../stream.ts\";\n\n/** The largest message body either side accepts, matching the Rust implementation. */\nconst MAX_SIZE = 64 * 1024 * 1024;\n\n// Encodes a message with a varint size prefix, refusing a body the peer would reject.\nexport async function encode(writer: Writer, f: (w: Writer) => Promise<void>, max = MAX_SIZE) {\n\tlet scratch = new Uint8Array();\n\n\tconst temp = new Writer(\n\t\tnew WritableStream({\n\t\t\twrite(chunk: Uint8Array) {\n\t\t\t\tconst needed = scratch.byteLength + chunk.byteLength;\n\t\t\t\tif (needed > scratch.buffer.byteLength) {\n\t\t\t\t\t// Resize the buffer to the needed size.\n\t\t\t\t\tconst capacity = Math.max(needed, scratch.buffer.byteLength * 2);\n\t\t\t\t\tconst newBuffer = new ArrayBuffer(capacity);\n\t\t\t\t\tconst newScratch = new Uint8Array(newBuffer, 0, needed);\n\n\t\t\t\t\t// Copy the old data into the new buffer.\n\t\t\t\t\tnewScratch.set(scratch);\n\n\t\t\t\t\t// Copy the new chunk into the new buffer.\n\t\t\t\t\tnewScratch.set(chunk, scratch.byteLength);\n\n\t\t\t\t\tscratch = newScratch;\n\t\t\t\t} else {\n\t\t\t\t\t// Copy chunk data into buffer\n\t\t\t\t\tscratch = new Uint8Array(scratch.buffer, 0, needed);\n\t\t\t\t\tscratch.set(chunk, needed - chunk.byteLength);\n\t\t\t\t}\n\t\t\t},\n\t\t}),\n\t\twriter.version,\n\t);\n\n\tawait f(temp);\n\ttemp.close();\n\tawait temp.closed;\n\n\tif (scratch.byteLength > max) {\n\t\tthrow new Error(`message too large: ${scratch.byteLength} bytes (max ${max})`);\n\t}\n\n\tawait writer.u53(scratch.byteLength);\n\tif (scratch.byteLength > 0) {\n\t\tawait writer.write(scratch);\n\t}\n}\n\n// Reads a message with a varint size prefix, refusing an oversized body before reading it.\nexport async function decode<T>(reader: Reader, f: (r: Reader) => Promise<T>, max = MAX_SIZE): Promise<T> {\n\tconst size = await reader.u53();\n\tif (size > max) {\n\t\tthrow new Error(`message too large: ${size} bytes (max ${max})`);\n\t}\n\tconst data = await reader.read(size);\n\n\tconst limit = new Reader(undefined, data, reader.version);\n\tconst msg = await f(limit);\n\n\t// Check that we consumed exactly the right number of bytes\n\tif (!(await limit.done())) {\n\t\tthrow new Error(\"Message decoding consumed too few bytes\");\n\t}\n\n\treturn msg;\n}\n\nexport async function decodeMaybe<T>(reader: Reader, f: (r: Reader) => Promise<T>): Promise<T | undefined> {\n\tif (await reader.done()) return;\n\treturn await decode(reader, f);\n}\n"]}
|
package/lite/priority.d.ts
CHANGED
|
@@ -3,65 +3,5 @@
|
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
import type * as track from "../track";
|
|
8
|
-
/**
|
|
9
|
-
* What to rank a group stream by.
|
|
10
|
-
*
|
|
11
|
-
* @internal
|
|
12
|
-
*/
|
|
13
|
-
export interface Rank {
|
|
14
|
-
/** The subscriber's track priority, which outweighs everything below it. */
|
|
15
|
-
priority: number;
|
|
16
|
-
/**
|
|
17
|
-
* Where the group sits in the queue its own subscription wants sent, 0 being the one to
|
|
18
|
-
* send next. Defaults to 0, which is right for a stream that is the only one of its kind.
|
|
19
|
-
*/
|
|
20
|
-
position?: number;
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* The transport send order for a group, where HIGHER values are transmitted first.
|
|
24
|
-
*
|
|
25
|
-
* A higher `priority` always wins. Below it, a group is ranked by its `position` in its own
|
|
26
|
-
* subscription rather than by its sequence, so two tracks at the same priority interleave:
|
|
27
|
-
* each one's next group ties with the other's, whatever their group numbering. Comparing
|
|
28
|
-
* sequences instead would let a track that had been running longer, or that numbered its
|
|
29
|
-
* groups from a clock, starve one that started later.
|
|
30
|
-
*
|
|
31
|
-
* @internal
|
|
32
|
-
*/
|
|
33
|
-
export declare function sendOrder({ priority, position }: Rank): number;
|
|
34
|
-
/**
|
|
35
|
-
* Ranks one subscription's group streams against each other, and re-ranks them whenever the
|
|
36
|
-
* set changes or the subscription is updated.
|
|
37
|
-
*
|
|
38
|
-
* A group's rank is its position among the groups in flight, so which group goes first is
|
|
39
|
-
* decided here rather than by arithmetic on sequence numbers. Newest-first by default, since
|
|
40
|
-
* that is what live playback wants; an `ordered` subscription is playing through in sequence,
|
|
41
|
-
* so it gets the oldest group first instead.
|
|
42
|
-
*
|
|
43
|
-
* One listener covers the whole subscription. A listener per group would instead pile up on
|
|
44
|
-
* it, and a track that stalls with many groups open would trip the signals leak guard.
|
|
45
|
-
*
|
|
46
|
-
* @internal
|
|
47
|
-
*/
|
|
48
|
-
export declare class Priority {
|
|
49
|
-
#private;
|
|
50
|
-
/** Follow `track`'s subscription until {@link close}. */
|
|
51
|
-
constructor(track: track.Subscriber);
|
|
52
|
-
/** The send order for a group at `sequence`, whether or not it has a stream yet. */
|
|
53
|
-
rank(sequence: number): number;
|
|
54
|
-
/** Rank a group's stream now, and again whenever the ranking changes, until {@link remove}. */
|
|
55
|
-
add(stream: Writer, sequence: number): void;
|
|
56
|
-
/** Stop ranking a finished group's stream, promoting whatever was queued behind it. */
|
|
57
|
-
remove(stream: Writer): void;
|
|
58
|
-
/**
|
|
59
|
-
* Stop taking new groups, and release the subscription listener once the last one leaves.
|
|
60
|
-
*
|
|
61
|
-
* The track can finish while several groups are still draining, and those are the ones the
|
|
62
|
-
* subscriber is still waiting on. They keep being promoted as the groups ahead of them
|
|
63
|
-
* finish, rather than being stranded at whatever position they held when the track ended.
|
|
64
|
-
*/
|
|
65
|
-
close(): void;
|
|
66
|
-
}
|
|
6
|
+
export {};
|
|
67
7
|
//# sourceMappingURL=priority.d.ts.map
|
package/lite/priority.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"priority.d.ts","sourceRoot":"","sources":["../../src/lite/priority.ts"],"names":[],"mappings":"AAAA;;;;GAIG
|
|
1
|
+
{"version":3,"file":"priority.d.ts","sourceRoot":"","sources":["../../src/lite/priority.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
package/lite/priority.js
CHANGED
|
@@ -36,9 +36,9 @@ function clamp(value, max) {
|
|
|
36
36
|
* set changes or the subscription is updated.
|
|
37
37
|
*
|
|
38
38
|
* A group's rank is its position among the groups in flight, so which group goes first is
|
|
39
|
-
* decided here rather than by arithmetic on sequence numbers. Newest
|
|
40
|
-
* that
|
|
41
|
-
*
|
|
39
|
+
* decided here rather than by arithmetic on sequence numbers. Newest first, always: under
|
|
40
|
+
* congestion that sheds the backlog rather than the live edge, and a subscriber wanting
|
|
41
|
+
* sequence order reorders locally rather than asking the wire for it.
|
|
42
42
|
*
|
|
43
43
|
* One listener covers the whole subscription. A listener per group would instead pile up on
|
|
44
44
|
* it, and a track that stalls with many groups open would trip the signals leak guard.
|
|
@@ -96,10 +96,9 @@ export class Priority {
|
|
|
96
96
|
// Sequences are unique within a subscription, so a group already registered never counts
|
|
97
97
|
// itself.
|
|
98
98
|
#position(sequence) {
|
|
99
|
-
const ordered = this.#track.subscription.peek()?.ordered ?? false;
|
|
100
99
|
let ahead = 0;
|
|
101
100
|
for (const other of this.#streams.values()) {
|
|
102
|
-
if (
|
|
101
|
+
if (other > sequence)
|
|
103
102
|
ahead++;
|
|
104
103
|
}
|
|
105
104
|
return ahead;
|
package/lite/priority.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"priority.js","sourceRoot":"","sources":["../../src/lite/priority.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH,0FAA0F;AAC1F,0FAA0F;AAC1F,8FAA8F;AAC9F,+FAA+F;AAC/F,kCAAkC;AAClC,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,CAAC;AAE3B,0DAA0D;AAC1D,MAAM,YAAY,GAAG,IAAI,CAAC;AAkB1B;;;;;;;;;;GAUG;AACH,MAAM,UAAU,SAAS,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG,CAAC,EAAQ;IACzD,OAAO,CAAC,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC,GAAG,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAE,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC7G,CAAC;AAED,8FAA8F;AAC9F,6EAA6E;AAC7E,SAAS,KAAK,CAAC,KAAa,EAAE,GAAW;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,QAAQ;IACpB,MAAM,CAAmB;IACzB,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IACrC,QAAQ,CAAU;IAClB,OAAO,GAAG,KAAK,CAAC;IAEhB,yDAAyD;IACzD,YAAY,KAAuB;QAClC,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;QACpB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,YAAY,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,oFAAoF;IACpF,IAAI,CAAC,QAAgB;QACpB,OAAO,SAAS,CAAC;YAChB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,QAAQ,IAAI,CAAC;YACxD,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;SAClC,CAAC,CAAC;IACJ,CAAC;IAED,+FAA+F;IAC/F,GAAG,CAAC,MAAc,EAAE,QAAgB;QACnC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAEpC,oFAAoF;QACpF,8DAA8D;QAC9D,IAAI,CAAC,OAAO,EAAE,CAAC;IAChB,CAAC;IAED,uFAAuF;IACvF,MAAM,CAAC,MAAc;QACpB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;YAAE,OAAO;QAE1C,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC9C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACjB,CAAC;aAAM,CAAC;YACP,IAAI,CAAC,OAAO,EAAE,CAAC;QAChB,CAAC;IACF,CAAC;IAED;;;;;;OAMG;IACH,KAAK;QACJ,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;YAAE,IAAI,CAAC,QAAQ,EAAE,CAAC;IAC/C,CAAC;IAED,qFAAqF;IACrF,yFAAyF;IACzF,UAAU;IACV,SAAS,CAAC,QAAgB;QACzB,
|
|
1
|
+
{"version":3,"file":"priority.js","sourceRoot":"","sources":["../../src/lite/priority.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Stream prioritization for moq-lite, mirroring the Rust `lite::priority` queue.\n *\n * @module\n */\n\nimport type { Dispose } from \"@moq/signals\";\nimport type { Writer } from \"../stream.ts\";\nimport type * as track from \"../track.ts\";\n\n// The transport compares send orders as a single number, so the two ranks are packed into\n// disjoint ranges: the track priority takes the high bits so it always dominates, and the\n// position below it. Group data stays negative so protocol streams at the transport's default\n// order of 0 always make progress. A u8 priority and 44-bit position fill the negative half of\n// the safe integer range exactly.\nconst GROUP_SPAN = 2 ** 44;\n\n/** The highest track priority the wire carries (a u8). */\nconst MAX_PRIORITY = 0xff;\n\n/**\n * What to rank a group stream by.\n *\n * @internal\n */\nexport interface Rank {\n\t/** The subscriber's track priority, which outweighs everything below it. */\n\tpriority: number;\n\n\t/**\n\t * Where the group sits in the queue its own subscription wants sent, 0 being the one to\n\t * send next. Defaults to 0, which is right for a stream that is the only one of its kind.\n\t */\n\tposition?: number;\n}\n\n/**\n * The transport send order for a group, where HIGHER values are transmitted first.\n *\n * A higher `priority` always wins. Below it, a group is ranked by its `position` in its own\n * subscription rather than by its sequence, so two tracks at the same priority interleave:\n * each one's next group ties with the other's, whatever their group numbering. Comparing\n * sequences instead would let a track that had been running longer, or that numbered its\n * groups from a clock, starve one that started later.\n *\n * @internal\n */\nexport function sendOrder({ priority, position = 0 }: Rank): number {\n\treturn -((MAX_PRIORITY - clamp(priority, MAX_PRIORITY)) * GROUP_SPAN + clamp(position, GROUP_SPAN - 1) + 1);\n}\n\n// Both ranks are bounded: the priority by its wire type, the position by the space left below\n// it. Anything outside is a caller bug rather than a value worth preserving.\nfunction clamp(value: number, max: number): number {\n\treturn Math.min(Math.max(Math.trunc(value), 0), max);\n}\n\n/**\n * Ranks one subscription's group streams against each other, and re-ranks them whenever the\n * set changes or the subscription is updated.\n *\n * A group's rank is its position among the groups in flight, so which group goes first is\n * decided here rather than by arithmetic on sequence numbers. Newest first, always: under\n * congestion that sheds the backlog rather than the live edge, and a subscriber wanting\n * sequence order reorders locally rather than asking the wire for it.\n *\n * One listener covers the whole subscription. A listener per group would instead pile up on\n * it, and a track that stalls with many groups open would trip the signals leak guard.\n *\n * @internal\n */\nexport class Priority {\n\t#track: track.Subscriber;\n\t#streams = new Map<Writer, number>();\n\t#dispose: Dispose;\n\t#closed = false;\n\n\t/** Follow `track`'s subscription until {@link close}. */\n\tconstructor(track: track.Subscriber) {\n\t\tthis.#track = track;\n\t\tthis.#dispose = track.subscription.subscribe(() => this.#rerank());\n\t}\n\n\t/** The send order for a group at `sequence`, whether or not it has a stream yet. */\n\trank(sequence: number): number {\n\t\treturn sendOrder({\n\t\t\tpriority: this.#track.subscription.peek()?.priority ?? 0,\n\t\t\tposition: this.#position(sequence),\n\t\t});\n\t}\n\n\t/** Rank a group's stream now, and again whenever the ranking changes, until {@link remove}. */\n\tadd(stream: Writer, sequence: number) {\n\t\tthis.#streams.set(stream, sequence);\n\n\t\t// Also covers the stream itself: opening one can block on transport capacity, so an\n\t\t// update that landed while it did predates this registration.\n\t\tthis.#rerank();\n\t}\n\n\t/** Stop ranking a finished group's stream, promoting whatever was queued behind it. */\n\tremove(stream: Writer) {\n\t\tif (!this.#streams.delete(stream)) return;\n\n\t\tif (this.#closed && this.#streams.size === 0) {\n\t\t\tthis.#dispose();\n\t\t} else {\n\t\t\tthis.#rerank();\n\t\t}\n\t}\n\n\t/**\n\t * Stop taking new groups, and release the subscription listener once the last one leaves.\n\t *\n\t * The track can finish while several groups are still draining, and those are the ones the\n\t * subscriber is still waiting on. They keep being promoted as the groups ahead of them\n\t * finish, rather than being stranded at whatever position they held when the track ended.\n\t */\n\tclose() {\n\t\tthis.#closed = true;\n\t\tif (this.#streams.size === 0) this.#dispose();\n\t}\n\n\t// How many of this subscription's groups in flight should be sent before `sequence`.\n\t// Sequences are unique within a subscription, so a group already registered never counts\n\t// itself.\n\t#position(sequence: number): number {\n\t\tlet ahead = 0;\n\t\tfor (const other of this.#streams.values()) {\n\t\t\tif (other > sequence) ahead++;\n\t\t}\n\t\treturn ahead;\n\t}\n\n\t#rerank() {\n\t\tconst priority = this.#track.subscription.peek()?.priority ?? 0;\n\t\tfor (const [stream, sequence] of this.#streams) {\n\t\t\tstream.setPriority(sendOrder({ priority, position: this.#position(sequence) }));\n\t\t}\n\t}\n}\n"]}
|
package/lite/publisher.d.ts
CHANGED
|
@@ -1,70 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
import type { Origin } from "../origin";
|
|
3
|
-
import * as Path from "../path";
|
|
4
|
-
import { type Stream } from "../stream";
|
|
5
|
-
import { type AnnounceRequest } from "./announce";
|
|
6
|
-
import type { Fetch } from "./fetch";
|
|
7
|
-
import { type Subscribe } from "./subscribe";
|
|
8
|
-
import { type Track as TrackMessage } from "./track";
|
|
9
|
-
import { Version } from "./version";
|
|
10
|
-
/**
|
|
11
|
-
* Handles publishing broadcasts and managing their lifecycle.
|
|
12
|
-
*
|
|
13
|
-
* @internal
|
|
14
|
-
*/
|
|
15
|
-
export declare class Publisher {
|
|
16
|
-
#private;
|
|
17
|
-
readonly version: Version;
|
|
18
|
-
readonly origin: Origin;
|
|
19
|
-
/**
|
|
20
|
-
* Creates a new Publisher instance.
|
|
21
|
-
* @param quic - The WebTransport session to use
|
|
22
|
-
* @param version - Negotiated protocol version
|
|
23
|
-
* @param origin - Origin id shared with the Subscriber
|
|
24
|
-
*
|
|
25
|
-
* @internal
|
|
26
|
-
*/
|
|
27
|
-
constructor(quic: WebTransport, version: Version, origin: Origin);
|
|
28
|
-
/**
|
|
29
|
-
* Publishes a broadcast with any associated tracks.
|
|
30
|
-
* @param name - The broadcast to publish
|
|
31
|
-
*/
|
|
32
|
-
publish(path: Path.Valid, broadcast: broadcast.Producer): void;
|
|
33
|
-
/**
|
|
34
|
-
* Handles an announce interest message.
|
|
35
|
-
* @param msg - The announce interest message
|
|
36
|
-
* @param stream - The stream to write announcements to
|
|
37
|
-
*
|
|
38
|
-
* @internal
|
|
39
|
-
*/
|
|
40
|
-
runAnnounce(msg: AnnounceRequest, stream: Stream): Promise<void>;
|
|
41
|
-
/**
|
|
42
|
-
* Handles a subscribe message.
|
|
43
|
-
* @param msg - The subscribe message
|
|
44
|
-
* @param stream - The stream to write track data to
|
|
45
|
-
*
|
|
46
|
-
* @internal
|
|
47
|
-
*/
|
|
48
|
-
runSubscribe(msg: Subscribe, stream: Stream): Promise<void>;
|
|
49
|
-
/**
|
|
50
|
-
* Handles a FETCH stream by serving one group as bare frame records (lite-05+).
|
|
51
|
-
*
|
|
52
|
-
* @internal
|
|
53
|
-
*/
|
|
54
|
-
runFetch(msg: Fetch, stream: Stream): Promise<void>;
|
|
55
|
-
/**
|
|
56
|
-
* Answers a TRACK stream (0x6) with a single TRACK_INFO, then FINs.
|
|
57
|
-
*
|
|
58
|
-
* @internal
|
|
59
|
-
*/
|
|
60
|
-
runTrackInfo(msg: TrackMessage, stream: Stream): Promise<void>;
|
|
61
|
-
/**
|
|
62
|
-
* Handles a probe stream by periodically reporting estimated bitrate.
|
|
63
|
-
* @param stream - The probe bidi stream
|
|
64
|
-
*
|
|
65
|
-
* @internal
|
|
66
|
-
*/
|
|
67
|
-
runProbe(stream: Stream): Promise<void>;
|
|
68
|
-
close(): void;
|
|
69
|
-
}
|
|
1
|
+
export {};
|
|
70
2
|
//# sourceMappingURL=publisher.d.ts.map
|
package/lite/publisher.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../src/lite/publisher.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"publisher.d.ts","sourceRoot":"","sources":["../../src/lite/publisher.ts"],"names":[],"mappings":""}
|