| 1 | import type { TranscriptSessionFollowerRuntime } from "./transcriptSessionFollowerRuntime"; |
| 2 | import { desktopHost } from "./desktopHost"; |
| 3 | |
| 4 | /** Load the synchronization engine only when a session starts following. */ |
| 5 | export class TranscriptSessionFollower { |
| 6 | private runtime?: TranscriptSessionFollowerRuntime; |
| 7 | private generation = 0; |
| 8 | private releaseServiceState?: () => void; |
| 9 | private readonly args: ConstructorParameters<typeof TranscriptSessionFollowerRuntime>; |
| 10 | |
| 11 | constructor(...args: ConstructorParameters<typeof TranscriptSessionFollowerRuntime>) { |
| 12 | this.args = args; |
| 13 | } |
| 14 | |
| 15 | get metrics() { return this.runtime?.metrics ?? { entries: 0, inlineBytes: 0 }; } |
| 16 | |
| 17 | async start(): Promise<void> { |
| 18 | this.stop(); |
| 19 | const generation = ++this.generation; |
| 20 | // Every local and remote owner observes the shell, including followers |
| 21 | // created by late hydration callbacks after stopping was already published. |
| 22 | const release = desktopHost().native.onServiceState(service => { |
| 23 | if (generation === this.generation && (service.phase === "stopping" || service.phase === "exited")) { |
| 24 | this.stop(false, "service_stopping"); |
| 25 | } |
| 26 | }); |
| 27 | // The preload replays its current state synchronously during registration. |
| 28 | if (generation !== this.generation) { release(); return; } |
| 29 | this.releaseServiceState = release; |
| 30 | const { TranscriptSessionFollowerRuntime } = await import("./transcriptSessionFollowerRuntime"); |
| 31 | if (generation !== this.generation) return; |
| 32 | const runtime = new TranscriptSessionFollowerRuntime(...this.args); |
| 33 | this.runtime = runtime; |
| 34 | try { await runtime.start(); } |
| 35 | catch (error) { if (generation === this.generation) throw error; } |
| 36 | } |
| 37 | |
| 38 | stop(closeSubscription = true, reason?: "service_stopping"): void { |
| 39 | this.generation++; |
| 40 | this.releaseServiceState?.(); |
| 41 | this.releaseServiceState = undefined; |
| 42 | this.runtime?.stop(closeSubscription, reason); |
| 43 | this.runtime = undefined; |
| 44 | } |
| 45 | } |
| 46 |