refactor: remove world smuggling — type styles, fragments, topologySubjectIds
registry.ts: Add optional styles and fragments table types to the
solver's GeometryWorld. Previously these were attached via
(world as any).styles / .fragments, bypassing the type system.
extraction.ts:
- remapTopologyIds: accept topologySubjectIds as explicit parameter
instead of reading (world as any)._topologySubjectIds and deleting
it post-remap. Eliminates 3 smuggling accesses (read, set, delete)
- All worldAny.styles / worldAny.fragments / worldAny.strings /
worldAny.transforms / worldAny.matrices assignments now use
typed world.xxx access. worldAny variable removed entirely.
- attachMeasuredChWidths: use world.styles directly.
- (result.world as any).env mutation replaced with typed
result.world.env assignment.
- (sizeWorld as any).styles replaced with typed access.
predicates.ts: Replace all 3 (world as any).styles inline type-assert
reads (getSubjectFontSizePx, getRootFontSizePx, getSubjectChWidthPx)
with direct world.styles?.xxx access. No runtime behavior change.
Zero (world as any) casts remain in extraction.ts or predicates.ts.
658 tests pass.
This commit is contained in:
@@ -342,12 +342,7 @@ export function attachMeasuredChWidths(
|
||||
selectorToIds: Map<string, number[]>,
|
||||
chWidthsBySelector: Map<string, number[]>,
|
||||
): void {
|
||||
const worldAny = world as any
|
||||
const styles = worldAny.styles as {
|
||||
subjectId?: number[]
|
||||
fontSize?: number[]
|
||||
chWidth?: number[]
|
||||
} | undefined
|
||||
const styles = world.styles
|
||||
if (!styles) return
|
||||
const bySubject = new Map<number, number>()
|
||||
for (const [key, ids] of selectorToIds) {
|
||||
@@ -543,7 +538,6 @@ export async function extractWorldFastGeometry(
|
||||
}) as FastExtractedPayload
|
||||
|
||||
const world = buildGeometryWorld(extracted.elements) as GeometryWorld
|
||||
const worldAny = world as any
|
||||
|
||||
if (requiredFacts?.fragments) {
|
||||
const fragmentId: number[] = []
|
||||
@@ -581,20 +575,15 @@ export async function extractWorldFastGeometry(
|
||||
}
|
||||
}
|
||||
|
||||
worldAny.fragments = {
|
||||
fragmentId,
|
||||
world.fragments = {
|
||||
subjectId,
|
||||
boxId: fragmentId,
|
||||
fragmentKind,
|
||||
boxLeft,
|
||||
boxTop,
|
||||
boxRight,
|
||||
boxBottom,
|
||||
lineIndex,
|
||||
flowIndex,
|
||||
parentFragmentId,
|
||||
firstTextRunId: [],
|
||||
textRunCount: [],
|
||||
}
|
||||
worldAny.subjects.firstFragmentId = firstFragmentIds
|
||||
worldAny.subjects.fragmentCount = fragmentCounts
|
||||
world.subjects.firstFragmentId = firstFragmentIds
|
||||
world.subjects.fragmentCount = fragmentCounts
|
||||
}
|
||||
|
||||
if (requiredFacts?.geometry) {
|
||||
@@ -619,7 +608,7 @@ export async function extractWorldFastGeometry(
|
||||
originY.push(t.originY)
|
||||
}
|
||||
|
||||
worldAny.transforms = {
|
||||
world.transforms = {
|
||||
transformId,
|
||||
subjectId: transformSubjectId,
|
||||
matrixStart,
|
||||
@@ -627,11 +616,11 @@ export async function extractWorldFastGeometry(
|
||||
originX,
|
||||
originY,
|
||||
}
|
||||
worldAny.matrices = { values: matrices }
|
||||
world.matrices = { values: matrices }
|
||||
}
|
||||
|
||||
if (requiredFacts?.styles) {
|
||||
const strings = worldAny.strings?.values ?? []
|
||||
const strings = world.strings?.values ?? []
|
||||
const stringToId = new Map<string, number>()
|
||||
for (let i = 0; i < strings.length; i++) stringToId.set(strings[i], i)
|
||||
const intern = (value: string): number => {
|
||||
@@ -696,8 +685,8 @@ export async function extractWorldFastGeometry(
|
||||
chWidth.push(s.chWidth)
|
||||
}
|
||||
|
||||
worldAny.strings = { values: strings }
|
||||
worldAny.styles = {
|
||||
world.strings = { values: strings }
|
||||
world.styles = {
|
||||
subjectId: styleSubjectId,
|
||||
display,
|
||||
position,
|
||||
@@ -757,7 +746,7 @@ export async function extractWorldFastGeometry(
|
||||
// CDP Extraction
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function remapTopologyIds(world: GeometryWorld): void {
|
||||
function remapTopologyIds(world: GeometryWorld, topologySubjectIds?: number[]): void {
|
||||
const nSubjects = world.subjects.ids.length
|
||||
if (nSubjects === 0) return
|
||||
|
||||
@@ -766,7 +755,7 @@ function remapTopologyIds(world: GeometryWorld): void {
|
||||
backendToSolver.set(world.subjects.domNodeId[i], world.subjects.ids[i])
|
||||
}
|
||||
|
||||
const rawSubjectIds = (world as any)._topologySubjectIds as number[] | undefined
|
||||
const rawSubjectIds = topologySubjectIds
|
||||
|
||||
function reorderAndRemap(rawValues: ArrayLike<number>, targetLength: number): number[] {
|
||||
if (!rawSubjectIds || rawSubjectIds.length === 0) {
|
||||
@@ -810,8 +799,6 @@ function remapTopologyIds(world: GeometryWorld): void {
|
||||
if (world.scroll) {
|
||||
world.scroll.containerId = remapSimple(world.scroll.containerId)
|
||||
}
|
||||
|
||||
delete (world as any)._topologySubjectIds
|
||||
}
|
||||
|
||||
export async function extractWorldCdp(
|
||||
@@ -901,7 +888,8 @@ export async function extractWorldCdp(
|
||||
const snapshot = cdpResponse.snapshots[0]
|
||||
const canonical = adaptSnapshotToCanonical(snapshot)
|
||||
const world = adaptCanonicalWorldToSolver(canonical as any) as GeometryWorld
|
||||
;(world as any).styles = {
|
||||
const topologySubjectIds = (canonical as any).topology?.subjectIds as number[] | undefined
|
||||
world.styles = {
|
||||
subjectId: Array.from(canonical.styles.subjectId),
|
||||
lineHeight: Array.from(canonical.styles.lineHeight),
|
||||
fontFamilyStringId: Array.from(canonical.styles.fontFamilyStringId),
|
||||
@@ -909,9 +897,7 @@ export async function extractWorldCdp(
|
||||
fontWeight: Array.from(canonical.styles.fontWeight),
|
||||
}
|
||||
|
||||
;(world as any)._topologySubjectIds = (canonical as any).topology?.subjectIds
|
||||
|
||||
remapTopologyIds(world)
|
||||
remapTopologyIds(world, topologySubjectIds)
|
||||
|
||||
const selectorToIds = new Map<string, number[]>()
|
||||
for (const [selectorKey, nodeIds] of selectorToNodeIds) {
|
||||
@@ -1010,10 +996,10 @@ export async function extractWorld(
|
||||
? await extractWorldFastGeometry(playwrightPage, filteredSelectors, requiredFacts)
|
||||
: await extractWorldCdp(playwrightPage, filteredSelectors, requiredFacts)
|
||||
|
||||
;(result.world as any).env = {
|
||||
...(result.world as any).env,
|
||||
viewportWidth: env.viewportWidth,
|
||||
viewportHeight: env.viewportHeight,
|
||||
result.world.env = {
|
||||
...result.world.env,
|
||||
viewportWidth: env.viewportWidth as number,
|
||||
viewportHeight: env.viewportHeight as number,
|
||||
}
|
||||
|
||||
if (requiredFacts?.styles) {
|
||||
@@ -1959,7 +1945,7 @@ export function buildCompatibilityReport(ui: ImhotepUi): CompatibilityReport {
|
||||
viewportWidth: 1280,
|
||||
viewportHeight: 720,
|
||||
}
|
||||
;(sizeWorld as any).styles = {
|
||||
sizeWorld.styles = {
|
||||
subjectId: [1],
|
||||
fontSize: [16],
|
||||
chWidth: [9],
|
||||
|
||||
@@ -314,8 +314,7 @@ function parseLengthOption(raw: unknown): { value: number; unit: string } | null
|
||||
}
|
||||
|
||||
function getSubjectFontSizePx(world: GeometryWorld, subjectId: number): number {
|
||||
const styles = (world as any).styles as { fontSize?: ArrayLike<number> } | undefined;
|
||||
const fontSize = styles?.fontSize;
|
||||
const fontSize = world.styles?.fontSize;
|
||||
if (!fontSize) return 16;
|
||||
const ids = world.subjects?.ids;
|
||||
if (!ids) return 16;
|
||||
@@ -326,28 +325,23 @@ function getSubjectFontSizePx(world: GeometryWorld, subjectId: number): number {
|
||||
}
|
||||
|
||||
function getRootFontSizePx(world: GeometryWorld): number {
|
||||
const styles = (world as any).styles as { fontSize?: ArrayLike<number> } | undefined;
|
||||
const fontSize = styles?.fontSize;
|
||||
const fontSize = world.styles?.fontSize;
|
||||
if (!fontSize || fontSize.length === 0) return 16;
|
||||
const fs = Number(fontSize[0] ?? 16);
|
||||
return Number.isFinite(fs) && fs > 0 ? fs : 16;
|
||||
}
|
||||
|
||||
function getSubjectChWidthPx(world: GeometryWorld, subjectId: number): number {
|
||||
const styles = (world as any).styles as {
|
||||
subjectId?: ArrayLike<number>
|
||||
chWidth?: ArrayLike<number>
|
||||
} | undefined;
|
||||
const chWidth = styles?.chWidth;
|
||||
if (!chWidth || chWidth.length === 0) {
|
||||
const subjectIdArr = world.styles?.subjectId;
|
||||
const chWidthArr = world.styles?.chWidth;
|
||||
if (!chWidthArr || chWidthArr.length === 0) {
|
||||
return getSubjectFontSizePx(world, subjectId) * 0.5;
|
||||
}
|
||||
|
||||
const styleSubjectIds = styles?.subjectId;
|
||||
if (styleSubjectIds && styleSubjectIds.length > 0) {
|
||||
for (let i = 0; i < styleSubjectIds.length; i++) {
|
||||
if (Number(styleSubjectIds[i]) !== subjectId) continue;
|
||||
const w = Number(chWidth[i]);
|
||||
if (subjectIdArr && subjectIdArr.length > 0) {
|
||||
for (let i = 0; i < subjectIdArr.length; i++) {
|
||||
if (Number(subjectIdArr[i]) !== subjectId) continue;
|
||||
const w = Number(chWidthArr[i]);
|
||||
if (Number.isFinite(w) && w > 0) return w;
|
||||
break;
|
||||
}
|
||||
@@ -357,7 +351,7 @@ function getSubjectChWidthPx(world: GeometryWorld, subjectId: number): number {
|
||||
if (ids) {
|
||||
const idx = ids.indexOf(subjectId);
|
||||
if (idx >= 0) {
|
||||
const w = Number(chWidth[idx]);
|
||||
const w = Number(chWidthArr[idx]);
|
||||
if (Number.isFinite(w) && w > 0) return w;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,6 +139,33 @@ export interface GeometryWorld {
|
||||
visibleArea: number[];
|
||||
clippedArea: number[];
|
||||
};
|
||||
/** Computed style data attached by extraction (optional, populated when requiredFacts.styles=true). */
|
||||
styles?: {
|
||||
subjectId: number[];
|
||||
display?: number[];
|
||||
position?: number[];
|
||||
zIndexKind?: number[];
|
||||
zIndexValue?: number[];
|
||||
overflowX?: number[];
|
||||
overflowY?: number[];
|
||||
opacity?: number[];
|
||||
visibility?: number[];
|
||||
containFlags?: number[];
|
||||
pointerEvents?: number[];
|
||||
lineHeight?: number[];
|
||||
fontFamilyStringId?: number[];
|
||||
fontSize?: number[];
|
||||
fontWeight?: number[];
|
||||
chWidth?: number[];
|
||||
};
|
||||
/** Fragment data attached by extraction (optional, populated when requiredFacts.fragments=true). */
|
||||
fragments?: {
|
||||
subjectId: number[];
|
||||
boxId: number[];
|
||||
fragmentKind: number[];
|
||||
firstTextRunId: number[];
|
||||
textRunCount: number[];
|
||||
};
|
||||
}
|
||||
|
||||
// --- Clause descriptor -------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user