mirror of
https://github.com/morluto/rea.git
synced 2026-10-29 10:55:57 +00:00
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>
50 lines
1.9 KiB
TypeScript
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;
|
|
};
|