Files
rea/src/domain/sourceMapEnvelope.ts
T
morluto ee50ed121e fix(sourcemaps)!: preserve nullable source declarations
Accept absent map-level sourcesContent and preserve null source-name slots alongside known originals and aligned content. Represent unnamed local declarations explicitly, retain leaf-local and flattened indices, and preserve declared indexed-section order across consumers.

Account for added indices before browser output allocation. Accept legacy retained rows without indices. Consolidate public CLI/MCP source-map fixtures and update the stale named-source-only assertion.

BREAKING CHANGE: Browser source-map original and mapping rows expose null for unavailable source names instead of inventing an unknown-source string. New index fields are optional when reading older retained evidence.

Refs #2023, #2024
2026-10-12 11:10:49 +08:00

49 lines
1.9 KiB
TypeScript

/** Artifact source declarations do not require generated-position decoding. */
type SourceMapRecord = Readonly<Record<string, unknown>>;
/**
* Return the JSON parsing view of retained source-map text. ECMA-426 permits
* HTTP responses to start with `)]}'`; the first line is transport framing.
* Keep the original text for artifact identity and Evidence provenance.
* https://tc39.es/ecma426/#sec-fetchsourcemap
*/
export const sourceMapJsonText = (text: string): string => {
const withoutBom = text.replace(/^\uFEFF/u, "");
if (!withoutBom.startsWith(")]}'")) return withoutBom;
const newline = withoutBom.search(/[\r\n]/u);
return newline === -1 ? "" : withoutBom.slice(newline);
};
const isRecord = (value: unknown): value is SourceMapRecord =>
typeof value === "object" && value !== null && !Array.isArray(value);
/** Whether a parsed value has the version-3 map shape. */
const isVersion3Map = (value: unknown): value is SourceMapRecord =>
isRecord(value) && value.version === 3;
/**
* Collect leaf maps from an indexed source-map envelope, or undefined when
* the envelope is not a version-3 map. A map carrying neither `mappings`
* nor `sections` yields itself as the single leaf; callers decide whether
* that leaf is valid content.
*/
export const flattenSourceMapLeaves = (
root: unknown,
): readonly SourceMapRecord[] | undefined => {
if (!isVersion3Map(root)) return undefined;
const leaves: SourceMapRecord[] = [];
const pending: unknown[] = [root];
while (pending.length > 0) {
const map = pending.pop();
if (!isVersion3Map(map)) return undefined;
if (Array.isArray(map.sections)) {
for (let index = map.sections.length - 1; index >= 0; index -= 1) {
const section = map.sections[index];
if (!isRecord(section) || !isRecord(section.map)) return undefined;
pending.push(section.map);
}
} else leaves.push(map);
}
return leaves;
};