SPB Git forge
7commits 1branches 0releases
229.0 KBsize
maindefault branch
12 days agolast push
TypeScript 91.8% HTML 3.2% JavaScript 3% SQL 1.4% CSS 0.7%
2.1 KB · 51 lines typescript
Raw Blame History
1import fs from "node:fs";2import path from "node:path";3import { fileURLToPath } from "node:url";4import type { Page } from "playwright";56/**7 * Raw DOM candidates scanned in-page (§13–§15). Generic: relies on links, landmarks, ARIA roles,8 * media elements and text density — never on generated class names. Adapters refine these later.9 *10 * The in-page code lives in `snapshot.page.js` (plain JS) because TypeScript transpilers inject11 * helpers such as `__name` into function bodies, which do not exist inside the page.12 */13export interface DomCandidate {14  kind: "link" | "article" | "video" | "image" | "heading" | "search" | "button";15  href?: string;16  text: string; // visible text (trimmed)17  aria_label?: string;18  role?: string;19  region: string; // main / feed / sidebar / dialog / header / navigation / comments / unknown20  index: number; // document order among candidates21  visible: boolean; // within viewport22  top: number; // bounding rect top (for feed position)23  meta?: Record<string, string>; // duration, views, author text found in the same card24  locator_hint: string; // a stable-ish CSS selector path we can use to click (prefers href / aria-label)25}2627export interface DomSnapshot {28  url: string;29  title: string;30  lang?: string;31  h1?: string;32  landmarks: string[];33  candidates: DomCandidate[];34  video_elements: { src?: string; current_src?: string; duration?: number; width: number; height: number; playing: boolean; poster?: string }[];35  text_excerpt: string; // first ~600 chars of main text36  scroll: { y: number; height: number; viewport: number };37  has_login_form: boolean;38  dialog_open: boolean;39}4041const here = path.dirname(fileURLToPath(import.meta.url));42const SNAPSHOT_SOURCE = fs43  .readFileSync(path.join(here, "snapshot.page.js"), "utf8")44  .replace(/^\s*\/\/.*$/gm, "") // strip comment lines so the string is a bare function expression45  .trim();4647export async function snapshotDom(page: Page, opts: { maxCandidates?: number } = {}): Promise<DomSnapshot> {48  const max = opts.maxCandidates ?? 250;49  return (await page.evaluate(`(${SNAPSHOT_SOURCE})(${max})`)) as DomSnapshot;50}51