返回 DeepSeek-Reasonix
refResolver.ts
根目录 / desktop / electron / src / main / browser / refResolver.ts
1 import { parseRef, type DocumentBinding, type FrameBinding } from "./documents.js";
2 import { staleReference } from "./errors.js";
3 import type { GuestFrame, GuestPage } from "./guestView.js";
4 import { LOCATE_SCRIPT_SOURCE, RESOLVE_SCRIPT_SOURCE, scriptCall, type LocateOutput, type ResolvedElement, type ResolveOutput } from "./pageScripts.js";
5 import { findFrame, REGISTRY_KEY, runInFrame } from "./snapshot.js";
6
7 export interface RefFrame {
8 frame: GuestFrame;
9 binding: FrameBinding;
10 isMainFrame: boolean;
11 }
12
13 export interface ResolvedRef extends RefFrame {
14 element: ResolvedElement;
15 }
16
17 export interface LocatedRef extends RefFrame {
18 ref: string;
19 snapshotId: string;
20 tag: string;
21 type: string;
22 path: string;
23 }
24
25 export type RefOutcome<T> = { ok: true; value: T } | { ok: false; reason: string };
26
27 function hasOk(value: unknown): value is { ok: boolean; reason?: string } {
28 return typeof value === "object" && value !== null && typeof (value as { ok?: unknown }).ok === "boolean";
29 }
30
31 export function frameForRef(page: GuestPage, binding: DocumentBinding, ref: string): RefFrame {
32 const parsed = parseRef(ref);
33 if (!parsed) throw staleReference(`malformed ref ${JSON.stringify(ref)}`);
34 const frameBinding = binding.frames.find((entry) => entry.prefix === parsed.prefix);
35 if (!frameBinding) throw staleReference(`ref ${ref} names a frame the snapshot did not see`);
36 const frame = findFrame(page, frameBinding.frameTreeNodeId);
37 if (!frame) throw staleReference(`frame of ${ref} is gone`);
38 return { frame, binding: frameBinding, isMainFrame: frame.frameTreeNodeId === page.mainFrame.frameTreeNodeId };
39 }
40
41 async function runRefScript(page: GuestPage, target: RefFrame, source: string, snapshotId: string, ref: string, extra: Record<string, unknown>): Promise<unknown> {
42 try {
43 return await runInFrame(page, target.frame, scriptCall(source, { key: REGISTRY_KEY, snapshotId, docId: target.binding.docId, ref, ...extra }));
44 } catch (error) {
45 throw staleReference(`frame of ${ref} cannot run scripts: ${String(error)}`);
46 }
47 }
48
49 // Resolves a snapshot ref to viewport geometry inside the frame that produced
50 // it. Stale refs throw -32010; a live ref that cannot be acted on reports why.
51 export async function resolveRef(page: GuestPage, binding: DocumentBinding, ref: string, scroll: boolean): Promise<RefOutcome<ResolvedRef>> {
52 const target = frameForRef(page, binding, ref);
53 const raw = await runRefScript(page, target, RESOLVE_SCRIPT_SOURCE, binding.snapshotId, ref, { scroll });
54 if (!hasOk(raw)) throw staleReference(`resolver returned nothing for ${ref}`);
55 const out = raw as ResolveOutput;
56 if (!out.ok) {
57 if (out.reason === "stale") throw staleReference(`${ref} predates the current document`);
58 return { ok: false, reason: out.reason };
59 }
60 if (!out.frameOffsetKnown) return { ok: false, reason: "element sits in a cross-origin frame; its position cannot be determined" };
61 return { ok: true, value: { ...target, element: out } };
62 }
63
64 export async function locateRef(page: GuestPage, binding: DocumentBinding, ref: string): Promise<RefOutcome<LocatedRef>> {
65 const target = frameForRef(page, binding, ref);
66 const raw = await runRefScript(page, target, LOCATE_SCRIPT_SOURCE, binding.snapshotId, ref, {});
67 if (!hasOk(raw)) throw staleReference(`locator returned nothing for ${ref}`);
68 const out = raw as LocateOutput;
69 if (!out.ok) {
70 if (out.reason === "stale") throw staleReference(`${ref} predates the current document`);
71 return { ok: false, reason: out.reason };
72 }
73 return { ok: true, value: { ...target, ref, snapshotId: binding.snapshotId, tag: out.tag, type: out.type, path: out.path } };
74 }
75
75 lines TYPESCRIPT