mirror of
https://github.com/morluto/rea
synced 2026-10-27 14:42:18 -04:00
1639189f26
A url-prefix runtime mapping decoded the whole pathname suffix at once, so `child%2Fpart.js` became the nested artifact path `child/part.js` and produced a medium-confidence location match and an inferred observed_as edge. Decode each URL path segment separately at the mapping boundary. A segment that decodes to contain a slash, like a malformed or non-UTF-8 escape, has no artifact path, so the runtime script stays unknown with no-authorized-location-mapping. Encoded spaces, Unicode, %25 and the literal %5C backslash name keep matching. File-root and URL-prefix mappings now share one tri-state translation (outside, untranslatable, translated) for matching and load-state scope. Static nodes beneath a prefix whose runtime script or worker could not be translated stay unknown instead of becoming not-observed-in-capture. Fixes #5878 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
182 lines
6.1 KiB
TypeScript
182 lines
6.1 KiB
TypeScript
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
|
|
import { uniqueSorted } from "../canonicalOrdering.js";
|
|
import type { ParsedStaticLayer } from "./javascriptRuntimeReconciliationParsing.js";
|
|
import type {
|
|
JavaScriptRuntimeReconciliationItem,
|
|
JavaScriptStaticLoadState,
|
|
} from "./javascriptRuntimeReconciliationSchemas.js";
|
|
import type { RuntimeReconciliationEntity } from "./javascriptRuntimeReconciliationRuntime.js";
|
|
import {
|
|
createStaticRuntimeScope,
|
|
mappedRuntimePaths,
|
|
staticNodeRuntimeScope,
|
|
} from "./javascriptRuntimeStaticCandidates.js";
|
|
|
|
export interface StaticLoadStateProjection {
|
|
readonly states: readonly JavaScriptStaticLoadState[];
|
|
readonly omittedStates: number;
|
|
}
|
|
|
|
/** Classify static code presence without claiming bundled module execution. */
|
|
export const classifyStaticLoadStates = (
|
|
layers: readonly ParsedStaticLayer[],
|
|
runtimeEntities: readonly RuntimeReconciliationEntity[],
|
|
matches: readonly JavaScriptRuntimeReconciliationItem[],
|
|
options: {
|
|
readonly reconciliationComplete: boolean;
|
|
},
|
|
): StaticLoadStateProjection => {
|
|
const states: JavaScriptStaticLoadState[] = [];
|
|
for (const layer of layers) {
|
|
const projection = classifyLayer(layer, runtimeEntities, matches, {
|
|
reconciliationComplete: options.reconciliationComplete,
|
|
});
|
|
states.push(...projection.states);
|
|
}
|
|
return { states, omittedStates: 0 };
|
|
};
|
|
|
|
const classifyLayer = (
|
|
layer: ParsedStaticLayer,
|
|
runtimeEntities: readonly RuntimeReconciliationEntity[],
|
|
matches: readonly JavaScriptRuntimeReconciliationItem[],
|
|
options: {
|
|
readonly reconciliationComplete: boolean;
|
|
},
|
|
): { readonly states: JavaScriptStaticLoadState[] } => {
|
|
const nodes = layer.graph.nodes.filter(isLoadStateNode);
|
|
const direct = new Map<string, string[]>();
|
|
const ambiguous = new Set<string>();
|
|
const incomplete = new Set<string>();
|
|
for (const match of matches)
|
|
if (
|
|
match.status === "matched" &&
|
|
match.static_layer_id === layer.layerId &&
|
|
match.static_node_id !== null
|
|
)
|
|
append(direct, match.static_node_id, match.runtime_node_id);
|
|
else if (match.status === "ambiguous" || match.status === "unknown") {
|
|
const uncertain = match.status === "ambiguous" ? ambiguous : incomplete;
|
|
for (const candidate of match.candidate_static_nodes)
|
|
if (candidate.static_layer_id === layer.layerId)
|
|
uncertain.add(candidate.static_node_id);
|
|
}
|
|
const resident = residentNodes(layer, new Set(direct.keys()));
|
|
const runtimeScope = createStaticRuntimeScope(layer, runtimeEntities);
|
|
const scopedEntities = runtimeEntities.filter(
|
|
(entity) => mappedRuntimePaths(layer, entity).length > 0,
|
|
);
|
|
const layerComplete =
|
|
options.reconciliationComplete &&
|
|
layer.graph.coverage.status === "complete" &&
|
|
scopedEntities.length > 0 &&
|
|
scopedEntities.every(({ capture }) => capture.scriptsCompleteWithinScope);
|
|
const states = nodes.map((node) => {
|
|
const runtimeNodeIds = uniqueSorted(direct.get(node.node_id) ?? []);
|
|
if (runtimeNodeIds.length > 0)
|
|
return state(layer, node, {
|
|
status: "loaded",
|
|
runtimeNodeIds,
|
|
reason: "runtime-script-correspondence",
|
|
});
|
|
if (resident.has(node.node_id))
|
|
return state(layer, node, {
|
|
status: "resident-in-loaded-asset",
|
|
runtimeNodeIds: [],
|
|
reason: "containing-asset-was-loaded",
|
|
});
|
|
if (incomplete.has(node.node_id) || ambiguous.has(node.node_id))
|
|
return state(layer, node, {
|
|
status: "unknown",
|
|
runtimeNodeIds: [],
|
|
reason: incomplete.has(node.node_id)
|
|
? "static-or-runtime-coverage-incomplete"
|
|
: "ambiguous-runtime-candidate",
|
|
});
|
|
const membership = staticNodeRuntimeScope(node, runtimeScope);
|
|
if (layerComplete && membership === "within")
|
|
return state(layer, node, {
|
|
status: "not-observed-in-capture",
|
|
runtimeNodeIds: [],
|
|
reason: "not-observed-in-bounded-capture",
|
|
});
|
|
return state(layer, node, {
|
|
status: "unknown",
|
|
runtimeNodeIds: [],
|
|
reason:
|
|
membership === "outside"
|
|
? "layer-outside-runtime-scope"
|
|
: "static-or-runtime-coverage-incomplete",
|
|
});
|
|
});
|
|
return { states };
|
|
};
|
|
|
|
const residentNodes = (
|
|
layer: ParsedStaticLayer,
|
|
directlyLoaded: ReadonlySet<string>,
|
|
): ReadonlySet<string> => {
|
|
const nodeKinds = new Map(
|
|
layer.graph.nodes.map(({ node_id: id, kind }) => [id, kind]),
|
|
);
|
|
const children = new Map<string, string[]>();
|
|
for (const edge of layer.graph.edges)
|
|
if (edge.relation === "contains")
|
|
append(children, edge.source_node_id, edge.target_node_id);
|
|
const pending = [...directlyLoaded].filter(
|
|
(nodeId) => nodeKinds.get(nodeId) === "javascript-asset",
|
|
);
|
|
const resident = new Set<string>();
|
|
for (let index = 0; index < pending.length; index += 1) {
|
|
const current = pending[index];
|
|
if (current === undefined) break;
|
|
for (const child of children.get(current) ?? []) {
|
|
if (resident.has(child)) continue;
|
|
resident.add(child);
|
|
pending.push(child);
|
|
}
|
|
}
|
|
return resident;
|
|
};
|
|
|
|
const append = (
|
|
values: Map<string, string[]>,
|
|
key: string,
|
|
value: string,
|
|
): void => {
|
|
const current = values.get(key);
|
|
if (current === undefined) values.set(key, [value]);
|
|
else current.push(value);
|
|
};
|
|
|
|
const isLoadStateNode = (
|
|
node: ApplicationNode,
|
|
): node is ApplicationNode & {
|
|
readonly kind: "javascript-asset" | "javascript-chunk" | "javascript-module";
|
|
} =>
|
|
node.kind === "javascript-asset" ||
|
|
node.kind === "javascript-chunk" ||
|
|
node.kind === "javascript-module";
|
|
|
|
const state = (
|
|
layer: ParsedStaticLayer,
|
|
node: ApplicationNode & {
|
|
readonly kind:
|
|
| "javascript-asset"
|
|
| "javascript-chunk"
|
|
| "javascript-module";
|
|
},
|
|
input: {
|
|
readonly status: JavaScriptStaticLoadState["status"];
|
|
readonly runtimeNodeIds: readonly string[];
|
|
readonly reason: JavaScriptStaticLoadState["reason"];
|
|
},
|
|
): JavaScriptStaticLoadState => ({
|
|
static_layer_id: layer.layerId,
|
|
static_node_id: node.node_id,
|
|
kind: node.kind,
|
|
status: input.status,
|
|
runtime_node_ids: [...input.runtimeNodeIds],
|
|
reason: input.reason,
|
|
});
|