Files
reverse-engineering-anything/src/domain/webCaptureFrameComparison.ts
T
morlutoandShuaixinHuang 27311f88d4 fix(browser): preserve capture uncertainty and frame ancestry
Parse required resource rows and retain valid siblings. Missing collections and malformed child subtrees keep unknown omission counts. Compare complete frame forests without capture-local IDs, and never infer aggregate DOM/metadata changes from omissions.

Co-authored-by: ShuaixinHuang <1836656514@qq.com>
2026-10-14 15:20:08 +08:00

50 lines
1.9 KiB
TypeScript

import type { WebPageInspection } from "./browserObservationSchemas.js";
import { digestCanonicalValue } from "./canonicalDigest.js";
/** Compare complete frame forests without treating capture-local IDs as identity. */
export const fingerprintWebCaptureFrames = (
frames: WebPageInspection["frames"],
): string | null => {
const byId = new Map(frames.map((frame) => [frame.frame_id, frame]));
if (byId.size !== frames.length) return null;
const remaining = new Map(frames.map((frame) => [frame.frame_id, 0]));
for (const frame of frames) {
if (frame.parent_frame_id === null) continue;
const count = remaining.get(frame.parent_frame_id);
if (count === undefined) return null;
remaining.set(frame.parent_frame_id, count + 1);
}
const ready = frames.filter((frame) => remaining.get(frame.frame_id) === 0);
const children = new Map<string, string[]>();
const roots: string[] = [];
let examined = 0;
for (let index = 0; index < ready.length; index += 1) {
const frame = ready[index];
if (frame === undefined) throw new Error("Missing ready frame");
const fingerprint = digestCanonicalValue({
url: frame.url,
origin: frame.origin,
children: (children.get(frame.frame_id) ?? []).sort(),
});
examined += 1;
const parentId = frame.parent_frame_id;
if (parentId === null) {
roots.push(fingerprint);
continue;
}
const siblings = children.get(parentId) ?? [];
siblings.push(fingerprint);
children.set(parentId, siblings);
const count = (remaining.get(parentId) ?? 0) - 1;
remaining.set(parentId, count);
if (count === 0) {
const parent = byId.get(parentId);
if (parent === undefined)
throw new Error("Missing admitted parent frame");
ready.push(parent);
}
}
// Cyclic ancestry never reaches the leaf queue and cannot prove equality.
return examined === frames.length ? digestCanonicalValue(roots.sort()) : null;
};