@lumencast/runtime 0.1.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/LICENSE +201 -0
- package/README.md +79 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/animate/crossfade.d.ts +13 -0
- package/dist/animate/crossfade.d.ts.map +1 -0
- package/dist/animate/crossfade.js +10 -0
- package/dist/animate/crossfade.js.map +1 -0
- package/dist/animate/keyframes.d.ts +42 -0
- package/dist/animate/keyframes.d.ts.map +1 -0
- package/dist/animate/keyframes.js +94 -0
- package/dist/animate/keyframes.js.map +1 -0
- package/dist/animate/transitions.d.ts +38 -0
- package/dist/animate/transitions.d.ts.map +1 -0
- package/dist/animate/transitions.js +81 -0
- package/dist/animate/transitions.js.map +1 -0
- package/dist/app.d.ts +16 -0
- package/dist/app.d.ts.map +1 -0
- package/dist/app.js +35 -0
- package/dist/app.js.map +1 -0
- package/dist/broadcast-BqOhSNsY.js +11 -0
- package/dist/broadcast-BqOhSNsY.js.map +1 -0
- package/dist/control-CRFn328D.js +16 -0
- package/dist/control-CRFn328D.js.map +1 -0
- package/dist/dev-entry.d.ts +2 -0
- package/dist/dev-entry.d.ts.map +1 -0
- package/dist/dev-entry.js +31 -0
- package/dist/dev-entry.js.map +1 -0
- package/dist/index-DUhPPRvw.js +583 -0
- package/dist/index-DUhPPRvw.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.html +46 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/validate-options.d.ts +5 -0
- package/dist/internal/validate-options.d.ts.map +1 -0
- package/dist/internal/validate-options.js +19 -0
- package/dist/internal/validate-options.js.map +1 -0
- package/dist/lumencast.js +5 -0
- package/dist/lumencast.js.map +1 -0
- package/dist/modes/broadcast.d.ts +3 -0
- package/dist/modes/broadcast.d.ts.map +1 -0
- package/dist/modes/broadcast.js +9 -0
- package/dist/modes/broadcast.js.map +1 -0
- package/dist/modes/control.d.ts +4 -0
- package/dist/modes/control.d.ts.map +1 -0
- package/dist/modes/control.js +12 -0
- package/dist/modes/control.js.map +1 -0
- package/dist/modes/test.d.ts +4 -0
- package/dist/modes/test.d.ts.map +1 -0
- package/dist/modes/test.js +13 -0
- package/dist/modes/test.js.map +1 -0
- package/dist/mount.d.ts +3 -0
- package/dist/mount.d.ts.map +1 -0
- package/dist/mount.js +144 -0
- package/dist/mount.js.map +1 -0
- package/dist/overlay/control.d.ts +2 -0
- package/dist/overlay/control.d.ts.map +1 -0
- package/dist/overlay/control.js +127 -0
- package/dist/overlay/control.js.map +1 -0
- package/dist/overlay/runtime-context.d.ts +20 -0
- package/dist/overlay/runtime-context.d.ts.map +1 -0
- package/dist/overlay/runtime-context.js +14 -0
- package/dist/overlay/runtime-context.js.map +1 -0
- package/dist/overlay/status-pill.d.ts +2 -0
- package/dist/overlay/status-pill.d.ts.map +1 -0
- package/dist/overlay/status-pill.js +29 -0
- package/dist/overlay/status-pill.js.map +1 -0
- package/dist/overlay/test.d.ts +5 -0
- package/dist/overlay/test.d.ts.map +1 -0
- package/dist/overlay/test.js +116 -0
- package/dist/overlay/test.js.map +1 -0
- package/dist/render/bundle.d.ts +102 -0
- package/dist/render/bundle.d.ts.map +1 -0
- package/dist/render/bundle.js +86 -0
- package/dist/render/bundle.js.map +1 -0
- package/dist/render/fill.d.ts +41 -0
- package/dist/render/fill.d.ts.map +1 -0
- package/dist/render/fill.js +95 -0
- package/dist/render/fill.js.map +1 -0
- package/dist/render/keyframe-player.d.ts +10 -0
- package/dist/render/keyframe-player.d.ts.map +1 -0
- package/dist/render/keyframe-player.js +65 -0
- package/dist/render/keyframe-player.js.map +1 -0
- package/dist/render/primitives/frame.d.ts +12 -0
- package/dist/render/primitives/frame.d.ts.map +1 -0
- package/dist/render/primitives/frame.js +65 -0
- package/dist/render/primitives/frame.js.map +1 -0
- package/dist/render/primitives/grid.d.ts +4 -0
- package/dist/render/primitives/grid.d.ts.map +1 -0
- package/dist/render/primitives/grid.js +14 -0
- package/dist/render/primitives/grid.js.map +1 -0
- package/dist/render/primitives/image.d.ts +5 -0
- package/dist/render/primitives/image.d.ts.map +1 -0
- package/dist/render/primitives/image.js +25 -0
- package/dist/render/primitives/image.js.map +1 -0
- package/dist/render/primitives/index.d.ts +10 -0
- package/dist/render/primitives/index.d.ts.map +1 -0
- package/dist/render/primitives/index.js +22 -0
- package/dist/render/primitives/index.js.map +1 -0
- package/dist/render/primitives/instance.d.ts +4 -0
- package/dist/render/primitives/instance.d.ts.map +1 -0
- package/dist/render/primitives/instance.js +35 -0
- package/dist/render/primitives/instance.js.map +1 -0
- package/dist/render/primitives/media.d.ts +6 -0
- package/dist/render/primitives/media.d.ts.map +1 -0
- package/dist/render/primitives/media.js +19 -0
- package/dist/render/primitives/media.js.map +1 -0
- package/dist/render/primitives/shape.d.ts +12 -0
- package/dist/render/primitives/shape.d.ts.map +1 -0
- package/dist/render/primitives/shape.js +66 -0
- package/dist/render/primitives/shape.js.map +1 -0
- package/dist/render/primitives/stack.d.ts +13 -0
- package/dist/render/primitives/stack.d.ts.map +1 -0
- package/dist/render/primitives/stack.js +45 -0
- package/dist/render/primitives/stack.js.map +1 -0
- package/dist/render/primitives/text.d.ts +6 -0
- package/dist/render/primitives/text.d.ts.map +1 -0
- package/dist/render/primitives/text.js +27 -0
- package/dist/render/primitives/text.js.map +1 -0
- package/dist/render/scope.d.ts +10 -0
- package/dist/render/scope.d.ts.map +1 -0
- package/dist/render/scope.js +27 -0
- package/dist/render/scope.js.map +1 -0
- package/dist/render/stagger-context.d.ts +9 -0
- package/dist/render/stagger-context.d.ts.map +1 -0
- package/dist/render/stagger-context.js +22 -0
- package/dist/render/stagger-context.js.map +1 -0
- package/dist/render/tree.d.ts +9 -0
- package/dist/render/tree.d.ts.map +1 -0
- package/dist/render/tree.js +139 -0
- package/dist/render/tree.js.map +1 -0
- package/dist/render/universal-wrapper.d.ts +16 -0
- package/dist/render/universal-wrapper.d.ts.map +1 -0
- package/dist/render/universal-wrapper.js +58 -0
- package/dist/render/universal-wrapper.js.map +1 -0
- package/dist/state/apply-delta.d.ts +11 -0
- package/dist/state/apply-delta.d.ts.map +1 -0
- package/dist/state/apply-delta.js +23 -0
- package/dist/state/apply-delta.js.map +1 -0
- package/dist/state/apply-snapshot.d.ts +6 -0
- package/dist/state/apply-snapshot.d.ts.map +1 -0
- package/dist/state/apply-snapshot.js +6 -0
- package/dist/state/apply-snapshot.js.map +1 -0
- package/dist/state/store.d.ts +28 -0
- package/dist/state/store.d.ts.map +1 -0
- package/dist/state/store.js +119 -0
- package/dist/state/store.js.map +1 -0
- package/dist/status-pill-DCHvrd_y.js +241 -0
- package/dist/status-pill-DCHvrd_y.js.map +1 -0
- package/dist/test-DBCtwx_I.js +210 -0
- package/dist/test-DBCtwx_I.js.map +1 -0
- package/dist/transport/reconnect.d.ts +22 -0
- package/dist/transport/reconnect.d.ts.map +1 -0
- package/dist/transport/reconnect.js +60 -0
- package/dist/transport/reconnect.js.map +1 -0
- package/dist/transport/ws.d.ts +66 -0
- package/dist/transport/ws.d.ts.map +1 -0
- package/dist/transport/ws.js +270 -0
- package/dist/transport/ws.js.map +1 -0
- package/dist/tree-CnhX02kd.js +494 -0
- package/dist/tree-CnhX02kd.js.map +1 -0
- package/dist/types.d.ts +38 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
- package/src/animate/crossfade.tsx +31 -0
- package/src/animate/keyframes.ts +142 -0
- package/src/animate/transitions.ts +116 -0
- package/src/app.tsx +84 -0
- package/src/dev-entry.tsx +38 -0
- package/src/index.ts +24 -0
- package/src/internal/validate-options.ts +20 -0
- package/src/modes/broadcast.tsx +8 -0
- package/src/modes/control.tsx +17 -0
- package/src/modes/test.tsx +19 -0
- package/src/mount.ts +169 -0
- package/src/overlay/control.tsx +239 -0
- package/src/overlay/runtime-context.tsx +37 -0
- package/src/overlay/status-pill.tsx +37 -0
- package/src/overlay/test.tsx +213 -0
- package/src/render/bundle.ts +208 -0
- package/src/render/fill.tsx +163 -0
- package/src/render/keyframe-player.tsx +89 -0
- package/src/render/primitives/frame.tsx +78 -0
- package/src/render/primitives/grid.tsx +20 -0
- package/src/render/primitives/image.tsx +35 -0
- package/src/render/primitives/index.ts +35 -0
- package/src/render/primitives/instance.tsx +70 -0
- package/src/render/primitives/media.tsx +28 -0
- package/src/render/primitives/shape.tsx +135 -0
- package/src/render/primitives/stack.tsx +48 -0
- package/src/render/primitives/text.tsx +38 -0
- package/src/render/scope.tsx +27 -0
- package/src/render/stagger-context.tsx +24 -0
- package/src/render/tree.tsx +182 -0
- package/src/render/universal-wrapper.tsx +95 -0
- package/src/state/apply-delta.ts +24 -0
- package/src/state/apply-snapshot.ts +8 -0
- package/src/state/store.ts +141 -0
- package/src/transport/reconnect.ts +83 -0
- package/src/transport/ws.ts +359 -0
- package/src/types.ts +54 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Exponential backoff schedule for the WS reconnect loop.
|
|
2
|
+
//
|
|
3
|
+
// Aggressive at first (200 ms, 400 ms, 800 ms…) then capped at 5 s so a
|
|
4
|
+
// sustained outage doesn't hammer the gateway. Jittered to avoid thundering
|
|
5
|
+
// herds when several Lumencast instances reconnect together (e.g. a CEF
|
|
6
|
+
// host + a webview waking from suspend at the same time).
|
|
7
|
+
//
|
|
8
|
+
// The reference schedule in LSDP/1 §7 is "0 ms, 500 ms, 1 s, 2 s, 4 s, 8 s,
|
|
9
|
+
// 15 s, 30 s, 60 s cap". This implementation defaults are tighter; both are
|
|
10
|
+
// MAY-bounded by the spec.
|
|
11
|
+
const DEFAULTS = {
|
|
12
|
+
initial: 200,
|
|
13
|
+
max: 5_000,
|
|
14
|
+
factor: 2,
|
|
15
|
+
jitter: 0.2,
|
|
16
|
+
};
|
|
17
|
+
class ScheduleImpl {
|
|
18
|
+
opts;
|
|
19
|
+
random;
|
|
20
|
+
_attempt = 0;
|
|
21
|
+
constructor(opts, random) {
|
|
22
|
+
this.opts = opts;
|
|
23
|
+
this.random = random;
|
|
24
|
+
}
|
|
25
|
+
get attempt() {
|
|
26
|
+
return this._attempt;
|
|
27
|
+
}
|
|
28
|
+
delayFor(attempt) {
|
|
29
|
+
if (!Number.isInteger(attempt) || attempt < 1) {
|
|
30
|
+
throw new RangeError(`attempt must be a positive integer, got ${attempt}`);
|
|
31
|
+
}
|
|
32
|
+
this._attempt = attempt;
|
|
33
|
+
const base = Math.min(this.opts.initial * Math.pow(this.opts.factor, attempt - 1), this.opts.max);
|
|
34
|
+
if (this.opts.jitter <= 0)
|
|
35
|
+
return base;
|
|
36
|
+
const offset = (this.random() * 2 - 1) * this.opts.jitter * base;
|
|
37
|
+
return Math.max(0, base + offset);
|
|
38
|
+
}
|
|
39
|
+
reset() {
|
|
40
|
+
this._attempt = 0;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function createReconnectSchedule(opts = {}) {
|
|
44
|
+
const merged = {
|
|
45
|
+
initial: opts.initial ?? DEFAULTS.initial,
|
|
46
|
+
max: opts.max ?? DEFAULTS.max,
|
|
47
|
+
factor: opts.factor ?? DEFAULTS.factor,
|
|
48
|
+
jitter: opts.jitter ?? DEFAULTS.jitter,
|
|
49
|
+
};
|
|
50
|
+
if (merged.initial <= 0)
|
|
51
|
+
throw new RangeError("initial must be > 0");
|
|
52
|
+
if (merged.max < merged.initial)
|
|
53
|
+
throw new RangeError("max must be >= initial");
|
|
54
|
+
if (merged.factor < 1)
|
|
55
|
+
throw new RangeError("factor must be >= 1");
|
|
56
|
+
if (merged.jitter < 0 || merged.jitter > 1)
|
|
57
|
+
throw new RangeError("jitter must be within [0, 1]");
|
|
58
|
+
return new ScheduleImpl(merged, opts.random ?? Math.random);
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=reconnect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconnect.js","sourceRoot":"","sources":["../../src/transport/reconnect.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,wEAAwE;AACxE,4EAA4E;AAC5E,wEAAwE;AACxE,0DAA0D;AAC1D,EAAE;AACF,4EAA4E;AAC5E,4EAA4E;AAC5E,2BAA2B;AAe3B,MAAM,QAAQ,GAAuD;IACnE,OAAO,EAAE,GAAG;IACZ,GAAG,EAAE,KAAK;IACV,MAAM,EAAE,CAAC;IACT,MAAM,EAAE,GAAG;CACZ,CAAC;AAWF,MAAM,YAAY;IAGG;IACA;IAHX,QAAQ,GAAG,CAAC,CAAC;IACrB,YACmB,IAAwD,EACxD,MAAoB;QADpB,SAAI,GAAJ,IAAI,CAAoD;QACxD,WAAM,GAAN,MAAM,CAAc;IACpC,CAAC;IAEJ,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,QAAQ,CAAC,OAAe;QACtB,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,IAAI,UAAU,CAAC,2CAA2C,OAAO,EAAE,CAAC,CAAC;QAC7E,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;QACxB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CACnB,IAAI,CAAC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,GAAG,CAAC,CAAC,EAC3D,IAAI,CAAC,IAAI,CAAC,GAAG,CACd,CAAC;QACF,IAAI,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,MAAM,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;QACjE,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAAC,CAAC;IACpC,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;IACpB,CAAC;CACF;AAED,MAAM,UAAU,uBAAuB,CAAC,OAAiC,EAAE;IACzE,MAAM,MAAM,GAAG;QACb,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,QAAQ,CAAC,OAAO;QACzC,GAAG,EAAE,IAAI,CAAC,GAAG,IAAI,QAAQ,CAAC,GAAG;QAC7B,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM;QACtC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,QAAQ,CAAC,MAAM;KACvC,CAAC;IACF,IAAI,MAAM,CAAC,OAAO,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,qBAAqB,CAAC,CAAC;IACrE,IAAI,MAAM,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO;QAAE,MAAM,IAAI,UAAU,CAAC,wBAAwB,CAAC,CAAC;IAChF,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,qBAAqB,CAAC,CAAC;IACnE,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,8BAA8B,CAAC,CAAC;IACjG,OAAO,IAAI,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,CAAC;AAC9D,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type DeltaFrame, type ErrorCode, type ErrorFrame, type Patch, type SceneChangedFrame, type SnapshotFrame } from "@lumencast/protocol";
|
|
2
|
+
import type { LumencastToken } from "../types.js";
|
|
3
|
+
import { type ReconnectScheduleOptions } from "./reconnect.js";
|
|
4
|
+
export type ConnectionStatus = "disconnected" | "connecting" | "live";
|
|
5
|
+
export interface WsClientOptions {
|
|
6
|
+
url: string;
|
|
7
|
+
token: LumencastToken;
|
|
8
|
+
/** Optional scene identifier (test mode). */
|
|
9
|
+
scene?: string;
|
|
10
|
+
/** Optional session identifier (test mode). */
|
|
11
|
+
session?: string;
|
|
12
|
+
/** Override the WebSocket constructor (for tests / non-browser hosts). */
|
|
13
|
+
webSocketImpl?: typeof WebSocket;
|
|
14
|
+
/** Reconnect tuning. */
|
|
15
|
+
reconnect?: ReconnectScheduleOptions;
|
|
16
|
+
/** Inject scheduler for tests. */
|
|
17
|
+
scheduler?: {
|
|
18
|
+
setTimeout: typeof globalThis.setTimeout;
|
|
19
|
+
clearTimeout: typeof globalThis.clearTimeout;
|
|
20
|
+
};
|
|
21
|
+
onStatus?: (status: ConnectionStatus) => void;
|
|
22
|
+
onSnapshot?: (frame: SnapshotFrame) => void;
|
|
23
|
+
onDelta?: (frame: DeltaFrame) => void;
|
|
24
|
+
onSceneChanged?: (frame: SceneChangedFrame) => void;
|
|
25
|
+
onServerError?: (frame: ErrorFrame) => void;
|
|
26
|
+
/** Wire-level / codec / unrecoverable errors. */
|
|
27
|
+
onTransportError?: (err: TransportError) => void;
|
|
28
|
+
}
|
|
29
|
+
export declare class TransportError extends Error {
|
|
30
|
+
readonly recoverable: boolean;
|
|
31
|
+
readonly code: ErrorCode;
|
|
32
|
+
readonly cause?: unknown;
|
|
33
|
+
constructor(message: string, recoverable: boolean, code?: ErrorCode, cause?: unknown);
|
|
34
|
+
}
|
|
35
|
+
export declare class WsClient {
|
|
36
|
+
private status;
|
|
37
|
+
private socket;
|
|
38
|
+
private token;
|
|
39
|
+
private readonly url;
|
|
40
|
+
private readonly WebSocketCtor;
|
|
41
|
+
private readonly schedule;
|
|
42
|
+
private readonly seq;
|
|
43
|
+
private readonly opts;
|
|
44
|
+
private readonly scheduler;
|
|
45
|
+
private reconnectTimer;
|
|
46
|
+
private active;
|
|
47
|
+
constructor(opts: WsClientOptions);
|
|
48
|
+
/** Open and start the connection lifecycle. Idempotent. */
|
|
49
|
+
start(): void;
|
|
50
|
+
/** Send `input` patches to the server. No-op if not connected. */
|
|
51
|
+
sendInput(patches: Patch[]): void;
|
|
52
|
+
/** Replace the auth token. Closes and reopens with the new token. */
|
|
53
|
+
setToken(token: LumencastToken): void;
|
|
54
|
+
/** Tear down for good. No more reconnect attempts. */
|
|
55
|
+
close(): void;
|
|
56
|
+
private openSocket;
|
|
57
|
+
private handleOpen;
|
|
58
|
+
private handleMessage;
|
|
59
|
+
private handleError;
|
|
60
|
+
private handleClose;
|
|
61
|
+
private scheduleReconnect;
|
|
62
|
+
private cancelReconnect;
|
|
63
|
+
private closeSocket;
|
|
64
|
+
private setStatus;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=ws.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../../src/transport/ws.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
// LSDP/1 WebSocket client.
|
|
2
|
+
//
|
|
3
|
+
// Lifecycle (LSDP/1 §6):
|
|
4
|
+
// 1. open() — opens the WS with subprotocol `lsdp.v1`
|
|
5
|
+
// 2. on open: send `subscribe` with the resolved token (and scene + session
|
|
6
|
+
// for test mode)
|
|
7
|
+
// 3. server replies `snapshot` (seq=1) → emit onSnapshot
|
|
8
|
+
// 4. subsequent `delta` / `scene_changed` / `error` / `pong` are dispatched
|
|
9
|
+
// 5. on a sequence gap → close + reconnect (fresh snapshot)
|
|
10
|
+
// 6. on close → reconnect with backoff, unless close was triggered by close()
|
|
11
|
+
// 7. setToken() opens a parallel WS with the new token; once its snapshot
|
|
12
|
+
// lands, atomically swap and close the old socket — no rendering gap
|
|
13
|
+
import { decodeServerFrame, encodeFrame, LumencastError, SequenceTracker, WS_SUBPROTOCOL_V1_1, WS_SUBPROTOCOLS, input as inputFrame, subscribe as subscribeFrame, } from "@lumencast/protocol";
|
|
14
|
+
import { createReconnectSchedule, } from "./reconnect.js";
|
|
15
|
+
export class TransportError extends Error {
|
|
16
|
+
recoverable;
|
|
17
|
+
code;
|
|
18
|
+
cause;
|
|
19
|
+
constructor(message, recoverable, code = "INTERNAL", cause) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = "TransportError";
|
|
22
|
+
this.recoverable = recoverable;
|
|
23
|
+
this.code = code;
|
|
24
|
+
this.cause = cause;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class WsClient {
|
|
28
|
+
status = "disconnected";
|
|
29
|
+
socket = null;
|
|
30
|
+
token;
|
|
31
|
+
url;
|
|
32
|
+
WebSocketCtor;
|
|
33
|
+
schedule;
|
|
34
|
+
seq = new SequenceTracker();
|
|
35
|
+
opts;
|
|
36
|
+
scheduler;
|
|
37
|
+
reconnectTimer = null;
|
|
38
|
+
active = true;
|
|
39
|
+
constructor(opts) {
|
|
40
|
+
this.opts = opts;
|
|
41
|
+
this.url = opts.url;
|
|
42
|
+
this.token = opts.token;
|
|
43
|
+
const ctor = opts.webSocketImpl ?? globalThis.WebSocket;
|
|
44
|
+
if (!ctor) {
|
|
45
|
+
throw new TypeError("Lumencast WsClient: no WebSocket implementation found in this environment");
|
|
46
|
+
}
|
|
47
|
+
this.WebSocketCtor = ctor;
|
|
48
|
+
this.schedule = createReconnectSchedule(opts.reconnect);
|
|
49
|
+
this.scheduler = opts.scheduler ?? {
|
|
50
|
+
setTimeout: globalThis.setTimeout.bind(globalThis),
|
|
51
|
+
clearTimeout: globalThis.clearTimeout.bind(globalThis),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/** Open and start the connection lifecycle. Idempotent. */
|
|
55
|
+
start() {
|
|
56
|
+
if (!this.active)
|
|
57
|
+
return;
|
|
58
|
+
if (this.socket || this.status === "connecting")
|
|
59
|
+
return;
|
|
60
|
+
void this.openSocket();
|
|
61
|
+
}
|
|
62
|
+
/** Send `input` patches to the server. No-op if not connected. */
|
|
63
|
+
sendInput(patches) {
|
|
64
|
+
if (!this.socket || this.socket.readyState !== this.WebSocketCtor.OPEN)
|
|
65
|
+
return;
|
|
66
|
+
if (patches.length === 0)
|
|
67
|
+
return;
|
|
68
|
+
this.socket.send(encodeFrame(inputFrame(patches)));
|
|
69
|
+
}
|
|
70
|
+
/** Replace the auth token. Closes and reopens with the new token. */
|
|
71
|
+
setToken(token) {
|
|
72
|
+
this.token = token;
|
|
73
|
+
if (!this.active)
|
|
74
|
+
return;
|
|
75
|
+
if (this.socket) {
|
|
76
|
+
this.closeSocket();
|
|
77
|
+
this.scheduleReconnect(true);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Tear down for good. No more reconnect attempts. */
|
|
81
|
+
close() {
|
|
82
|
+
if (!this.active)
|
|
83
|
+
return;
|
|
84
|
+
this.active = false;
|
|
85
|
+
this.cancelReconnect();
|
|
86
|
+
this.closeSocket();
|
|
87
|
+
this.setStatus("disconnected");
|
|
88
|
+
}
|
|
89
|
+
// --- internals --------------------------------------------------
|
|
90
|
+
async openSocket() {
|
|
91
|
+
if (!this.active)
|
|
92
|
+
return;
|
|
93
|
+
this.setStatus("connecting");
|
|
94
|
+
let resolvedToken;
|
|
95
|
+
try {
|
|
96
|
+
resolvedToken = await resolveToken(this.token);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
this.opts.onTransportError?.(new TransportError(`failed to resolve token: ${err.message}`, true, "AUTH_DENIED", err));
|
|
100
|
+
this.scheduleReconnect();
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (!this.active)
|
|
104
|
+
return;
|
|
105
|
+
let socket;
|
|
106
|
+
try {
|
|
107
|
+
// Advertise both 1.1 (preferred) and 1.0 (fallback) ; the server
|
|
108
|
+
// picks one. Spread to a mutable array — the WebSocket constructor
|
|
109
|
+
// type expects string[].
|
|
110
|
+
socket = new this.WebSocketCtor(this.url, [...WS_SUBPROTOCOLS]);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
this.opts.onTransportError?.(new TransportError(`failed to open WebSocket: ${err.message}`, true, "INTERNAL", err));
|
|
114
|
+
this.scheduleReconnect();
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
this.socket = socket;
|
|
118
|
+
socket.onopen = () => this.handleOpen(resolvedToken);
|
|
119
|
+
socket.onmessage = (event) => this.handleMessage(event);
|
|
120
|
+
socket.onerror = (event) => this.handleError(event);
|
|
121
|
+
socket.onclose = (event) => this.handleClose(event);
|
|
122
|
+
}
|
|
123
|
+
handleOpen(token) {
|
|
124
|
+
if (!this.socket)
|
|
125
|
+
return;
|
|
126
|
+
// LSDP/1.1 §4.1, §18 — if we have a previously-observed seq AND the
|
|
127
|
+
// negotiated subprotocol is 1.1, request an incremental resume.
|
|
128
|
+
// The server will EITHER ship buffered deltas (cache stays valid)
|
|
129
|
+
// OR a fresh snapshot which we rebase via observeSnapshot.
|
|
130
|
+
const subprotocol = this.socket.protocol;
|
|
131
|
+
const canResume = subprotocol === WS_SUBPROTOCOL_V1_1 && this.seq.last > 0;
|
|
132
|
+
const sinceSequence = canResume ? this.seq.last : undefined;
|
|
133
|
+
if (!canResume) {
|
|
134
|
+
// Fresh subscription (no resume) — reset the tracker baseline.
|
|
135
|
+
this.seq.reset();
|
|
136
|
+
}
|
|
137
|
+
const frame = subscribeFrame({
|
|
138
|
+
token,
|
|
139
|
+
...(this.opts.scene !== undefined ? { scene: this.opts.scene } : {}),
|
|
140
|
+
...(this.opts.session !== undefined ? { session: this.opts.session } : {}),
|
|
141
|
+
...(sinceSequence !== undefined ? { since_sequence: sinceSequence } : {}),
|
|
142
|
+
});
|
|
143
|
+
this.socket.send(encodeFrame(frame));
|
|
144
|
+
}
|
|
145
|
+
handleMessage(event) {
|
|
146
|
+
const data = typeof event.data === "string" ? event.data : "";
|
|
147
|
+
if (!data)
|
|
148
|
+
return;
|
|
149
|
+
let frame;
|
|
150
|
+
try {
|
|
151
|
+
frame = decodeServerFrame(data);
|
|
152
|
+
}
|
|
153
|
+
catch (err) {
|
|
154
|
+
const message = err instanceof LumencastError ? err.message : err.message;
|
|
155
|
+
const code = err instanceof LumencastError ? err.code : "INTERNAL";
|
|
156
|
+
this.opts.onTransportError?.(new TransportError(`codec: ${message}`, true, code, err));
|
|
157
|
+
this.closeSocket();
|
|
158
|
+
this.scheduleReconnect();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if (frame === null)
|
|
162
|
+
return; // unknown frame type — forward-compat ignore
|
|
163
|
+
switch (frame.type) {
|
|
164
|
+
case "snapshot": {
|
|
165
|
+
// LSDP/1.1 §18.1.1 — snapshot rebases the tracker to its seq
|
|
166
|
+
// value. This is the ONLY valid way to set or change the
|
|
167
|
+
// tracker's baseline (handles fresh sub, scene_changed, and
|
|
168
|
+
// back-pressure recovery uniformly).
|
|
169
|
+
if (frame.seq < 1) {
|
|
170
|
+
this.opts.onTransportError?.(new TransportError(`snapshot seq must be >= 1, got ${frame.seq}`, true, "VERSION_GAP"));
|
|
171
|
+
this.closeSocket();
|
|
172
|
+
this.scheduleReconnect();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
this.seq.observeSnapshot(frame.seq);
|
|
176
|
+
this.schedule.reset();
|
|
177
|
+
this.setStatus("live");
|
|
178
|
+
this.opts.onSnapshot?.(frame);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
case "delta": {
|
|
182
|
+
const obs = this.seq.observe(frame.seq);
|
|
183
|
+
if (obs.kind === "gap") {
|
|
184
|
+
this.opts.onTransportError?.(new TransportError(`sequence gap: expected ${this.seq.last + 1}, got ${frame.seq}`, true, "VERSION_GAP"));
|
|
185
|
+
this.closeSocket();
|
|
186
|
+
this.scheduleReconnect();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
if (obs.kind === "duplicate")
|
|
190
|
+
return; // silent drop per LSDP/1 §5
|
|
191
|
+
this.opts.onDelta?.(frame);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
case "scene_changed": {
|
|
195
|
+
// The next snapshot will rebase the tracker via observeSnapshot.
|
|
196
|
+
// Reset here so the tracker doesn't fault on the SceneChanged's
|
|
197
|
+
// own seq (which advances prev's counter one final step).
|
|
198
|
+
this.seq.reset();
|
|
199
|
+
this.opts.onSceneChanged?.(frame);
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
case "error": {
|
|
203
|
+
this.opts.onServerError?.(frame);
|
|
204
|
+
if (!frame.recoverable)
|
|
205
|
+
this.close();
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
case "pong":
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
handleError(_event) {
|
|
213
|
+
// The browser does not give us a real reason — `close` will follow.
|
|
214
|
+
}
|
|
215
|
+
handleClose(event) {
|
|
216
|
+
this.socket = null;
|
|
217
|
+
if (!this.active) {
|
|
218
|
+
this.setStatus("disconnected");
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (event.code === 4401 || event.code === 4403 || event.code === 1008) {
|
|
222
|
+
// Auth-related close codes: not recoverable without operator intervention.
|
|
223
|
+
this.opts.onTransportError?.(new TransportError(`server closed: ${event.code} ${event.reason}`, false, "AUTH_DENIED"));
|
|
224
|
+
this.close();
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
this.scheduleReconnect();
|
|
228
|
+
}
|
|
229
|
+
scheduleReconnect(immediate = false) {
|
|
230
|
+
if (!this.active)
|
|
231
|
+
return;
|
|
232
|
+
this.cancelReconnect();
|
|
233
|
+
const attempt = (this.schedule.attempt || 0) + 1;
|
|
234
|
+
const delay = immediate ? 0 : this.schedule.delayFor(attempt);
|
|
235
|
+
this.setStatus("disconnected");
|
|
236
|
+
this.reconnectTimer = this.scheduler.setTimeout(() => {
|
|
237
|
+
this.reconnectTimer = null;
|
|
238
|
+
void this.openSocket();
|
|
239
|
+
}, delay);
|
|
240
|
+
}
|
|
241
|
+
cancelReconnect() {
|
|
242
|
+
if (this.reconnectTimer) {
|
|
243
|
+
this.scheduler.clearTimeout(this.reconnectTimer);
|
|
244
|
+
this.reconnectTimer = null;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
closeSocket() {
|
|
248
|
+
if (this.socket) {
|
|
249
|
+
try {
|
|
250
|
+
this.socket.close(1000, "client closing");
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
// ignore
|
|
254
|
+
}
|
|
255
|
+
this.socket = null;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
setStatus(next) {
|
|
259
|
+
if (this.status === next)
|
|
260
|
+
return;
|
|
261
|
+
this.status = next;
|
|
262
|
+
this.opts.onStatus?.(next);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function resolveToken(token) {
|
|
266
|
+
if (typeof token === "string")
|
|
267
|
+
return token;
|
|
268
|
+
return await token.fetch();
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=ws.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.js","sourceRoot":"","sources":["../../src/transport/ws.ts"],"names":[],"mappings":"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"}
|