返回 DeepSeek-Reasonix
desktopEventRecovery.ts
根目录 / desktop / frontend / src / lib / desktopEventRecovery.ts
1 /** Read-side recovery only: callers retain their mounted UI and unsent drafts. */
2 export interface SnapshotRecoveryPorts<Scope, Snapshot> {
3 subscribe(callback: () => void): () => void;
4 capture(): Scope;
5 isCurrent(scope: Scope): boolean;
6 read(scope: Scope): Promise<Snapshot>;
7 apply(snapshot: Snapshot, scope: Scope): void;
8 failed?(error: unknown): void;
9 timer(callback: () => void, delay: number): unknown;
10 clearTimer(timer: unknown): void;
11 }
12
13 export function startDesktopEventRecovery<Scope, Snapshot>(ports: SnapshotRecoveryPorts<Scope, Snapshot>): () => void {
14 let disposed = false, inFlight = false, requested = 0, failures = 0;
15 let timer: unknown;
16 const repair = async () => {
17 if (disposed || inFlight) return;
18 ports.clearTimer(timer);
19 timer = undefined;
20 inFlight = true;
21 const version = requested;
22 const scope = ports.capture();
23 let retry = false;
24 try {
25 const snapshot = await ports.read(scope);
26 if (disposed || version !== requested) return;
27 if (!ports.isCurrent(scope)) { retry = true; return; }
28 ports.apply(snapshot, scope);
29 failures = 0;
30 } catch (error) {
31 if (!disposed && version === requested) { failures++; retry = true; ports.failed?.(error); }
32 } finally {
33 inFlight = false;
34 if (!disposed && version !== requested) void repair();
35 else if (!disposed && retry) timer = ports.timer(() => { void repair(); }, Math.min(30000, 1000 * 2 ** Math.min(failures, 5)));
36 }
37 };
38 const off = ports.subscribe(() => { requested++; void repair(); });
39 return () => { disposed = true; off(); ports.clearTimer(timer); };
40 }
41
41 lines TYPESCRIPT