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:
John Dvorak
2026-05-22 14:38:37 -07:00
parent 45b5575e53
commit e78ffe3419
3 changed files with 59 additions and 52 deletions
+10 -16
View File
@@ -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;
}
}
+27
View File
@@ -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 -------------------------------------------------------