a424d29ccc
pipeline.ts: || undefined → ?? undefined (9 occurrences) - || converts valid subject ID 0 to undefined because 0 is falsy in JS. This broke clause witnesses and topology references for the first subject. domain-index.ts: remove .toLowerCase() on CSS selectors - CSS selectors are case-sensitive (IDs, class names, attribute values). Lowercasing on lookup but not on storage (selectorIndex) meant case- sensitive selectors never matched — returning empty arrays silently. canonical.ts: add warning when visualBoxes falls back to layout boxes - visualBoxes ?? boxes silently substituted layout coordinates for visual space, producing incorrect results for transform-dependent assertions. Now emits console.warn so silent data corruption is visible. extraction.ts: serialize materializeSemanticSelector calls (3 sites) - Changed Promise.all over page.evaluate() to sequential for..of. While Playwright serializes CDP calls internally, concurrent DOM-modifying evaluate() calls create undefined execution order. Sequential resolution eliminates theoretical race conditions for semantic selector injection. engine.ts: include stack trace in evaluator exception diagnostics - Catch-all converted ALL exceptions (including TypeError from programming bugs) to IMH_EVALUATOR_EXCEPTION with just err.message. Now includes stack trace and logs to console.warn for visibility. Distinguishes TypeError (programming bug) from other evaluation errors. 648 SDK tests + 57 E2E hard tests pass, zero regressions.
80 lines
2.6 KiB
TypeScript
80 lines
2.6 KiB
TypeScript
// Domain index query APIs for deterministic first-order logic enumeration (V1.1)
|
|
// All returned arrays are owned by the index — no copies on read.
|
|
|
|
import { GeometryWorld } from './world.js'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Selector queries
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Return all element subject IDs that match the given CSS selector string.
|
|
* Supported selector forms:
|
|
* - tag name: "div", "button", "span"
|
|
* - class: ".button", ".card"
|
|
* Returns a sorted array owned by the world selectorIndex.
|
|
* If the selector is not indexed, returns an empty array.
|
|
*/
|
|
export function getElementsBySelector(world: GeometryWorld, selector: string): number[] {
|
|
const normalized = selector.trim()
|
|
return world.selectorIndex.get(normalized) ?? []
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Ancestor / descendant queries
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Return all descendant element subject IDs of `parentId`.
|
|
* If `selector` is provided, filter to those matching the selector.
|
|
* Returns a sorted array owned by the world ancestorIndex (or a filtered copy).
|
|
*/
|
|
export function getDescendants(world: GeometryWorld, parentId: number, selector?: string): number[] {
|
|
const all = world.ancestorIndex.get(parentId) ?? []
|
|
if (!selector) {
|
|
return all
|
|
}
|
|
const matched = getElementsBySelector(world, selector)
|
|
if (matched.length === 0) {
|
|
return []
|
|
}
|
|
// Intersect two sorted arrays without allocating intermediates.
|
|
const out: number[] = []
|
|
let i = 0
|
|
let j = 0
|
|
while (i < all.length && j < matched.length) {
|
|
const a = all[i]
|
|
const b = matched[j]
|
|
if (a === b) {
|
|
out.push(a)
|
|
i++
|
|
j++
|
|
} else if (a < b) {
|
|
i++
|
|
} else {
|
|
j++
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Text geometry queries
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Return line box fragment IDs for the given text node subject ID.
|
|
* Returns a sorted array owned by the world lineBoxIndex.
|
|
*/
|
|
export function getLineBoxes(world: GeometryWorld, textNodeId: number): number[] {
|
|
return world.lineBoxIndex.get(textNodeId) ?? []
|
|
}
|
|
|
|
/**
|
|
* Return text run IDs for the given text node subject ID.
|
|
* Returns a sorted array owned by the world textRunIndex.
|
|
*/
|
|
export function getTextRuns(world: GeometryWorld, textNodeId: number): number[] {
|
|
return world.textRunIndex.get(textNodeId) ?? []
|
|
}
|