Files
reverse-engineering-anything/src/domain/javascript/javascriptSemanticMutationValues.ts
T
Matt Van Hornandmorluto 45e3edc17f feat(javascript): report observed export property presence (#1160)
* feat(javascript): report observed export property presence

Extend compare_javascript_export_shapes so each retained return variant
lists observed property names and each change reports presence independently
of value resolution. Complete parent coverage classifies one-sided names as
added or removed even when static values stay unknown.

Fixes #725

* test(javascript): match unknown export-shape values by availability

Nested availability objects include a reason when values stay unresolved.
Assert availability through objectContaining so presence-only added and
removed rows still verify unknown values.

* fix(javascript): preserve uncertain export property presence and locations

* fix(javascript): normalize presence and preserve local property coverage

* fix(javascript): require present discriminants for return pairing

* fix(javascript): normalize retained shapes before comparison

* refactor(javascript): share return projection with executable examples

* fix(javascript): localize uncertainty for missing mutated slots

* fix(javascript): bind advertised export examples to exact artifacts

* fix(javascript): keep uncertain property flow as candidates

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-09 18:30:31 +08:00

57 lines
1.8 KiB
TypeScript

import type { JavaScriptSemanticValue } from "./javascriptSemanticIr.js";
/** Invalidate only slots that an explicit mutation can affect. */
export const invalidateSemanticMutationPath = (
value: JavaScriptSemanticValue,
path: readonly (string | number | null)[],
): JavaScriptSemanticValue => {
const [key, ...remaining] = path;
const unknown: JavaScriptSemanticValue = {
status: "unknown",
reason: "This property may have been mutated.",
};
if (key === undefined || key === null) return unknown;
if (value.status === "object") {
const name = String(key);
const observed = value.properties.some(
(property) => property.name === name,
);
const properties = value.properties.map((property) =>
property.name === name
? {
...property,
...(remaining.length === 0
? { presence: "unknown-coverage" as const }
: {}),
value: invalidateSemanticMutationPath(property.value, remaining),
}
: property,
);
if (!observed)
properties.push({ name, value: unknown, presence: "unknown-coverage" });
return { ...value, properties };
}
if (value.status === "array") {
const index = typeof key === "number" ? key : Number(key);
if (
!Number.isSafeInteger(index) ||
index < 0 ||
String(index) !== String(key)
)
return unknown;
const items = value.items.map((item, position) =>
position === index
? invalidateSemanticMutationPath(item, remaining)
: item,
);
const itemPresence = {
...value.itemPresence,
...(remaining.length === 0 || value.items[index] === undefined
? { [index]: "unknown-coverage" as const }
: {}),
};
return { ...value, items, itemPresence };
}
return unknown;
};