@norskvideo/moq-net 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/announced.d.ts +111 -21
- package/announced.d.ts.map +1 -1
- package/announced.js +213 -40
- package/announced.js.map +1 -1
- package/broadcast.d.ts +80 -25
- package/broadcast.d.ts.map +1 -1
- package/broadcast.js +270 -49
- package/broadcast.js.map +1 -1
- package/connection/accept.d.ts +6 -0
- package/connection/accept.d.ts.map +1 -1
- package/connection/accept.js +42 -13
- package/connection/accept.js.map +1 -1
- package/connection/browser.d.ts +5 -0
- package/connection/browser.d.ts.map +1 -0
- package/connection/browser.js +31 -0
- package/connection/browser.js.map +1 -0
- package/connection/connect.d.ts +43 -1
- package/connection/connect.d.ts.map +1 -1
- package/connection/connect.js +118 -123
- package/connection/connect.js.map +1 -1
- package/connection/established.d.ts +43 -16
- package/connection/established.d.ts.map +1 -1
- package/connection/established.js.map +1 -1
- package/connection/handshake.d.ts +12 -1
- package/connection/handshake.d.ts.map +1 -1
- package/connection/handshake.js +30 -14
- package/connection/handshake.js.map +1 -1
- package/connection/index.d.ts +8 -0
- package/connection/index.d.ts.map +1 -1
- package/connection/index.js +6 -0
- package/connection/index.js.map +1 -1
- package/connection/reload.d.ts +66 -9
- package/connection/reload.d.ts.map +1 -1
- package/connection/reload.js +192 -66
- package/connection/reload.js.map +1 -1
- package/connection/stats.d.ts +92 -0
- package/connection/stats.d.ts.map +1 -0
- package/connection/stats.js +35 -0
- package/connection/stats.js.map +1 -0
- package/connection/transport.d.ts +10 -0
- package/connection/transport.d.ts.map +1 -0
- package/connection/transport.js +12 -0
- package/connection/transport.js.map +1 -0
- package/consume.d.ts +44 -0
- package/consume.d.ts.map +1 -0
- package/consume.js +60 -0
- package/consume.js.map +1 -0
- package/datagram.d.ts +23 -0
- package/datagram.d.ts.map +1 -0
- package/datagram.js +3 -0
- package/datagram.js.map +1 -0
- package/error.d.ts +76 -0
- package/error.d.ts.map +1 -0
- package/error.js +107 -0
- package/error.js.map +1 -0
- package/group.d.ts +143 -38
- package/group.d.ts.map +1 -1
- package/group.js +270 -68
- package/group.js.map +1 -1
- package/ietf/adapter.d.ts +2 -2
- package/ietf/adapter.d.ts.map +1 -1
- package/ietf/adapter.js +20 -10
- package/ietf/adapter.js.map +1 -1
- package/ietf/aliases.d.ts +73 -5
- package/ietf/aliases.d.ts.map +1 -1
- package/ietf/aliases.js +121 -14
- package/ietf/aliases.js.map +1 -1
- package/ietf/cluster.d.ts +141 -0
- package/ietf/cluster.d.ts.map +1 -0
- package/ietf/cluster.js +204 -0
- package/ietf/cluster.js.map +1 -0
- package/ietf/connection.d.ts +39 -6
- package/ietf/connection.d.ts.map +1 -1
- package/ietf/connection.js +73 -12
- package/ietf/connection.js.map +1 -1
- package/ietf/fetch.d.ts +17 -0
- package/ietf/fetch.d.ts.map +1 -1
- package/ietf/fetch.js +21 -0
- package/ietf/fetch.js.map +1 -1
- package/ietf/filter.d.ts +87 -0
- package/ietf/filter.d.ts.map +1 -0
- package/ietf/filter.js +292 -0
- package/ietf/filter.js.map +1 -0
- package/ietf/index.d.ts +2 -0
- package/ietf/index.d.ts.map +1 -1
- package/ietf/index.js +3 -0
- package/ietf/index.js.map +1 -1
- package/ietf/namespace.d.ts +6 -0
- package/ietf/namespace.d.ts.map +1 -1
- package/ietf/namespace.js +27 -2
- package/ietf/namespace.js.map +1 -1
- package/ietf/object.d.ts +54 -6
- package/ietf/object.d.ts.map +1 -1
- package/ietf/object.js +178 -25
- package/ietf/object.js.map +1 -1
- package/ietf/parameters.d.ts +35 -10
- package/ietf/parameters.d.ts.map +1 -1
- package/ietf/parameters.js +236 -45
- package/ietf/parameters.js.map +1 -1
- package/ietf/priority.d.ts +5 -0
- package/ietf/priority.d.ts.map +1 -0
- package/ietf/priority.js +10 -0
- package/ietf/priority.js.map +1 -0
- package/ietf/properties.d.ts +13 -2
- package/ietf/properties.d.ts.map +1 -1
- package/ietf/properties.js +65 -8
- package/ietf/properties.js.map +1 -1
- package/ietf/publish.d.ts.map +1 -1
- package/ietf/publish.js +13 -3
- package/ietf/publish.js.map +1 -1
- package/ietf/publish_namespace.d.ts +16 -2
- package/ietf/publish_namespace.d.ts.map +1 -1
- package/ietf/publish_namespace.js +25 -6
- package/ietf/publish_namespace.js.map +1 -1
- package/ietf/publisher.d.ts +40 -7
- package/ietf/publisher.d.ts.map +1 -1
- package/ietf/publisher.js +751 -134
- package/ietf/publisher.js.map +1 -1
- package/ietf/request.js +1 -1
- package/ietf/request.js.map +1 -1
- package/ietf/solicit.d.ts +41 -0
- package/ietf/solicit.d.ts.map +1 -0
- package/ietf/solicit.js +47 -0
- package/ietf/solicit.js.map +1 -0
- package/ietf/subscribe.d.ts +41 -2
- package/ietf/subscribe.d.ts.map +1 -1
- package/ietf/subscribe.js +103 -22
- package/ietf/subscribe.js.map +1 -1
- package/ietf/subscribe_namespace.d.ts +21 -7
- package/ietf/subscribe_namespace.d.ts.map +1 -1
- package/ietf/subscribe_namespace.js +31 -14
- package/ietf/subscribe_namespace.js.map +1 -1
- package/ietf/subscriber.d.ts +20 -7
- package/ietf/subscriber.d.ts.map +1 -1
- package/ietf/subscriber.js +512 -142
- package/ietf/subscriber.js.map +1 -1
- package/ietf/version.d.ts +7 -1
- package/ietf/version.d.ts.map +1 -1
- package/ietf/version.js +7 -0
- package/ietf/version.js.map +1 -1
- package/index.d.ts +10 -5
- package/index.d.ts.map +1 -1
- package/index.js +10 -5
- package/index.js.map +1 -1
- package/internal.d.ts +29 -0
- package/internal.d.ts.map +1 -0
- package/internal.js +8 -0
- package/internal.js.map +1 -0
- package/lite/announce.d.ts +62 -15
- package/lite/announce.d.ts.map +1 -1
- package/lite/announce.js +230 -94
- package/lite/announce.js.map +1 -1
- package/lite/connection.d.ts +69 -20
- package/lite/connection.d.ts.map +1 -1
- package/lite/connection.js +128 -81
- package/lite/connection.js.map +1 -1
- package/lite/datagram.d.ts +23 -0
- package/lite/datagram.d.ts.map +1 -0
- package/lite/datagram.js +57 -0
- package/lite/datagram.js.map +1 -0
- package/lite/datagram_stream.d.ts +8 -0
- package/lite/datagram_stream.d.ts.map +1 -0
- package/lite/datagram_stream.js +44 -0
- package/lite/datagram_stream.js.map +1 -0
- package/lite/fetch.d.ts.map +1 -1
- package/lite/fetch.js +2 -2
- package/lite/fetch.js.map +1 -1
- package/lite/index.d.ts +3 -0
- package/lite/index.d.ts.map +1 -1
- package/lite/index.js +3 -0
- package/lite/index.js.map +1 -1
- package/lite/priority.d.ts +67 -0
- package/lite/priority.d.ts.map +1 -0
- package/lite/priority.js +114 -0
- package/lite/priority.js.map +1 -0
- package/lite/probe.d.ts +18 -2
- package/lite/probe.d.ts.map +1 -1
- package/lite/probe.js +24 -22
- package/lite/probe.js.map +1 -1
- package/lite/publisher.d.ts +14 -7
- package/lite/publisher.d.ts.map +1 -1
- package/lite/publisher.js +406 -84
- package/lite/publisher.js.map +1 -1
- package/lite/setup.d.ts +83 -6
- package/lite/setup.d.ts.map +1 -1
- package/lite/setup.js +203 -31
- package/lite/setup.js.map +1 -1
- package/lite/stream.d.ts +12 -3
- package/lite/stream.d.ts.map +1 -1
- package/lite/stream.js +7 -4
- package/lite/stream.js.map +1 -1
- package/lite/subscribe.d.ts +37 -23
- package/lite/subscribe.d.ts.map +1 -1
- package/lite/subscribe.js +70 -55
- package/lite/subscribe.js.map +1 -1
- package/lite/subscriber.d.ts +29 -23
- package/lite/subscriber.d.ts.map +1 -1
- package/lite/subscriber.js +689 -112
- package/lite/subscriber.js.map +1 -1
- package/lite/track.d.ts +25 -13
- package/lite/track.d.ts.map +1 -1
- package/lite/track.js +60 -35
- package/lite/track.js.map +1 -1
- package/lite/version.d.ts +39 -4
- package/lite/version.d.ts.map +1 -1
- package/lite/version.js +139 -17
- package/lite/version.js.map +1 -1
- package/mock.d.ts +53 -3
- package/mock.d.ts.map +1 -1
- package/mock.js +110 -21
- package/mock.js.map +1 -1
- package/{lite/origin.d.ts → origin.d.ts} +25 -1
- package/origin.d.ts.map +1 -0
- package/{lite/origin.js → origin.js} +25 -1
- package/origin.js.map +1 -0
- package/package.json +6 -4
- package/path.d.ts +67 -0
- package/path.d.ts.map +1 -1
- package/path.js +128 -0
- package/path.js.map +1 -1
- package/stream.d.ts +94 -4
- package/stream.d.ts.map +1 -1
- package/stream.js +128 -17
- package/stream.js.map +1 -1
- package/time.d.ts +109 -86
- package/time.d.ts.map +1 -1
- package/time.js +80 -30
- package/time.js.map +1 -1
- package/track.d.ts +298 -47
- package/track.d.ts.map +1 -1
- package/track.js +721 -109
- package/track.js.map +1 -1
- package/util/timeout.d.ts +5 -0
- package/util/timeout.d.ts.map +1 -0
- package/util/timeout.js +18 -0
- package/util/timeout.js.map +1 -0
- package/util/utf8.d.ts +3 -0
- package/util/utf8.d.ts.map +1 -0
- package/util/utf8.js +7 -0
- package/util/utf8.js.map +1 -0
- package/varint.d.ts +14 -3
- package/varint.d.ts.map +1 -1
- package/varint.js +23 -12
- package/varint.js.map +1 -1
- package/zod.d.ts +5 -5
- package/zod.d.ts.map +1 -1
- package/zod.js.map +1 -1
- package/bandwidth.d.ts +0 -11
- package/bandwidth.d.ts.map +0 -1
- package/bandwidth.js +0 -7
- package/bandwidth.js.map +0 -1
- package/connection/cert.test.d.ts +0 -2
- package/connection/cert.test.d.ts.map +0 -1
- package/connection/cert.test.js +0 -26
- package/connection/cert.test.js.map +0 -1
- package/connection/reload.test.d.ts +0 -2
- package/connection/reload.test.d.ts.map +0 -1
- package/connection/reload.test.js +0 -39
- package/connection/reload.test.js.map +0 -1
- package/group.test.d.ts +0 -2
- package/group.test.d.ts.map +0 -1
- package/group.test.js +0 -66
- package/group.test.js.map +0 -1
- package/ietf/aliases.test.d.ts +0 -2
- package/ietf/aliases.test.d.ts.map +0 -1
- package/ietf/aliases.test.js +0 -30
- package/ietf/aliases.test.js.map +0 -1
- package/ietf/ietf.test.d.ts +0 -2
- package/ietf/ietf.test.d.ts.map +0 -1
- package/ietf/ietf.test.js +0 -873
- package/ietf/ietf.test.js.map +0 -1
- package/integration.test.d.ts +0 -2
- package/integration.test.d.ts.map +0 -1
- package/integration.test.js +0 -90
- package/integration.test.js.map +0 -1
- package/lite/origin.d.ts.map +0 -1
- package/lite/origin.js.map +0 -1
- package/lite/subscriber.test.d.ts +0 -2
- package/lite/subscriber.test.d.ts.map +0 -1
- package/lite/subscriber.test.js +0 -30
- package/lite/subscriber.test.js.map +0 -1
- package/lite/track.test.d.ts +0 -2
- package/lite/track.test.d.ts.map +0 -1
- package/lite/track.test.js +0 -53
- package/lite/track.test.js.map +0 -1
- package/path.test.d.ts +0 -2
- package/path.test.d.ts.map +0 -1
- package/path.test.js +0 -169
- package/path.test.js.map +0 -1
- package/stream.test.d.ts +0 -2
- package/stream.test.d.ts.map +0 -1
- package/stream.test.js +0 -210
- package/stream.test.js.map +0 -1
- package/track.test.d.ts +0 -2
- package/track.test.d.ts.map +0 -1
- package/track.test.js +0 -56
- package/track.test.js.map +0 -1
- package/util/error.d.ts +0 -3
- package/util/error.d.ts.map +0 -1
- package/util/error.js +0 -9
- package/util/error.js.map +0 -1
- package/util/index.d.ts +0 -2
- package/util/index.d.ts.map +0 -1
- package/util/index.js +0 -3
- package/util/index.js.map +0 -1
- package/varint.test.d.ts +0 -2
- package/varint.test.d.ts.map +0 -1
- package/varint.test.js +0 -106
- package/varint.test.js.map +0 -1
package/README.md
CHANGED
|
@@ -60,6 +60,7 @@ await quicheLoaded; //This is a promise, connect after it resolves
|
|
|
60
60
|
- **[Publishing](examples/publish.ts)** - Publish data to a broadcast
|
|
61
61
|
- **[Subscribing](examples/subscribe.ts)** - Subscribe to and receive broadcast data
|
|
62
62
|
- **[Discovery](examples/discovery.ts)** - Discover broadcasts announced by the server
|
|
63
|
+
- **[Waiting](examples/wait.ts)** - Wait for one known broadcast to come online, and follow it
|
|
63
64
|
- **[Server side usage](https://github.com/sb2702/webcodecs-examples/tree/main/src/moq-server)** - Publish from browser to a server
|
|
64
65
|
|
|
65
66
|
## License
|
package/announced.d.ts
CHANGED
|
@@ -1,45 +1,135 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Broadcast announcement streams: which broadcast paths are available under a prefix.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { type GetPromise, type Getter, type GetterInit } from "@norskvideo/moq-signals";
|
|
7
|
+
import type * as broadcast from "./broadcast.js";
|
|
8
|
+
import type { Established } from "./connection/established.js";
|
|
2
9
|
import * as Path from "./path.js";
|
|
3
10
|
/**
|
|
4
11
|
* The availability of a broadcast.
|
|
5
12
|
*
|
|
6
13
|
* @public
|
|
7
14
|
*/
|
|
8
|
-
export interface
|
|
15
|
+
export interface Event {
|
|
16
|
+
/** Broadcast path relative to the prefix passed to `announced()`. */
|
|
9
17
|
path: Path.Valid;
|
|
18
|
+
/** True when the broadcast is available, false when it was removed. */
|
|
10
19
|
active: boolean;
|
|
11
20
|
}
|
|
12
|
-
/** Reactive backing state for an {@link Announced}: the pending queue plus a closed flag. */
|
|
13
|
-
export declare class AnnouncedState {
|
|
14
|
-
queue: Signal<AnnouncedEntry[]>;
|
|
15
|
-
closed: Signal<boolean | Error>;
|
|
16
|
-
}
|
|
17
21
|
/**
|
|
18
|
-
*
|
|
22
|
+
* The write side of an announcement stream.
|
|
19
23
|
*
|
|
20
24
|
* @public
|
|
21
25
|
*/
|
|
22
|
-
export declare class
|
|
23
|
-
|
|
24
|
-
state: AnnouncedState;
|
|
26
|
+
export declare class Producer {
|
|
27
|
+
#private;
|
|
25
28
|
/** Path prefix this stream is scoped to. */
|
|
26
29
|
prefix: Path.Valid;
|
|
27
|
-
/** Resolves with the abort error (or undefined) once closed. */
|
|
28
|
-
readonly closed: Promise<Error | undefined>;
|
|
29
30
|
constructor(prefix?: Path.Valid);
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
-
*
|
|
32
|
+
* Settles once the stream closes: `null` on a clean close, or the abort {@link Error}.
|
|
33
|
+
* Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.
|
|
34
|
+
*/
|
|
35
|
+
get closed(): GetPromise<Error | null>;
|
|
36
|
+
/** A read handle for this announcement stream. */
|
|
37
|
+
consume(): Consumer;
|
|
38
|
+
/** Writes an announcement to the queue. */
|
|
39
|
+
append(event: Event): void;
|
|
40
|
+
/** Closes the writer. Idempotent. */
|
|
41
|
+
close(abort?: Error): void;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* The read side of an announcement stream.
|
|
45
|
+
*
|
|
46
|
+
* Created internally: obtain one from {@link Producer.consume} or the connection's
|
|
47
|
+
* `announced(prefix)`.
|
|
48
|
+
*
|
|
49
|
+
* @public
|
|
50
|
+
*/
|
|
51
|
+
export declare class Consumer {
|
|
52
|
+
#private;
|
|
53
|
+
/** Path prefix this stream is scoped to. */
|
|
54
|
+
prefix: Path.Valid;
|
|
55
|
+
private constructor();
|
|
56
|
+
/** Settles once the stream closes; see {@link Producer.closed}. */
|
|
57
|
+
get closed(): GetPromise<Error | null>;
|
|
58
|
+
/** Returns the next announcement. */
|
|
59
|
+
next(): Promise<Event | undefined>;
|
|
60
|
+
/** Closes the reader. Idempotent. */
|
|
61
|
+
close(abort?: Error): void;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* What to watch, for {@link Broadcast}.
|
|
65
|
+
*
|
|
66
|
+
* @public
|
|
67
|
+
*/
|
|
68
|
+
export interface BroadcastProps {
|
|
69
|
+
/**
|
|
70
|
+
* The connection to watch on. Accepts a live {@link Established} session, or a reactive one
|
|
71
|
+
* (a `Connection.Reload`'s `established`), which is how the handle survives reconnects.
|
|
33
72
|
*/
|
|
34
|
-
|
|
73
|
+
connection: GetterInit<Established | undefined>;
|
|
74
|
+
/** The broadcast path to watch. */
|
|
75
|
+
path: Path.Valid;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* A reactive handle to a single broadcast: {@link Broadcast.active} holds a live
|
|
79
|
+
* {@link broadcast.Consumer} while the path is announced and `undefined` while nobody
|
|
80
|
+
* publishes it.
|
|
81
|
+
*
|
|
82
|
+
* Use this instead of {@link Established.consume} whenever the broadcast may not exist yet.
|
|
83
|
+
* Subscribing to a path nobody publishes gets the stream reset, so a consumer that races the
|
|
84
|
+
* publisher stays silent forever unless it retries; this waits for the announcement instead.
|
|
85
|
+
*
|
|
86
|
+
* A same-name republish re-consumes, so the handle attaches to the new instance rather than
|
|
87
|
+
* clinging to the dead one. A relay failover that keeps the same publisher does *not*: the
|
|
88
|
+
* subscription resumes across the new route, so `active` holds the same consumer throughout and
|
|
89
|
+
* never goes offline. Only a change of publisher produces an offline/online transition.
|
|
90
|
+
*
|
|
91
|
+
* Built from a reconnecting `Connection.Reload`, the handle also spans reconnects: the broadcast
|
|
92
|
+
* drops to `undefined` while disconnected and resolves again once the new connection announces it.
|
|
93
|
+
*
|
|
94
|
+
* Falls back to consuming blind (and warns once) on a relay without
|
|
95
|
+
* {@link Established.discovery}, where there is no announcement to wait for. `active` then
|
|
96
|
+
* means *assumed present* rather than known live: nothing reports whether the path exists, so
|
|
97
|
+
* a subscribe to a missing broadcast is how a caller finds out. The handle stays usable either
|
|
98
|
+
* way, and because it is scoped to the path rather than to one publisher, a subscribe made
|
|
99
|
+
* after a publisher finally appears succeeds.
|
|
100
|
+
*
|
|
101
|
+
* If discovery fails on a live session (the announcement stream is reset, or the relay
|
|
102
|
+
* refuses it) the handle goes offline and stays there: nothing reopens the stream on that
|
|
103
|
+
* connection. Build it from a `Connection.Reload` if you need it to recover, since a new
|
|
104
|
+
* connection starts a new stream.
|
|
105
|
+
*
|
|
106
|
+
* Close it to release the announcement stream and the current broadcast.
|
|
107
|
+
*
|
|
108
|
+
* @public
|
|
109
|
+
*/
|
|
110
|
+
export declare class Broadcast {
|
|
111
|
+
#private;
|
|
112
|
+
/** The broadcast path this handle watches. */
|
|
113
|
+
readonly path: Path.Valid;
|
|
35
114
|
/**
|
|
36
|
-
*
|
|
37
|
-
*
|
|
115
|
+
* The live broadcast, or `undefined` while it is offline.
|
|
116
|
+
*
|
|
117
|
+
* Borrowed, not yours to close: this handle owns the consumer and swaps it when the path is
|
|
118
|
+
* republished. `active` keeps pointing at whatever you closed, so once you drop the last
|
|
119
|
+
* reference the shared broadcast is gone and reads fail until the next announcement replaces
|
|
120
|
+
* it. Take a {@link broadcast.Consumer.clone} for a lifetime of your own, or close this whole
|
|
121
|
+
* handle to release everything.
|
|
38
122
|
*/
|
|
39
|
-
|
|
123
|
+
readonly active: Getter<broadcast.Consumer | undefined>;
|
|
40
124
|
/**
|
|
41
|
-
*
|
|
125
|
+
* Watch a path on a connection.
|
|
126
|
+
*
|
|
127
|
+
* Prefer `announcedBroadcast(path)` on the connection itself. Reach for this when the
|
|
128
|
+
* session you want to follow isn't either connection type, e.g. your own
|
|
129
|
+
* `Getter<Established | undefined>`.
|
|
42
130
|
*/
|
|
43
|
-
|
|
131
|
+
constructor({ connection, path }: BroadcastProps);
|
|
132
|
+
/** Closes the handle and the broadcast it currently holds. Idempotent. */
|
|
133
|
+
close(): void;
|
|
44
134
|
}
|
|
45
135
|
//# sourceMappingURL=announced.d.ts.map
|
package/announced.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"announced.d.ts","sourceRoot":"","sources":["../src/announced.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"announced.d.ts","sourceRoot":"","sources":["../src/announced.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAU,KAAK,UAAU,EAAE,KAAK,MAAM,EAAE,KAAK,UAAU,EAAwB,MAAM,cAAc,CAAC;AAC3G,OAAO,KAAK,KAAK,SAAS,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,KAAK;IACrB,qEAAqE;IACrE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC;IACjB,uEAAuE;IACvE,MAAM,EAAE,OAAO,CAAC;CAChB;AAiBD;;;;GAIG;AACH,qBAAa,QAAQ;;IACpB,4CAA4C;IAC5C,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC;IAInB,YAAY,MAAM,aAAe,EAEhC;IAED;;;OAGG;IACH,IAAI,MAAM,IAAI,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,CAErC;IAED,kDAAkD;IAClD,OAAO,IAAI,QAAQ,CAElB;IAED,2CAA2C;IAC3C,MAAM,CAAC,KAAK,EAAE,KAAK,QAKlB;IAED,qCAAqC;IACrC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,QAElB;CACD;AAMD;;;;;;;GAOG;AACH,qBAAa,QAAQ;;IACpB,4CAA4C;IAC5C,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC;IAInB,OAAO,eAGN;IAED,mEAAmE;IACnE,IAAI,MAAM,IAAI,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,CAErC;IAMD,qCAAqC;IAC/B,IAAI,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAWvC;IAED,qCAAqC;IACrC,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,QAElB;CACD;AAMD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC9B;;;OAGG;IACH,UAAU,EAAE,UAAU,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IAEhD,mCAAmC;IACnC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,qBAAa,SAAS;;IACrB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC;IAE1B;;;;;;;;OAQG;IACH,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,SAAS,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;IAKxD;;;;;;OAMG;IACH,YAAY,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,cAAc,EA6E/C;IAED,0EAA0E;IAC1E,KAAK,SAEJ;CACD"}
|
package/announced.js
CHANGED
|
@@ -1,70 +1,243 @@
|
|
|
1
1
|
/* @ts-self-types="./announced.d.ts" */
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Broadcast announcement streams: which broadcast paths are available under a prefix.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
import { Effect, getter, Once, Signal } from "@norskvideo/moq-signals";
|
|
3
8
|
import * as Path from "./path.js";
|
|
4
|
-
/** Reactive backing state
|
|
5
|
-
|
|
9
|
+
/** Reactive backing state shared by announcement producers and consumers. */
|
|
10
|
+
class AnnounceState {
|
|
6
11
|
queue = new Signal([]);
|
|
7
|
-
closed = new
|
|
12
|
+
closed = new Once();
|
|
13
|
+
}
|
|
14
|
+
// Once.set throws on a second settle, and both ends of a stream can close independently.
|
|
15
|
+
function closeState(state, abort) {
|
|
16
|
+
if (state.closed.peek() !== undefined)
|
|
17
|
+
return;
|
|
18
|
+
state.closed.set(abort ?? null);
|
|
19
|
+
state.queue.mutate((queue) => {
|
|
20
|
+
queue.length = 0;
|
|
21
|
+
});
|
|
8
22
|
}
|
|
9
23
|
/**
|
|
10
|
-
*
|
|
24
|
+
* The write side of an announcement stream.
|
|
11
25
|
*
|
|
12
26
|
* @public
|
|
13
27
|
*/
|
|
14
|
-
export class
|
|
15
|
-
/** Reactive backing state. */
|
|
16
|
-
state = new AnnouncedState();
|
|
28
|
+
export class Producer {
|
|
17
29
|
/** Path prefix this stream is scoped to. */
|
|
18
30
|
prefix;
|
|
19
|
-
|
|
20
|
-
closed;
|
|
31
|
+
#state = new AnnounceState();
|
|
21
32
|
constructor(prefix = Path.empty()) {
|
|
22
33
|
this.prefix = prefix;
|
|
23
|
-
this.closed = new Promise((resolve) => {
|
|
24
|
-
const dispose = this.state.closed.subscribe((closed) => {
|
|
25
|
-
if (!closed)
|
|
26
|
-
return;
|
|
27
|
-
resolve(closed instanceof Error ? closed : undefined);
|
|
28
|
-
dispose();
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
34
|
}
|
|
32
35
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
36
|
+
* Settles once the stream closes: `null` on a clean close, or the abort {@link Error}.
|
|
37
|
+
* Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.
|
|
35
38
|
*/
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
get closed() {
|
|
40
|
+
return this.#state.closed;
|
|
41
|
+
}
|
|
42
|
+
/** A read handle for this announcement stream. */
|
|
43
|
+
consume() {
|
|
44
|
+
return makeConsumer(this.prefix, this.#state);
|
|
45
|
+
}
|
|
46
|
+
/** Writes an announcement to the queue. */
|
|
47
|
+
append(event) {
|
|
48
|
+
if (this.#state.closed.peek() !== undefined)
|
|
49
|
+
throw new Error("announcements are closed");
|
|
50
|
+
this.#state.queue.mutate((queue) => {
|
|
51
|
+
queue.push(event);
|
|
41
52
|
});
|
|
42
53
|
}
|
|
43
|
-
/**
|
|
44
|
-
* Closes the writer.
|
|
45
|
-
* @param abort - If provided, throw this exception instead of returning undefined.
|
|
46
|
-
*/
|
|
54
|
+
/** Closes the writer. Idempotent. */
|
|
47
55
|
close(abort) {
|
|
48
|
-
this
|
|
49
|
-
this.state.queue.mutate((queue) => {
|
|
50
|
-
queue.length = 0;
|
|
51
|
-
});
|
|
56
|
+
closeState(this.#state, abort);
|
|
52
57
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
}
|
|
59
|
+
// Constructs a Consumer from within this module without exposing a public constructor
|
|
60
|
+
// that would leak the unexported AnnounceState. Assigned in the class's static block.
|
|
61
|
+
let makeConsumer;
|
|
62
|
+
/**
|
|
63
|
+
* The read side of an announcement stream.
|
|
64
|
+
*
|
|
65
|
+
* Created internally: obtain one from {@link Producer.consume} or the connection's
|
|
66
|
+
* `announced(prefix)`.
|
|
67
|
+
*
|
|
68
|
+
* @public
|
|
69
|
+
*/
|
|
70
|
+
export class Consumer {
|
|
71
|
+
/** Path prefix this stream is scoped to. */
|
|
72
|
+
prefix;
|
|
73
|
+
#state;
|
|
74
|
+
constructor(prefix, state) {
|
|
75
|
+
this.prefix = prefix;
|
|
76
|
+
this.#state = state;
|
|
77
|
+
}
|
|
78
|
+
/** Settles once the stream closes; see {@link Producer.closed}. */
|
|
79
|
+
get closed() {
|
|
80
|
+
return this.#state.closed;
|
|
81
|
+
}
|
|
82
|
+
static {
|
|
83
|
+
makeConsumer = (prefix, state) => new Consumer(prefix, state);
|
|
84
|
+
}
|
|
85
|
+
/** Returns the next announcement. */
|
|
56
86
|
async next() {
|
|
57
87
|
for (;;) {
|
|
58
|
-
const announce = this
|
|
88
|
+
const announce = this.#state.queue.peek().shift();
|
|
59
89
|
if (announce)
|
|
60
90
|
return announce;
|
|
61
|
-
const closed = this
|
|
91
|
+
const closed = this.#state.closed.peek();
|
|
62
92
|
if (closed instanceof Error)
|
|
63
93
|
throw closed;
|
|
64
|
-
if (closed)
|
|
94
|
+
if (closed !== undefined)
|
|
65
95
|
return undefined;
|
|
66
|
-
await Signal.race(this
|
|
96
|
+
await Signal.race(this.#state.queue, this.#state.closed);
|
|
67
97
|
}
|
|
68
98
|
}
|
|
99
|
+
/** Closes the reader. Idempotent. */
|
|
100
|
+
close(abort) {
|
|
101
|
+
closeState(this.#state, abort);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
// Connections already warned about missing broadcast discovery, so the fallback logs at most
|
|
105
|
+
// once per connection instead of once per watched path.
|
|
106
|
+
const warnedNoDiscovery = new WeakSet();
|
|
107
|
+
/**
|
|
108
|
+
* A reactive handle to a single broadcast: {@link Broadcast.active} holds a live
|
|
109
|
+
* {@link broadcast.Consumer} while the path is announced and `undefined` while nobody
|
|
110
|
+
* publishes it.
|
|
111
|
+
*
|
|
112
|
+
* Use this instead of {@link Established.consume} whenever the broadcast may not exist yet.
|
|
113
|
+
* Subscribing to a path nobody publishes gets the stream reset, so a consumer that races the
|
|
114
|
+
* publisher stays silent forever unless it retries; this waits for the announcement instead.
|
|
115
|
+
*
|
|
116
|
+
* A same-name republish re-consumes, so the handle attaches to the new instance rather than
|
|
117
|
+
* clinging to the dead one. A relay failover that keeps the same publisher does *not*: the
|
|
118
|
+
* subscription resumes across the new route, so `active` holds the same consumer throughout and
|
|
119
|
+
* never goes offline. Only a change of publisher produces an offline/online transition.
|
|
120
|
+
*
|
|
121
|
+
* Built from a reconnecting `Connection.Reload`, the handle also spans reconnects: the broadcast
|
|
122
|
+
* drops to `undefined` while disconnected and resolves again once the new connection announces it.
|
|
123
|
+
*
|
|
124
|
+
* Falls back to consuming blind (and warns once) on a relay without
|
|
125
|
+
* {@link Established.discovery}, where there is no announcement to wait for. `active` then
|
|
126
|
+
* means *assumed present* rather than known live: nothing reports whether the path exists, so
|
|
127
|
+
* a subscribe to a missing broadcast is how a caller finds out. The handle stays usable either
|
|
128
|
+
* way, and because it is scoped to the path rather than to one publisher, a subscribe made
|
|
129
|
+
* after a publisher finally appears succeeds.
|
|
130
|
+
*
|
|
131
|
+
* If discovery fails on a live session (the announcement stream is reset, or the relay
|
|
132
|
+
* refuses it) the handle goes offline and stays there: nothing reopens the stream on that
|
|
133
|
+
* connection. Build it from a `Connection.Reload` if you need it to recover, since a new
|
|
134
|
+
* connection starts a new stream.
|
|
135
|
+
*
|
|
136
|
+
* Close it to release the announcement stream and the current broadcast.
|
|
137
|
+
*
|
|
138
|
+
* @public
|
|
139
|
+
*/
|
|
140
|
+
export class Broadcast {
|
|
141
|
+
/** The broadcast path this handle watches. */
|
|
142
|
+
path;
|
|
143
|
+
/**
|
|
144
|
+
* The live broadcast, or `undefined` while it is offline.
|
|
145
|
+
*
|
|
146
|
+
* Borrowed, not yours to close: this handle owns the consumer and swaps it when the path is
|
|
147
|
+
* republished. `active` keeps pointing at whatever you closed, so once you drop the last
|
|
148
|
+
* reference the shared broadcast is gone and reads fail until the next announcement replaces
|
|
149
|
+
* it. Take a {@link broadcast.Consumer.clone} for a lifetime of your own, or close this whole
|
|
150
|
+
* handle to release everything.
|
|
151
|
+
*/
|
|
152
|
+
active;
|
|
153
|
+
#active = new Signal(undefined);
|
|
154
|
+
#signals = new Effect();
|
|
155
|
+
/**
|
|
156
|
+
* Watch a path on a connection.
|
|
157
|
+
*
|
|
158
|
+
* Prefer `announcedBroadcast(path)` on the connection itself. Reach for this when the
|
|
159
|
+
* session you want to follow isn't either connection type, e.g. your own
|
|
160
|
+
* `Getter<Established | undefined>`.
|
|
161
|
+
*/
|
|
162
|
+
constructor({ connection, path }) {
|
|
163
|
+
this.path = path;
|
|
164
|
+
this.active = this.#active;
|
|
165
|
+
const source = getter(connection);
|
|
166
|
+
this.#signals.run((effect) => {
|
|
167
|
+
const conn = effect.get(source);
|
|
168
|
+
if (!conn)
|
|
169
|
+
return;
|
|
170
|
+
// Without discovery no announcement ever arrives, so waiting would hang forever.
|
|
171
|
+
if (!conn.discovery) {
|
|
172
|
+
if (!warnedNoDiscovery.has(conn)) {
|
|
173
|
+
warnedNoDiscovery.add(conn);
|
|
174
|
+
console.warn("relay does not support broadcast discovery; consuming without waiting.");
|
|
175
|
+
}
|
|
176
|
+
const blind = conn.consume(path);
|
|
177
|
+
effect.cleanup(() => blind.close());
|
|
178
|
+
effect.set(this.#active, blind, undefined);
|
|
179
|
+
// The announcement-gated path below goes offline when the stream ends with the
|
|
180
|
+
// session; without discovery there is no stream, so watch the session itself.
|
|
181
|
+
// A consumed broadcast is a path-scoped handle, not a subscription, so its own
|
|
182
|
+
// `closed` says nothing about whether the path exists or the session is alive.
|
|
183
|
+
// Raced against the run's teardown so a closed handle isn't retained until the
|
|
184
|
+
// session ends; the cleanup above has already cleared `active` in that case.
|
|
185
|
+
effect.spawn(async () => {
|
|
186
|
+
await Promise.race([effect.cancel, conn.closed]);
|
|
187
|
+
if (this.#active.peek() === blind)
|
|
188
|
+
this.#active.set(undefined);
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const announced = conn.announced(path);
|
|
193
|
+
effect.cleanup(() => announced.close());
|
|
194
|
+
let current;
|
|
195
|
+
const offline = () => {
|
|
196
|
+
const mine = current;
|
|
197
|
+
current?.close();
|
|
198
|
+
current = undefined;
|
|
199
|
+
// Only clear what this run put there. A spawn task that resumes after its run was
|
|
200
|
+
// torn down would otherwise wipe the consumer a newer run already installed.
|
|
201
|
+
if (this.#active.peek() === mine)
|
|
202
|
+
this.#active.set(undefined);
|
|
203
|
+
};
|
|
204
|
+
effect.cleanup(offline);
|
|
205
|
+
effect.spawn(async () => {
|
|
206
|
+
try {
|
|
207
|
+
for (;;) {
|
|
208
|
+
const event = await Promise.race([effect.cancel, announced.next()]);
|
|
209
|
+
if (!event)
|
|
210
|
+
break;
|
|
211
|
+
// Scoped to `path`, so the exact broadcast arrives with an empty suffix; ignore children.
|
|
212
|
+
if (event.path !== Path.empty())
|
|
213
|
+
continue;
|
|
214
|
+
if (event.active) {
|
|
215
|
+
// A live subscription survives a redundant (re-)announce; only replace a dead one.
|
|
216
|
+
if (current && current.closed.peek() === undefined)
|
|
217
|
+
continue;
|
|
218
|
+
current?.close();
|
|
219
|
+
current = conn.consume(path);
|
|
220
|
+
this.#active.set(current);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
offline();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
catch (err) {
|
|
228
|
+
// Discovery failed: the session died under the stream, or the relay refused
|
|
229
|
+
// to answer. Nothing reopens it on this connection, so say so out loud.
|
|
230
|
+
console.warn("broadcast discovery failed", err);
|
|
231
|
+
}
|
|
232
|
+
// The stream ended, or this run was torn down (its cleanup already ran). Either
|
|
233
|
+
// way there is nothing left announcing the path, so don't hold a dead broadcast.
|
|
234
|
+
offline();
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/** Closes the handle and the broadcast it currently holds. Idempotent. */
|
|
239
|
+
close() {
|
|
240
|
+
this.#signals.close();
|
|
241
|
+
}
|
|
69
242
|
}
|
|
70
243
|
//# sourceMappingURL=announced.js.map
|
package/announced.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"announced.js","sourceRoot":"","sources":["../src/announced.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAYlC,6FAA6F;AAC7F,MAAM,OAAO,cAAc;IAC1B,KAAK,GAAG,IAAI,MAAM,CAAmB,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,IAAI,MAAM,CAAkB,KAAK,CAAC,CAAC;CAC5C;AAED;;;;GAIG;AACH,MAAM,OAAO,SAAS;IACrB,8BAA8B;IAC9B,KAAK,GAAG,IAAI,cAAc,EAAE,CAAC;IAE7B,4CAA4C;IAC5C,MAAM,CAAa;IAEnB,gEAAgE;IACvD,MAAM,CAA6B;IAE5C,YAAY,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE;QAChC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YACrC,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,EAAE,EAAE;gBACtD,IAAI,CAAC,MAAM;oBAAE,OAAO;gBACpB,OAAO,CAAC,MAAM,YAAY,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;gBACtD,OAAO,EAAE,CAAC;YACX,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,YAA4B;QAClC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;QACrE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACjC,KAAK,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAC1B,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAa;QAClB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE;YACjC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;QAClB,CAAC,CAAC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI;QACT,SAAS,CAAC;YACT,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,CAAC;YACjD,IAAI,QAAQ;gBAAE,OAAO,QAAQ,CAAC;YAE9B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,MAAM,YAAY,KAAK;gBAAE,MAAM,MAAM,CAAC;YAC1C,IAAI,MAAM;gBAAE,OAAO,SAAS,CAAC;YAE7B,MAAM,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACxD,CAAC;IACF,CAAC;CACD","sourcesContent":["import { Signal } from \"@moq/signals\";\nimport * as Path from \"./path.js\";\n\n/**\n * The availability of a broadcast.\n *\n * @public\n */\nexport interface AnnouncedEntry {\n\tpath: Path.Valid;\n\tactive: boolean;\n}\n\n/** Reactive backing state for an {@link Announced}: the pending queue plus a closed flag. */\nexport class AnnouncedState {\n\tqueue = new Signal<AnnouncedEntry[]>([]);\n\tclosed = new Signal<boolean | Error>(false);\n}\n\n/**\n * Handles writing announcements to the announcement queue.\n *\n * @public\n */\nexport class Announced {\n\t/** Reactive backing state. */\n\tstate = new AnnouncedState();\n\n\t/** Path prefix this stream is scoped to. */\n\tprefix: Path.Valid;\n\n\t/** Resolves with the abort error (or undefined) once closed. */\n\treadonly closed: Promise<Error | undefined>;\n\n\tconstructor(prefix = Path.empty()) {\n\t\tthis.prefix = prefix;\n\t\tthis.closed = new Promise((resolve) => {\n\t\t\tconst dispose = this.state.closed.subscribe((closed) => {\n\t\t\t\tif (!closed) return;\n\t\t\t\tresolve(closed instanceof Error ? closed : undefined);\n\t\t\t\tdispose();\n\t\t\t});\n\t\t});\n\t}\n\n\t/**\n\t * Writes an announcement to the queue.\n\t * @param announcement - The announcement to write\n\t */\n\tappend(announcement: AnnouncedEntry) {\n\t\tif (this.state.closed.peek()) throw new Error(\"announced is closed\");\n\t\tthis.state.queue.mutate((queue) => {\n\t\t\tqueue.push(announcement);\n\t\t});\n\t}\n\n\t/**\n\t * Closes the writer.\n\t * @param abort - If provided, throw this exception instead of returning undefined.\n\t */\n\tclose(abort?: Error) {\n\t\tthis.state.closed.set(abort ?? true);\n\t\tthis.state.queue.mutate((queue) => {\n\t\t\tqueue.length = 0;\n\t\t});\n\t}\n\n\t/**\n\t * Returns the next announcement.\n\t */\n\tasync next(): Promise<AnnouncedEntry | undefined> {\n\t\tfor (;;) {\n\t\t\tconst announce = this.state.queue.peek().shift();\n\t\t\tif (announce) return announce;\n\n\t\t\tconst closed = this.state.closed.peek();\n\t\t\tif (closed instanceof Error) throw closed;\n\t\t\tif (closed) return undefined;\n\n\t\t\tawait Signal.race(this.state.queue, this.state.closed);\n\t\t}\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"announced.js","sourceRoot":"","sources":["../src/announced.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Broadcast announcement streams: which broadcast paths are available under a prefix.\n *\n * @module\n */\nimport { Effect, type GetPromise, type Getter, type GetterInit, getter, Once, Signal } from \"@moq/signals\";\nimport type * as broadcast from \"./broadcast.js\";\nimport type { Established } from \"./connection/established.js\";\nimport * as Path from \"./path.js\";\n\n/**\n * The availability of a broadcast.\n *\n * @public\n */\nexport interface Event {\n\t/** Broadcast path relative to the prefix passed to `announced()`. */\n\tpath: Path.Valid;\n\t/** True when the broadcast is available, false when it was removed. */\n\tactive: boolean;\n}\n\n/** Reactive backing state shared by announcement producers and consumers. */\nclass AnnounceState {\n\tqueue = new Signal<Event[]>([]);\n\tclosed = new Once<Error | null>();\n}\n\n// Once.set throws on a second settle, and both ends of a stream can close independently.\nfunction closeState(state: AnnounceState, abort?: Error) {\n\tif (state.closed.peek() !== undefined) return;\n\tstate.closed.set(abort ?? null);\n\tstate.queue.mutate((queue) => {\n\t\tqueue.length = 0;\n\t});\n}\n\n/**\n * The write side of an announcement stream.\n *\n * @public\n */\nexport class Producer {\n\t/** Path prefix this stream is scoped to. */\n\tprefix: Path.Valid;\n\n\t#state = new AnnounceState();\n\n\tconstructor(prefix = Path.empty()) {\n\t\tthis.prefix = prefix;\n\t}\n\n\t/**\n\t * Settles once the stream closes: `null` on a clean close, or the abort {@link Error}.\n\t * Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.\n\t */\n\tget closed(): GetPromise<Error | null> {\n\t\treturn this.#state.closed;\n\t}\n\n\t/** A read handle for this announcement stream. */\n\tconsume(): Consumer {\n\t\treturn makeConsumer(this.prefix, this.#state);\n\t}\n\n\t/** Writes an announcement to the queue. */\n\tappend(event: Event) {\n\t\tif (this.#state.closed.peek() !== undefined) throw new Error(\"announcements are closed\");\n\t\tthis.#state.queue.mutate((queue) => {\n\t\t\tqueue.push(event);\n\t\t});\n\t}\n\n\t/** Closes the writer. Idempotent. */\n\tclose(abort?: Error) {\n\t\tcloseState(this.#state, abort);\n\t}\n}\n\n// Constructs a Consumer from within this module without exposing a public constructor\n// that would leak the unexported AnnounceState. Assigned in the class's static block.\nlet makeConsumer: (prefix: Path.Valid, state: AnnounceState) => Consumer;\n\n/**\n * The read side of an announcement stream.\n *\n * Created internally: obtain one from {@link Producer.consume} or the connection's\n * `announced(prefix)`.\n *\n * @public\n */\nexport class Consumer {\n\t/** Path prefix this stream is scoped to. */\n\tprefix: Path.Valid;\n\n\t#state: AnnounceState;\n\n\tprivate constructor(prefix: Path.Valid, state: AnnounceState) {\n\t\tthis.prefix = prefix;\n\t\tthis.#state = state;\n\t}\n\n\t/** Settles once the stream closes; see {@link Producer.closed}. */\n\tget closed(): GetPromise<Error | null> {\n\t\treturn this.#state.closed;\n\t}\n\n\tstatic {\n\t\tmakeConsumer = (prefix, state) => new Consumer(prefix, state);\n\t}\n\n\t/** Returns the next announcement. */\n\tasync next(): Promise<Event | undefined> {\n\t\tfor (;;) {\n\t\t\tconst announce = this.#state.queue.peek().shift();\n\t\t\tif (announce) return announce;\n\n\t\t\tconst closed = this.#state.closed.peek();\n\t\t\tif (closed instanceof Error) throw closed;\n\t\t\tif (closed !== undefined) return undefined;\n\n\t\t\tawait Signal.race(this.#state.queue, this.#state.closed);\n\t\t}\n\t}\n\n\t/** Closes the reader. Idempotent. */\n\tclose(abort?: Error) {\n\t\tcloseState(this.#state, abort);\n\t}\n}\n\n// Connections already warned about missing broadcast discovery, so the fallback logs at most\n// once per connection instead of once per watched path.\nconst warnedNoDiscovery = new WeakSet<Established>();\n\n/**\n * What to watch, for {@link Broadcast}.\n *\n * @public\n */\nexport interface BroadcastProps {\n\t/**\n\t * The connection to watch on. Accepts a live {@link Established} session, or a reactive one\n\t * (a `Connection.Reload`'s `established`), which is how the handle survives reconnects.\n\t */\n\tconnection: GetterInit<Established | undefined>;\n\n\t/** The broadcast path to watch. */\n\tpath: Path.Valid;\n}\n\n/**\n * A reactive handle to a single broadcast: {@link Broadcast.active} holds a live\n * {@link broadcast.Consumer} while the path is announced and `undefined` while nobody\n * publishes it.\n *\n * Use this instead of {@link Established.consume} whenever the broadcast may not exist yet.\n * Subscribing to a path nobody publishes gets the stream reset, so a consumer that races the\n * publisher stays silent forever unless it retries; this waits for the announcement instead.\n *\n * A same-name republish re-consumes, so the handle attaches to the new instance rather than\n * clinging to the dead one. A relay failover that keeps the same publisher does *not*: the\n * subscription resumes across the new route, so `active` holds the same consumer throughout and\n * never goes offline. Only a change of publisher produces an offline/online transition.\n *\n * Built from a reconnecting `Connection.Reload`, the handle also spans reconnects: the broadcast\n * drops to `undefined` while disconnected and resolves again once the new connection announces it.\n *\n * Falls back to consuming blind (and warns once) on a relay without\n * {@link Established.discovery}, where there is no announcement to wait for. `active` then\n * means *assumed present* rather than known live: nothing reports whether the path exists, so\n * a subscribe to a missing broadcast is how a caller finds out. The handle stays usable either\n * way, and because it is scoped to the path rather than to one publisher, a subscribe made\n * after a publisher finally appears succeeds.\n *\n * If discovery fails on a live session (the announcement stream is reset, or the relay\n * refuses it) the handle goes offline and stays there: nothing reopens the stream on that\n * connection. Build it from a `Connection.Reload` if you need it to recover, since a new\n * connection starts a new stream.\n *\n * Close it to release the announcement stream and the current broadcast.\n *\n * @public\n */\nexport class Broadcast {\n\t/** The broadcast path this handle watches. */\n\treadonly path: Path.Valid;\n\n\t/**\n\t * The live broadcast, or `undefined` while it is offline.\n\t *\n\t * Borrowed, not yours to close: this handle owns the consumer and swaps it when the path is\n\t * republished. `active` keeps pointing at whatever you closed, so once you drop the last\n\t * reference the shared broadcast is gone and reads fail until the next announcement replaces\n\t * it. Take a {@link broadcast.Consumer.clone} for a lifetime of your own, or close this whole\n\t * handle to release everything.\n\t */\n\treadonly active: Getter<broadcast.Consumer | undefined>;\n\n\t#active = new Signal<broadcast.Consumer | undefined>(undefined);\n\t#signals = new Effect();\n\n\t/**\n\t * Watch a path on a connection.\n\t *\n\t * Prefer `announcedBroadcast(path)` on the connection itself. Reach for this when the\n\t * session you want to follow isn't either connection type, e.g. your own\n\t * `Getter<Established | undefined>`.\n\t */\n\tconstructor({ connection, path }: BroadcastProps) {\n\t\tthis.path = path;\n\t\tthis.active = this.#active;\n\n\t\tconst source = getter(connection);\n\t\tthis.#signals.run((effect) => {\n\t\t\tconst conn = effect.get(source);\n\t\t\tif (!conn) return;\n\n\t\t\t// Without discovery no announcement ever arrives, so waiting would hang forever.\n\t\t\tif (!conn.discovery) {\n\t\t\t\tif (!warnedNoDiscovery.has(conn)) {\n\t\t\t\t\twarnedNoDiscovery.add(conn);\n\t\t\t\t\tconsole.warn(\"relay does not support broadcast discovery; consuming without waiting.\");\n\t\t\t\t}\n\n\t\t\t\tconst blind = conn.consume(path);\n\t\t\t\teffect.cleanup(() => blind.close());\n\t\t\t\teffect.set(this.#active, blind, undefined);\n\n\t\t\t\t// The announcement-gated path below goes offline when the stream ends with the\n\t\t\t\t// session; without discovery there is no stream, so watch the session itself.\n\t\t\t\t// A consumed broadcast is a path-scoped handle, not a subscription, so its own\n\t\t\t\t// `closed` says nothing about whether the path exists or the session is alive.\n\t\t\t\t// Raced against the run's teardown so a closed handle isn't retained until the\n\t\t\t\t// session ends; the cleanup above has already cleared `active` in that case.\n\t\t\t\teffect.spawn(async () => {\n\t\t\t\t\tawait Promise.race([effect.cancel, conn.closed]);\n\t\t\t\t\tif (this.#active.peek() === blind) this.#active.set(undefined);\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst announced = conn.announced(path);\n\t\t\teffect.cleanup(() => announced.close());\n\n\t\t\tlet current: broadcast.Consumer | undefined;\n\t\t\tconst offline = () => {\n\t\t\t\tconst mine = current;\n\t\t\t\tcurrent?.close();\n\t\t\t\tcurrent = undefined;\n\t\t\t\t// Only clear what this run put there. A spawn task that resumes after its run was\n\t\t\t\t// torn down would otherwise wipe the consumer a newer run already installed.\n\t\t\t\tif (this.#active.peek() === mine) this.#active.set(undefined);\n\t\t\t};\n\t\t\teffect.cleanup(offline);\n\n\t\t\teffect.spawn(async () => {\n\t\t\t\ttry {\n\t\t\t\t\tfor (;;) {\n\t\t\t\t\t\tconst event = await Promise.race([effect.cancel, announced.next()]);\n\t\t\t\t\t\tif (!event) break;\n\n\t\t\t\t\t\t// Scoped to `path`, so the exact broadcast arrives with an empty suffix; ignore children.\n\t\t\t\t\t\tif (event.path !== Path.empty()) continue;\n\n\t\t\t\t\t\tif (event.active) {\n\t\t\t\t\t\t\t// A live subscription survives a redundant (re-)announce; only replace a dead one.\n\t\t\t\t\t\t\tif (current && current.closed.peek() === undefined) continue;\n\t\t\t\t\t\t\tcurrent?.close();\n\t\t\t\t\t\t\tcurrent = conn.consume(path);\n\t\t\t\t\t\t\tthis.#active.set(current);\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\toffline();\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} catch (err) {\n\t\t\t\t\t// Discovery failed: the session died under the stream, or the relay refused\n\t\t\t\t\t// to answer. Nothing reopens it on this connection, so say so out loud.\n\t\t\t\t\tconsole.warn(\"broadcast discovery failed\", err);\n\t\t\t\t}\n\n\t\t\t\t// The stream ended, or this run was torn down (its cleanup already ran). Either\n\t\t\t\t// way there is nothing left announcing the path, so don't hold a dead broadcast.\n\t\t\t\toffline();\n\t\t\t});\n\t\t});\n\t}\n\n\t/** Closes the handle and the broadcast it currently holds. Idempotent. */\n\tclose() {\n\t\tthis.#signals.close();\n\t}\n}\n"]}
|
package/broadcast.d.ts
CHANGED
|
@@ -1,38 +1,93 @@
|
|
|
1
|
-
import { Signal } from "@norskvideo/moq-signals";
|
|
2
|
-
import { Track } from "./track";
|
|
3
|
-
/** A track a subscriber asked for, along with its requested delivery priority. */
|
|
4
|
-
export interface TrackRequest {
|
|
5
|
-
track: Track;
|
|
6
|
-
priority: number;
|
|
7
|
-
}
|
|
8
|
-
/** Reactive backing state for a {@link Broadcast}: requested tracks plus a closed flag. */
|
|
9
|
-
export declare class BroadcastState {
|
|
10
|
-
requested: Signal<TrackRequest[]>;
|
|
11
|
-
closed: Signal<boolean | Error>;
|
|
12
|
-
}
|
|
13
1
|
/**
|
|
14
|
-
*
|
|
2
|
+
* Broadcast role handles: a named collection of tracks produced by a publisher.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { type GetPromise } from "@norskvideo/moq-signals";
|
|
7
|
+
import type { Consumer as GroupConsumer } from "./group";
|
|
8
|
+
import * as track from "./track";
|
|
9
|
+
/**
|
|
10
|
+
* The write side of a broadcast.
|
|
15
11
|
*
|
|
16
12
|
* @public
|
|
17
13
|
*/
|
|
18
|
-
export declare class Broadcast {
|
|
19
|
-
|
|
20
|
-
state: BroadcastState;
|
|
21
|
-
/** Resolves with the abort error (or undefined) once closed. */
|
|
22
|
-
readonly closed: Promise<Error | undefined>;
|
|
23
|
-
constructor();
|
|
14
|
+
export declare class Producer implements track.Broadcast {
|
|
15
|
+
#private;
|
|
24
16
|
/**
|
|
25
|
-
*
|
|
17
|
+
* Settles once the broadcast closes: `null` on a clean close, or the abort {@link Error}.
|
|
18
|
+
* Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.
|
|
26
19
|
*/
|
|
27
|
-
|
|
20
|
+
get closed(): GetPromise<Error | null>;
|
|
21
|
+
/** A read handle for this broadcast. */
|
|
22
|
+
consume(): Consumer;
|
|
23
|
+
/** Return the next track requested by a peer. */
|
|
24
|
+
requested(): Promise<track.Request | undefined>;
|
|
25
|
+
/** Insert a track that is served directly, without an on-demand request round-trip. */
|
|
26
|
+
insertTrack(track: track.Producer): void;
|
|
27
|
+
/** Create a track, insert it into the broadcast, and return its producer. */
|
|
28
|
+
createTrack(name: string, info?: Partial<track.Info>): track.Producer;
|
|
29
|
+
/** Remove a statically inserted track by name. */
|
|
30
|
+
removeTrack(name: string): void;
|
|
31
|
+
/** Open a live subscription to a track. Used by the publishing wire layer. */
|
|
32
|
+
subscribe(name: string, options?: track.Subscription): track.Subscriber;
|
|
33
|
+
/** Resolve a track's immutable info. Used by the publishing wire layer. */
|
|
34
|
+
resolveTrackInfo(name: string): Promise<track.Info>;
|
|
35
|
+
/** Fetch a single group from the local retained window. Used by track handles. */
|
|
36
|
+
fetchGroup(name: string, sequence: number, options?: track.FetchGroupOptions): Promise<GroupConsumer>;
|
|
37
|
+
/** A lazy read handle for a track on this broadcast. */
|
|
38
|
+
track(name: string): track.Consumer;
|
|
39
|
+
/** Close the broadcast, optionally with an error to abort waiters. Idempotent. */
|
|
40
|
+
close(abort?: Error): void;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* The read side of a broadcast.
|
|
44
|
+
*
|
|
45
|
+
* Created internally: obtain one from {@link Producer.consume} or the connection's
|
|
46
|
+
* `consume(path)`. The wire layers subclass it to resolve tracks over the network.
|
|
47
|
+
*
|
|
48
|
+
* @public
|
|
49
|
+
*/
|
|
50
|
+
export declare class Consumer implements track.Broadcast {
|
|
51
|
+
#private;
|
|
52
|
+
protected constructor(state?: never);
|
|
28
53
|
/**
|
|
29
|
-
*
|
|
54
|
+
* Settles once the broadcast closes: `null` on a clean close, or the abort {@link Error}.
|
|
55
|
+
* Peek it synchronously (`undefined` while open), observe it reactively, or `await` it.
|
|
56
|
+
*
|
|
57
|
+
* Shared by every {@link clone}: it settles once the last handle closes. The subscribing
|
|
58
|
+
* wire layer peeks it to evict a closed entry from its per-path consume cache.
|
|
30
59
|
*/
|
|
31
|
-
|
|
60
|
+
get closed(): GetPromise<Error | null>;
|
|
32
61
|
/**
|
|
33
|
-
*
|
|
62
|
+
* Return another handle to the same broadcast, reference-counted with this one.
|
|
34
63
|
*
|
|
35
|
-
*
|
|
64
|
+
* Both handles read the same tracks and share one {@link closed} state; the broadcast
|
|
65
|
+
* closes only once *every* handle has {@link close}d. Used by the connection's per-path
|
|
66
|
+
* consume cache to share one subscription across callers. Subclasses that resolve info over
|
|
67
|
+
* the wire override this to preserve their type (see the wire layer's consumed broadcast).
|
|
68
|
+
*/
|
|
69
|
+
clone(): Consumer;
|
|
70
|
+
protected shareState(): never;
|
|
71
|
+
/** Get a lazy handle for a track on this broadcast. Repeat subscriptions dedupe onto one upstream subscription. */
|
|
72
|
+
track(name: string): track.Consumer;
|
|
73
|
+
/** Open a live subscription to a track. Used by the subscribing wire layer. Repeat subscriptions to the same track share one upstream subscription. */
|
|
74
|
+
subscribe(name: string, options?: track.Subscription): track.Subscriber;
|
|
75
|
+
/** Return the next track requested by the local consumer. Used by the subscribing wire layer. */
|
|
76
|
+
requested(): Promise<track.Request | undefined>;
|
|
77
|
+
/**
|
|
78
|
+
* Resolve a track's immutable info. Used by track handles. This base resolves it from
|
|
79
|
+
* the local producers; the consuming wire layer overrides it to fetch over the wire.
|
|
80
|
+
*/
|
|
81
|
+
resolveTrackInfo(name: string): Promise<track.Info>;
|
|
82
|
+
/**
|
|
83
|
+
* Fetch a single group by sequence. Used by track handles. This base serves from the
|
|
84
|
+
* local retained window; the consuming wire layer overrides it to fetch over the wire
|
|
85
|
+
* (or to reject when the transport has no FETCH).
|
|
86
|
+
*/
|
|
87
|
+
fetchGroup(name: string, sequence: number, options?: track.FetchGroupOptions): Promise<GroupConsumer>;
|
|
88
|
+
/**
|
|
89
|
+
* Release this handle. The broadcast is closed (optionally with an error to abort waiters)
|
|
90
|
+
* once this was the last live handle; while other {@link clone}s remain open it stays live.
|
|
36
91
|
*/
|
|
37
92
|
close(abort?: Error): void;
|
|
38
93
|
}
|
package/broadcast.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"broadcast.d.ts","sourceRoot":"","sources":["../src/broadcast.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,KAAK,UAAU,EAAgB,MAAM,cAAc,CAAC;AAC7D,OAAO,KAAK,EAAE,QAAQ,IAAI,aAAa,EAAE,MAAM,YAAY,CAAC;AAE5D,OAAO,KAAK,KAAK,MAAM,YAAY,CAAC;AAiIpC;;;;GAIG;AACH,qBAAa,QAAS,YAAW,KAAK,CAAC,SAAS;;IAG/C;;;OAGG;IACH,IAAI,MAAM,IAAI,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,CAErC;IAED,wCAAwC;IACxC,OAAO,IAAI,QAAQ,CAElB;IAED,iDAAiD;IAC3C,SAAS,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,CAWpD;IAED,uFAAuF;IACvF,WAAW,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,GAAG,IAAI,CAiBvC;IAED,6EAA6E;IAC7E,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAM,GAAG,KAAK,CAAC,QAAQ,CAIxE;IAED,kDAAkD;IAClD,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAE9B;IAED,8EAA8E;IAC9E,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,UAAU,CAEtE;IAED,2EAA2E;IAC3E,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAElD;IAED,kFAAkF;IAClF,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAEpG;IAED,wDAAwD;IACxD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC,QAAQ,CAElC;IAED,kFAAkF;IAClF,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,QAElB;CACD;AAMD;;;;;;;GAOG;AACH,qBAAa,QAAS,YAAW,KAAK,CAAC,SAAS;;IAM/C,SAAS,aAAa,KAAK,CAAC,EAAE,KAAK,EAAE;IAUrC;;;;;;OAMG;IACH,IAAI,MAAM,IAAI,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,CAErC;IAED;;;;;;;OAOG;IACH,KAAK,IAAI,QAAQ,CAEhB;IAID,SAAS,CAAC,UAAU,IAAI,KAAK,CAE5B;IAED,mHAAmH;IACnH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC,QAAQ,CAElC;IAED,uJAAuJ;IACvJ,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,UAAU,CAEtE;IAED,iGAAiG;IAC3F,SAAS,IAAI,OAAO,CAAC,KAAK,CAAC,OAAO,GAAG,SAAS,CAAC,CAWpD;IAED;;;OAGG;IACH,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAElD;IAED;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,CAAC,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,CAEpG;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,EAAE,KAAK,QAKlB;CACD"}
|