mirror of
https://github.com/morluto/rea
synced 2026-10-27 22:45:19 -04:00
fix(javascript): retain partial indexed source map facts
Share section classification across declaration analysis and mapping parsing. Preserve external URLs, malformed section reasons, section paths and safe sibling declarations without fetching nested maps. Refs #5466. All 50 focused tests passed, including real CLI and stdio MCP regressions; typecheck and lint passed.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import { z } from "zod";
|
||||
import { WEB_SOURCE_MAP_LIMITS } from "../../domain/webSourceLocation.js";
|
||||
import {
|
||||
classifySourceMapSection,
|
||||
classifySourceMapSections,
|
||||
sourceMapJsonText,
|
||||
sourceMapMappingsSchema,
|
||||
} from "../../domain/sourceMapEnvelope.js";
|
||||
import type { SourceMapSection } from "../../domain/sourceMapEnvelope.js";
|
||||
import type { EncodedSourceMapXInput } from "@jridgewell/trace-mapping";
|
||||
|
||||
const leafSchema = z.object({
|
||||
@@ -28,13 +31,8 @@ const sectionSchema = z.object({
|
||||
column: z.number().int().min(0).max(Number.MAX_SAFE_INTEGER),
|
||||
}),
|
||||
map: z.unknown().optional(),
|
||||
url: z.string().optional(),
|
||||
url: z.unknown().optional(),
|
||||
});
|
||||
const indexedSchema = z.object({
|
||||
version: z.literal(3),
|
||||
sections: z.array(sectionSchema),
|
||||
});
|
||||
|
||||
type Offset = { readonly line: number; readonly column: number };
|
||||
interface PendingMap {
|
||||
readonly value: unknown;
|
||||
@@ -126,21 +124,26 @@ const consumeRecords = (budget: { records: number }, count: number): void => {
|
||||
budget.records += count;
|
||||
};
|
||||
|
||||
const addSections = (
|
||||
node: PendingMap,
|
||||
pending: PendingMap[],
|
||||
budget: { records: number },
|
||||
options: SourceMapStructureOptions,
|
||||
): void => {
|
||||
const addSections = ({
|
||||
node,
|
||||
pending,
|
||||
rawSections,
|
||||
budget,
|
||||
options,
|
||||
}: {
|
||||
readonly node: PendingMap;
|
||||
readonly pending: PendingMap[];
|
||||
readonly rawSections: readonly unknown[];
|
||||
readonly budget: { records: number };
|
||||
readonly options: SourceMapStructureOptions;
|
||||
}): void => {
|
||||
options.check?.();
|
||||
const header = mapHeaderSchema.parse(node.value);
|
||||
if (!Array.isArray(header.sections))
|
||||
throw new SourceMapFormatFailure(
|
||||
"format",
|
||||
"Indexed source-map sections must be an array.",
|
||||
);
|
||||
consumeRecords(budget, header.sections.length);
|
||||
const sections = indexedSchema.parse(node.value).sections;
|
||||
consumeRecords(budget, rawSections.length);
|
||||
const sections = z.array(sectionSchema).parse(rawSections);
|
||||
const embeddedMaps: Extract<
|
||||
SourceMapSection,
|
||||
{ readonly kind: "embedded" }
|
||||
>["map"][] = [];
|
||||
for (const [index, section] of sections.entries()) {
|
||||
if ((index & 255) === 0) options.check?.();
|
||||
const previous = sections[index - 1];
|
||||
@@ -149,20 +152,25 @@ const addSections = (
|
||||
"format",
|
||||
"Indexed section offsets must be strictly increasing.",
|
||||
);
|
||||
if (section.url !== undefined)
|
||||
const classification = classifySourceMapSection(section);
|
||||
if (classification.kind === "external")
|
||||
throw new SourceMapFormatFailure(
|
||||
"unsupported",
|
||||
`External source-map section URL is not fetched: ${section.url}`,
|
||||
);
|
||||
if (section.map === undefined)
|
||||
throw new SourceMapFormatFailure(
|
||||
"format",
|
||||
"Indexed section requires an embedded map.",
|
||||
`External source-map section URL is not fetched: ${classification.url}`,
|
||||
);
|
||||
if (classification.kind === "invalid")
|
||||
throw new SourceMapFormatFailure("format", classification.reason);
|
||||
embeddedMaps.push(classification.map);
|
||||
}
|
||||
for (let index = sections.length - 1; index >= 0; index -= 1) {
|
||||
const section = sections[index];
|
||||
if (section === undefined) continue;
|
||||
const map = embeddedMaps[index];
|
||||
if (map === undefined)
|
||||
throw new SourceMapFormatFailure(
|
||||
"format",
|
||||
"Validated indexed section map is unavailable.",
|
||||
);
|
||||
const next = sections[index + 1];
|
||||
const nextOffset =
|
||||
next === undefined ? null : addOffset(node.offset, next.offset);
|
||||
@@ -179,7 +187,7 @@ const addSections = (
|
||||
"Nested source-map section overlaps its enclosing boundary.",
|
||||
);
|
||||
pending.push({
|
||||
value: section.map,
|
||||
value: map,
|
||||
path: [...node.path, index],
|
||||
offset,
|
||||
stop,
|
||||
@@ -319,8 +327,17 @@ export const inspectSourceMapValue = (
|
||||
"limit",
|
||||
"Indexed source-map nesting exceeds the 64-level decoder stack budget.",
|
||||
);
|
||||
if (mapHeaderSchema.parse(node.value).sections !== undefined) {
|
||||
addSections(node, pending, budget, options);
|
||||
const sections = classifySourceMapSections(node.value);
|
||||
if (sections.kind === "invalid")
|
||||
throw new SourceMapFormatFailure("format", sections.reason);
|
||||
if (sections.kind === "indexed") {
|
||||
addSections({
|
||||
node,
|
||||
pending,
|
||||
rawSections: sections.sections,
|
||||
budget,
|
||||
options,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
const leaf = inspectLeaf(node, budget, options);
|
||||
|
||||
Reference in New Issue
Block a user