@antcdn/live-signaling 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.d.ts +25 -0
- package/dist/client.js +30 -0
- package/dist/protocol.d.ts +1 -1
- package/package.json +1 -1
package/dist/client.d.ts
CHANGED
|
@@ -38,6 +38,17 @@ export interface LiveRoomEvents {
|
|
|
38
38
|
connected: boolean;
|
|
39
39
|
reconnecting: boolean;
|
|
40
40
|
};
|
|
41
|
+
/**
|
|
42
|
+
* A cued question arrived and is being held until the video reports its cue.
|
|
43
|
+
*
|
|
44
|
+
* Emitted so that waiting is visible. A question held forever because no cue ever
|
|
45
|
+
* came looks exactly like a question that was never asked, and that is the single
|
|
46
|
+
* hardest state to diagnose from outside.
|
|
47
|
+
*/
|
|
48
|
+
pending: {
|
|
49
|
+
question: LiveQuestion;
|
|
50
|
+
cueTimeoutMs: number;
|
|
51
|
+
};
|
|
41
52
|
}
|
|
42
53
|
type Listener<K extends keyof LiveRoomEvents> = (payload: LiveRoomEvents[K]) => void;
|
|
43
54
|
export interface LiveRoomOptions {
|
|
@@ -54,6 +65,18 @@ export interface LiveRoomOptions {
|
|
|
54
65
|
* reintroduces exactly the early-reveal problem cues exist to prevent.
|
|
55
66
|
*/
|
|
56
67
|
revealImmediately?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* How long to wait for a question's in-band cue before revealing it anyway, in
|
|
70
|
+
* milliseconds. Zero waits forever.
|
|
71
|
+
*
|
|
72
|
+
* The cue is what makes a question appear at the moment it was asked, so waiting is
|
|
73
|
+
* the whole point. But some players never surface in-band metadata at all, and there
|
|
74
|
+
* the choice is between a question that arrives slightly early and one that never
|
|
75
|
+
* arrives. Late and visible beats perfectly timed and invisible.
|
|
76
|
+
*
|
|
77
|
+
* Defaults to 30 seconds, which is beyond any plausible live delay.
|
|
78
|
+
*/
|
|
79
|
+
cueTimeoutMs?: number;
|
|
57
80
|
/** Injectable for tests and non-browser runtimes. */
|
|
58
81
|
webSocketFactory?: (url: string) => WebSocket;
|
|
59
82
|
}
|
|
@@ -68,6 +91,8 @@ export declare class LiveRoom {
|
|
|
68
91
|
private earlyCues;
|
|
69
92
|
/** Revealed already, so a duplicate cue cannot fire the event twice. */
|
|
70
93
|
private revealed;
|
|
94
|
+
/** Fallback reveals for held questions, cancelled when the real cue arrives. */
|
|
95
|
+
private cueTimers;
|
|
71
96
|
private closedByCaller;
|
|
72
97
|
private reconnectAttempts;
|
|
73
98
|
private reconnectTimer;
|
package/dist/client.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { PROTOCOL_VERSION, } from './protocol.js';
|
|
2
2
|
import { resolveSessionId } from './session.js';
|
|
3
3
|
const RECONNECT_BASE_MS = 500;
|
|
4
|
+
/** See LiveRoomOptions.cueTimeoutMs. */
|
|
5
|
+
const DEFAULT_CUE_TIMEOUT_MS = 30_000;
|
|
4
6
|
const RECONNECT_MAX_MS = 15000;
|
|
5
7
|
export class LiveRoom {
|
|
6
8
|
options;
|
|
@@ -13,6 +15,8 @@ export class LiveRoom {
|
|
|
13
15
|
earlyCues = new Set();
|
|
14
16
|
/** Revealed already, so a duplicate cue cannot fire the event twice. */
|
|
15
17
|
revealed = new Set();
|
|
18
|
+
/** Fallback reveals for held questions, cancelled when the real cue arrives. */
|
|
19
|
+
cueTimers = new Map();
|
|
16
20
|
closedByCaller = false;
|
|
17
21
|
reconnectAttempts = 0;
|
|
18
22
|
reconnectTimer = null;
|
|
@@ -76,6 +80,9 @@ export class LiveRoom {
|
|
|
76
80
|
}
|
|
77
81
|
close() {
|
|
78
82
|
this.closedByCaller = true;
|
|
83
|
+
for (const timer of this.cueTimers.values())
|
|
84
|
+
clearTimeout(timer);
|
|
85
|
+
this.cueTimers.clear();
|
|
79
86
|
if (this.reconnectTimer)
|
|
80
87
|
clearTimeout(this.reconnectTimer);
|
|
81
88
|
this.reconnectTimer = null;
|
|
@@ -205,9 +212,32 @@ export class LiveRoom {
|
|
|
205
212
|
return;
|
|
206
213
|
}
|
|
207
214
|
this.pending.set(question.id, question);
|
|
215
|
+
const timeout = this.options.cueTimeoutMs ?? DEFAULT_CUE_TIMEOUT_MS;
|
|
216
|
+
this.emit('pending', { question, cueTimeoutMs: timeout });
|
|
217
|
+
if (timeout > 0) {
|
|
218
|
+
const timer = setTimeout(() => {
|
|
219
|
+
this.cueTimers.delete(question.id);
|
|
220
|
+
// Still held, so no cue ever arrived for it.
|
|
221
|
+
if (!this.pending.has(question.id))
|
|
222
|
+
return;
|
|
223
|
+
this.pending.delete(question.id);
|
|
224
|
+
this.emit('error', {
|
|
225
|
+
code: 'cue_timeout',
|
|
226
|
+
message: `No in-band cue arrived for ${question.id} within ${timeout}ms, so it is ` +
|
|
227
|
+
'being shown now. It will be slightly ahead of the moment it was asked.',
|
|
228
|
+
});
|
|
229
|
+
this.reveal(question);
|
|
230
|
+
}, timeout);
|
|
231
|
+
this.cueTimers.set(question.id, timer);
|
|
232
|
+
}
|
|
208
233
|
}
|
|
209
234
|
reveal(question) {
|
|
210
235
|
this.revealed.add(question.id);
|
|
236
|
+
const timer = this.cueTimers.get(question.id);
|
|
237
|
+
if (timer) {
|
|
238
|
+
clearTimeout(timer);
|
|
239
|
+
this.cueTimers.delete(question.id);
|
|
240
|
+
}
|
|
211
241
|
this.emit('question', question);
|
|
212
242
|
}
|
|
213
243
|
emit(event, payload) {
|
package/dist/protocol.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export interface LiveTally {
|
|
|
42
42
|
*/
|
|
43
43
|
textCount?: number;
|
|
44
44
|
}
|
|
45
|
-
export type ServerErrorCode = 'bad_message' | 'no_active_question' | 'already_voted' | 'unknown_option' | 'text_not_allowed' | 'text_too_long';
|
|
45
|
+
export type ServerErrorCode = 'bad_message' | 'no_active_question' | 'already_voted' | 'unknown_option' | 'text_not_allowed' | 'text_too_long' | 'cue_timeout';
|
|
46
46
|
export interface WelcomeMessage {
|
|
47
47
|
v: number;
|
|
48
48
|
type: 'welcome';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@antcdn/live-signaling",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Interactivity for AntCDN live streams and recordings: viewer count, questions, and voting, independent of any video player.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|