mirror of
https://github.com/morluto/rea
synced 2026-10-26 06:16:17 -04:00
f4322b15ed
Admit unknowns before canonical construction and provenance interning. Offer independent per-file and application capacities through CLI, Evidence and the owned analysis worker, and reject over-allocation during streamed decoding. Report examined omitted emission occurrences by family and reason without retaining discarded identities. Preserve omission-free graph identities and distinguish node truncation from overall partial coverage. Fixes #4797
337 lines
11 KiB
TypeScript
337 lines
11 KiB
TypeScript
import type { JavaScriptSemanticGraphNode } from "../../domain/javascript/javascriptSemanticGraphSchemas.js";
|
|
import type {
|
|
JavaScriptSemanticBoundaryOperation,
|
|
JavaScriptSemanticConfigurationOperation,
|
|
JavaScriptSemanticRequestOperation,
|
|
} from "../../domain/javascript/javascriptSemanticIr.js";
|
|
import {
|
|
retainSemanticGraphNode,
|
|
addSemanticGraphRelation,
|
|
addSemanticGraphUnknown,
|
|
} from "./JavaScriptSemanticGraphConstruction.js";
|
|
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
|
import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js";
|
|
import { semanticResourceLimitReason } from "../../domain/javascript/javascriptSemanticResourceLimits.js";
|
|
|
|
/** Project environment, argv, file, default, and precedence candidates. */
|
|
export const projectSemanticConfiguration = (
|
|
context: SemanticFlowProjectionContext,
|
|
): void => {
|
|
const configNodes = new Map(
|
|
context.ir.configurationOperations.flatMap((operation) => {
|
|
const node = addConfigurationNode(context, operation);
|
|
return node === null ? [] : [[operation.configId, node] as const];
|
|
}),
|
|
);
|
|
for (const operation of context.ir.configurationOperations) {
|
|
const config = configNodes.get(operation.configId);
|
|
if (config === undefined) continue;
|
|
const target = bindingOrOwner(
|
|
context,
|
|
operation.resultBindingId,
|
|
operation.ownerCallableId,
|
|
);
|
|
addSemanticGraphRelation(context.state, {
|
|
source: config,
|
|
target,
|
|
relation: configurationRelation(operation),
|
|
resolution:
|
|
operation.resolution === "complete" ? "resolved" : "candidate",
|
|
});
|
|
if (operation.kind === "default" && operation.sourceConfigId !== null)
|
|
addSemanticGraphRelation(context.state, {
|
|
source: configNodes.get(operation.sourceConfigId),
|
|
target: config,
|
|
relation: "overrides",
|
|
resolution:
|
|
operation.resolution === "complete" ? "resolved" : "candidate",
|
|
});
|
|
if (operation.resolution !== "complete")
|
|
addDataUnknown({
|
|
context,
|
|
node: config,
|
|
family: "configuration",
|
|
relationKinds: [
|
|
configurationRelation(operation),
|
|
...(operation.kind === "default" ? (["overrides"] as const) : []),
|
|
],
|
|
location: operation.location,
|
|
...(operation.valueResourceLimit === undefined
|
|
? {
|
|
detail:
|
|
operation.kind === "default"
|
|
? "Static default configuration value is partial."
|
|
: `Static ${operation.kind} configuration identity is partial.`,
|
|
}
|
|
: {
|
|
reason: "resource-limit",
|
|
detail: semanticResourceLimitReason(operation.valueResourceLimit),
|
|
}),
|
|
candidateNodeIds: [],
|
|
});
|
|
}
|
|
};
|
|
|
|
const addConfigurationNode = (
|
|
context: SemanticFlowProjectionContext,
|
|
operation: JavaScriptSemanticConfigurationOperation,
|
|
): JavaScriptSemanticGraphNode | null =>
|
|
retainSemanticGraphNode(context.state, context.file, {
|
|
kind: "config-source",
|
|
roleKey: operation.configId,
|
|
location: operation.location,
|
|
label: operation.key ?? operation.kind,
|
|
functionNodeId:
|
|
operation.ownerCallableId === null
|
|
? null
|
|
: (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
|
|
null),
|
|
properties: {
|
|
key: operation.key,
|
|
source_kind: operation.kind,
|
|
value: operation.value,
|
|
...(operation.valueResourceLimit === undefined
|
|
? {}
|
|
: { value_resource_limit: operation.valueResourceLimit }),
|
|
},
|
|
});
|
|
|
|
const configurationRelation = (
|
|
operation: JavaScriptSemanticConfigurationOperation,
|
|
): "reads-environment" | "reads-argv" | "reads-config" | "defaults" =>
|
|
operation.kind === "environment"
|
|
? "reads-environment"
|
|
: operation.kind === "argv"
|
|
? "reads-argv"
|
|
: operation.kind === "file"
|
|
? "reads-config"
|
|
: "defaults";
|
|
|
|
/** Project request construction, field provenance, and response consumers. */
|
|
export const projectSemanticRequests = (
|
|
context: SemanticFlowProjectionContext,
|
|
): void => {
|
|
const requestNodes = new Map(
|
|
context.ir.requestOperations.flatMap((operation) => {
|
|
const node = addRequestNode(context, operation);
|
|
return node === null ? [] : [[operation.requestId, node] as const];
|
|
}),
|
|
);
|
|
for (const operation of context.ir.requestOperations) {
|
|
const request = requestNodes.get(operation.requestId);
|
|
if (request === undefined) continue;
|
|
if (operation.kind === "request")
|
|
projectRequestConstruction(context, operation, request);
|
|
else projectResponseConsumer(context, operation, request);
|
|
if (operation.resolution !== "complete")
|
|
addDataUnknown({
|
|
context,
|
|
node: request,
|
|
family: "request",
|
|
relationKinds:
|
|
operation.kind === "request"
|
|
? ["constructs-request", "supplies-request-field"]
|
|
: ["consumed-by"],
|
|
location: operation.location,
|
|
detail: `Static ${operation.method} request resolution was ${operation.resolution}.`,
|
|
candidateNodeIds: operation.linkedRequestIds.flatMap((identifier) => {
|
|
const candidate = requestNodes.get(identifier);
|
|
return candidate === undefined ? [] : [candidate.node_id];
|
|
}),
|
|
});
|
|
}
|
|
};
|
|
|
|
const addRequestNode = (
|
|
context: SemanticFlowProjectionContext,
|
|
operation: JavaScriptSemanticRequestOperation,
|
|
): JavaScriptSemanticGraphNode | null =>
|
|
retainSemanticGraphNode(context.state, context.file, {
|
|
kind: operation.kind === "request" ? "request" : "response",
|
|
roleKey: operation.requestId,
|
|
location: operation.location,
|
|
label:
|
|
operation.endpoint !== null &&
|
|
operation.endpoint.length <= operation.method.length
|
|
? operation.endpoint
|
|
: operation.method,
|
|
functionNodeId:
|
|
operation.ownerCallableId === null
|
|
? null
|
|
: (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
|
|
null),
|
|
properties: {
|
|
endpoint: operation.endpoint,
|
|
method: operation.method,
|
|
operation_kind: operation.kind,
|
|
},
|
|
});
|
|
|
|
const projectRequestConstruction = (
|
|
context: SemanticFlowProjectionContext,
|
|
operation: JavaScriptSemanticRequestOperation,
|
|
request: JavaScriptSemanticGraphNode,
|
|
): void => {
|
|
const callSite = context.callSiteAt(operation.location);
|
|
addSemanticGraphRelation(context.state, {
|
|
source: callSite,
|
|
target: request,
|
|
relation: "constructs-request",
|
|
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
});
|
|
for (const projected of operation.projectedResultBindings)
|
|
addSemanticGraphRelation(context.state, {
|
|
source: request,
|
|
target: context.bindingNodes.get(projected.bindingId),
|
|
relation: "destructures",
|
|
resolution:
|
|
projected.resolution === "complete" ? "resolved" : "candidate",
|
|
properties: {
|
|
projection_path: [...projected.projectionPath],
|
|
projection_resolution: projected.resolution,
|
|
},
|
|
});
|
|
for (const field of operation.fields)
|
|
addSemanticGraphRelation(context.state, {
|
|
source:
|
|
field.sourceBindingId === null
|
|
? callSite
|
|
: context.bindingNodes.get(field.sourceBindingId),
|
|
target: request,
|
|
relation: "supplies-request-field",
|
|
resolution: field.sourceBindingId === null ? "candidate" : "resolved",
|
|
properties: { field: field.name },
|
|
});
|
|
};
|
|
|
|
const projectResponseConsumer = (
|
|
context: SemanticFlowProjectionContext,
|
|
operation: JavaScriptSemanticRequestOperation,
|
|
response: JavaScriptSemanticGraphNode,
|
|
): void => {
|
|
addSemanticGraphRelation(context.state, {
|
|
source: response,
|
|
target: context.callSiteAt(operation.location),
|
|
relation: "consumed-by",
|
|
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
properties: { method: operation.method },
|
|
});
|
|
};
|
|
|
|
/** Project parse, coercion, and validation consumers as static boundaries. */
|
|
export const projectSemanticBoundaries = (
|
|
context: SemanticFlowProjectionContext,
|
|
): void => {
|
|
for (const operation of context.ir.boundaryOperations) {
|
|
const boundary = addBoundaryNode(context, operation);
|
|
if (boundary === null) continue;
|
|
addSemanticGraphRelation(context.state, {
|
|
source: bindingOrOwner(
|
|
context,
|
|
operation.sourceBindingId,
|
|
operation.ownerCallableId,
|
|
),
|
|
target: boundary,
|
|
relation:
|
|
operation.kind === "parse"
|
|
? "parses"
|
|
: operation.kind === "coerce"
|
|
? "coerces"
|
|
: "validates",
|
|
resolution:
|
|
operation.resolution === "complete" ? "resolved" : "candidate",
|
|
});
|
|
if (operation.resolution !== "complete")
|
|
addDataUnknown({
|
|
context,
|
|
node: boundary,
|
|
family: "boundary",
|
|
relationKinds: [
|
|
operation.kind === "parse"
|
|
? "parses"
|
|
: operation.kind === "coerce"
|
|
? "coerces"
|
|
: "validates",
|
|
],
|
|
location: operation.location,
|
|
detail: `Static ${operation.method} boundary kind is a candidate.`,
|
|
candidateNodeIds: [],
|
|
});
|
|
}
|
|
};
|
|
|
|
const addBoundaryNode = (
|
|
context: SemanticFlowProjectionContext,
|
|
operation: JavaScriptSemanticBoundaryOperation,
|
|
): JavaScriptSemanticGraphNode | null =>
|
|
retainSemanticGraphNode(context.state, context.file, {
|
|
kind: "boundary",
|
|
roleKey: operation.boundaryId,
|
|
location: operation.location,
|
|
label: operation.method,
|
|
functionNodeId:
|
|
operation.ownerCallableId === null
|
|
? null
|
|
: (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
|
|
null),
|
|
properties: {
|
|
boundary_kind: operation.kind,
|
|
field: operation.method,
|
|
method: operation.method,
|
|
},
|
|
});
|
|
|
|
const bindingOrOwner = (
|
|
context: SemanticFlowProjectionContext,
|
|
bindingId: string | null,
|
|
callableId: string | null,
|
|
): JavaScriptSemanticGraphNode =>
|
|
(bindingId === null ? undefined : context.bindingNodes.get(bindingId)) ??
|
|
(callableId === null
|
|
? context.moduleNode
|
|
: (context.callableNodes.get(callableId) ?? context.moduleNode));
|
|
|
|
interface DataUnknownInput {
|
|
readonly reason?: "ambiguous-target" | "resource-limit";
|
|
readonly context: SemanticFlowProjectionContext;
|
|
readonly node: JavaScriptSemanticGraphNode;
|
|
readonly family: "boundary" | "configuration" | "request";
|
|
readonly relationKinds: readonly (
|
|
| "coerces"
|
|
| "constructs-request"
|
|
| "consumed-by"
|
|
| "defaults"
|
|
| "overrides"
|
|
| "parses"
|
|
| "reads-argv"
|
|
| "reads-config"
|
|
| "reads-environment"
|
|
| "supplies-request-field"
|
|
| "validates"
|
|
)[];
|
|
readonly location: JavaScriptSemanticBoundaryOperation["location"];
|
|
readonly detail: string;
|
|
readonly candidateNodeIds: readonly string[];
|
|
}
|
|
|
|
const addDataUnknown = (input: DataUnknownInput): void => {
|
|
const {
|
|
context,
|
|
node,
|
|
family,
|
|
relationKinds,
|
|
location,
|
|
detail,
|
|
candidateNodeIds,
|
|
} = input;
|
|
addSemanticGraphUnknown(context.state, {
|
|
node_id: node.node_id,
|
|
family,
|
|
relation_kinds: relationKinds,
|
|
reason: input.reason ?? "ambiguous-target",
|
|
detail,
|
|
candidate_node_ids: candidateNodeIds,
|
|
evidence: unknownSemanticEvidence(context.file, location),
|
|
});
|
|
};
|