返回 DeepSeek-Reasonix
debuggerLease.ts
根目录 / desktop / electron / src / main / browser / debuggerLease.ts
1 import type { GuestDebugger } from "./guestView.js";
2 import { traceBrowserCommand } from "./diagnosticContext.js";
3
4 export const DEBUGGER_IDLE_MS = 1500;
5 interface SharedResource { dispose(transportAvailable: boolean): void }
6 interface Lease { users: number; pending: number; owned: boolean; resources: Map<object, SharedResource>; timer?: ReturnType<typeof setTimeout> }
7 export type DebuggerSender = (method: string, params?: unknown, sessionId?: string) => Promise<unknown>;
8 export type DebuggerRelease = (() => void) & { send: DebuggerSender; owned: boolean; shared<T extends SharedResource>(key: object, create: () => T): T };
9 const leases = new WeakMap<GuestDebugger, Lease>();
10
11 function idle(debuggerAPI: GuestDebugger, lease: Lease): void {
12 if (leases.get(debuggerAPI) !== lease || lease.users || lease.pending) return;
13 clearTimeout(lease.timer);
14 // Immediate root detach/re-attach between observe and input can strand
15 // Chromium's OOPIF isolated-world creation. Keep adjacent calls together.
16 lease.timer = setTimeout(() => {
17 if (leases.get(debuggerAPI) === lease && !lease.users && !lease.pending) disposeDebugger(debuggerAPI);
18 }, DEBUGGER_IDLE_MS);
19 lease.timer.unref?.();
20 }
21 export function acquireDebugger(debuggerAPI: GuestDebugger): DebuggerRelease {
22 let lease = leases.get(debuggerAPI);
23 if (lease && !debuggerAPI.isAttached()) { disposeDebugger(debuggerAPI); lease = undefined; }
24 if (!lease) {
25 const owned = !debuggerAPI.isAttached();
26 if (owned) debuggerAPI.attach("1.3");
27 lease = { users: 0, pending: 0, owned, resources: new Map() };
28 leases.set(debuggerAPI, lease);
29 }
30 clearTimeout(lease.timer);
31 lease.users++;
32 let released = false;
33 const release = () => {
34 if (released) return;
35 released = true;
36 lease.users--;
37 idle(debuggerAPI, lease);
38 };
39 return Object.assign(release, { owned: lease.owned, send: (method: string, params?: unknown, sessionId?: string) => sendDebuggerCommand(debuggerAPI, method, params, sessionId, lease), shared: <T extends SharedResource>(key: object, create: () => T): T => {
40 if (leases.get(debuggerAPI) !== lease) throw new Error("browser debugger lease is unavailable");
41 let resource = lease.resources.get(key);
42 if (!resource) { resource = create(); lease.resources.set(key, resource); }
43 return resource as T;
44 } });
45 }
46
47 // Count the underlying command, not its deadline-limited waiter. Timing out a
48 // waiter must not detach a connection with a reply still in flight.
49 export async function sendDebuggerCommand(debuggerAPI: GuestDebugger, method: string, params?: unknown, sessionId?: string, expected?: Lease): Promise<unknown> {
50 const lease = leases.get(debuggerAPI);
51 if (!lease || expected && lease !== expected || !debuggerAPI.isAttached()) throw new Error("browser debugger lease is unavailable");
52 clearTimeout(lease.timer);
53 lease.pending++;
54 const started = performance.now();
55 const target = sessionId ? "child" : "root";
56 traceBrowserCommand(method, target, "cdp_started", 0);
57 try {
58 const result = await debuggerAPI.sendCommand(method, params, sessionId);
59 traceBrowserCommand(method, target, "cdp_completed", Math.round(performance.now() - started));
60 return result;
61 } catch (error) {
62 traceBrowserCommand(method, target, "cdp_failed", Math.round(performance.now() - started));
63 throw error;
64 }
65 finally { lease.pending--; idle(debuggerAPI, lease); }
66 }
67
68 // Run before native destruction/crash recovery, while the debugger is still
69 // accessible. Late completions must not touch a replacement lease.
70 export function disposeDebugger(debuggerAPI: GuestDebugger, detach = true): void {
71 const lease = leases.get(debuggerAPI);
72 if (!lease) return;
73 leases.delete(debuggerAPI);
74 clearTimeout(lease.timer);
75 for (const resource of lease.resources.values()) resource.dispose(detach);
76 lease.resources.clear();
77 if (detach && lease.owned && debuggerAPI.isAttached()) debuggerAPI.detach();
78 }
79
79 lines TYPESCRIPT