rea-agents 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +56 -9
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
- package/dist/application/BinarySession.js +9 -2
- package/dist/application/BinarySessionStatus.js +39 -1
- package/dist/application/CapabilityInventory.js +15 -0
- package/dist/application/CommandShimReplay.js +6 -3
- package/dist/application/CompositeProvider.js +5 -2
- package/dist/application/DirectAnalysis.js +1 -20
- package/dist/application/DirectAnalysisStatus.js +83 -0
- package/dist/application/Doctor.js +34 -25
- package/dist/application/DoctorDiagnostics.js +1 -4
- package/dist/application/DoctorProjection.js +43 -0
- package/dist/application/DoctorScope.js +102 -0
- package/dist/application/EvidenceReferenceResolver.js +26 -0
- package/dist/application/JavaScriptApplicationEvidence.js +1 -1
- package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
- package/dist/application/JavaScriptApplicationService.js +3 -2
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
- package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
- package/dist/application/JavaScriptModuleRelationships.js +25 -1
- package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
- package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
- package/dist/application/JavaScriptSemanticTraceService.js +45 -0
- package/dist/application/LoopbackReplay.js +277 -133
- package/dist/application/LoopbackReplayRecorder.js +101 -0
- package/dist/application/PermissionAuthority.js +7 -3
- package/dist/application/ProcessCaptureJournal.js +95 -0
- package/dist/application/ProcessCaptureLifecycle.js +24 -13
- package/dist/application/ProcessCheckpoints.js +30 -4
- package/dist/application/ProcessCli.js +26 -4
- package/dist/application/ProcessHarness.js +20 -50
- package/dist/application/ProcessNormalization.js +3 -2
- package/dist/application/ProcessReactiveCoordinator.js +218 -0
- package/dist/application/ProcessReactiveEffects.js +133 -0
- package/dist/application/ProcessReactiveObservations.js +20 -0
- package/dist/application/ProcessSampling.js +2 -0
- package/dist/application/SessionProviderRouter.js +7 -6
- package/dist/application/Setup.js +11 -10
- package/dist/application/SetupPlan.js +1 -1
- package/dist/application/SetupRegistrationEnvironment.js +12 -0
- package/dist/application/TerminalRenderer.js +3 -1
- package/dist/browser/CdpEndpoint.js +37 -4
- package/dist/catalogIdentity.js +16 -7
- package/dist/cli/coreAnalysisCommands.js +34 -2
- package/dist/cli/javascriptApplicationAnalysis.js +19 -0
- package/dist/cli/setupCommands.js +81 -32
- package/dist/cli/utilityCommands.js +10 -2
- package/dist/cliApplicationCommands.js +79 -16
- package/dist/cliCommandNames.js +3 -0
- package/dist/cliElectronCommands.js +23 -33
- package/dist/cliObservationOptions.js +1 -1
- package/dist/cliOutput.js +56 -0
- package/dist/cliProcessCommands.js +32 -1
- package/dist/cliSetup.js +30 -21
- package/dist/contracts/applicationToolContracts.js +54 -7
- package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
- package/dist/contracts/electronToolContracts.js +53 -32
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
- package/dist/contracts/processCaptureExample.js +1 -0
- package/dist/contracts/replayMachineExample.js +37 -0
- package/dist/contracts/toolContractExamples.js +2 -0
- package/dist/contracts/toolContracts.js +5 -1
- package/dist/contracts/toolEffects.js +3 -0
- package/dist/contracts/toolOutputSchemaGroups.js +2 -0
- package/dist/contracts/toolOutputSchemaPrimitives.js +40 -0
- package/dist/doctorRuntime.js +41 -0
- package/dist/domain/analysisErrorPresentation.js +5 -3
- package/dist/domain/analysisErrorProjection.js +7 -1
- package/dist/domain/changedBehavior.js +5 -4
- package/dist/domain/completionLedgerGeneration.js +190 -0
- package/dist/domain/errors.js +4 -2
- package/dist/domain/evidenceCompletionLedger.js +113 -0
- package/dist/domain/hopperStartupFailure.js +9 -0
- package/dist/domain/javascriptApplicationAnalysis.js +44 -2
- package/dist/domain/javascriptExportShapeComparison.js +145 -0
- package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
- package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
- package/dist/domain/javascriptExportShapeSelection.js +132 -0
- package/dist/domain/javascriptExportShapeVariants.js +272 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
- package/dist/domain/javascriptSemanticAnalysis.js +17 -36
- package/dist/domain/javascriptSemanticCallResolution.js +129 -0
- package/dist/domain/javascriptSemanticCalls.js +269 -0
- package/dist/domain/javascriptSemanticGraph.js +356 -0
- package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
- package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
- package/dist/domain/javascriptSemanticIr.js +14 -1
- package/dist/domain/javascriptSemanticPrimitives.js +36 -0
- package/dist/domain/javascriptSemanticProjection.js +36 -4
- package/dist/domain/javascriptSemanticProvenance.js +29 -0
- package/dist/domain/javascriptSemanticQuery.js +294 -0
- package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
- package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
- package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
- package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
- package/dist/domain/javascriptSemanticReturns.js +187 -0
- package/dist/domain/javascriptSemanticState.js +32 -0
- package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
- package/dist/domain/javascriptSemanticValues.js +54 -70
- package/dist/domain/processCapture.js +36 -0
- package/dist/domain/processCaptureValidation.js +101 -12
- package/dist/domain/processComparison.js +78 -25
- package/dist/domain/processObservation.js +153 -0
- package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
- package/dist/domain/processReactiveMatching.js +210 -0
- package/dist/domain/processReactiveRuntime.js +109 -0
- package/dist/domain/processReactiveScenario.js +361 -0
- package/dist/domain/processReactiveScenarioPreflight.js +120 -0
- package/dist/domain/processReactiveTransition.js +159 -0
- package/dist/domain/processScenario.js +12 -0
- package/dist/domain/processTraceComparison.js +43 -0
- package/dist/domain/processTraceDimensionProjection.js +50 -0
- package/dist/domain/processTraceEvaluation.js +312 -0
- package/dist/domain/processTraceSpecification.js +373 -0
- package/dist/domain/reconstructionVerification.js +5 -4
- package/dist/domain/replayMachine.js +18 -2
- package/dist/domain/replayMachineRun.js +216 -0
- package/dist/domain/replayMachineRuntime.js +88 -9
- package/dist/domain/staticRuntimeCorrelation.js +4 -2
- package/dist/evaluation/CodexAgentEval.js +24 -0
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraClient.js +13 -2
- package/dist/ghidra/GhidraDiagnostics.js +1 -0
- package/dist/ghidra/GhidraLauncher.js +7 -3
- package/dist/ghidra/GhidraProvider.js +8 -1
- package/dist/hopper/BridgeLauncher.js +56 -14
- package/dist/hopper/HopperClient.js +23 -10
- package/dist/hopper/HopperDiagnostics.js +14 -0
- package/dist/hopper/HopperProcessDiagnostic.js +14 -0
- package/dist/hopper/HopperProvider.js +8 -1
- package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
- package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
- package/dist/main/transport.js +2 -0
- package/dist/mcpDoctor.js +310 -0
- package/dist/process/ProcessOwnership.js +200 -53
- package/dist/process/ProviderRunLineage.js +21 -0
- package/dist/server/createServer.js +4 -1
- package/dist/server/javascriptApplicationResult.js +22 -1
- package/dist/server/registerApplicationTools/characterization.js +3 -3
- package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
- package/dist/server/registerApplicationTools/compareVersions.js +12 -6
- package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
- package/dist/server/registerApplicationTools/coverage.js +3 -3
- package/dist/server/registerApplicationTools/helpers.js +37 -25
- package/dist/server/registerApplicationTools/traceFeature.js +9 -5
- package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
- package/dist/server/registerApplicationTools.js +4 -0
- package/dist/server/registerEvidenceResources.js +5 -1
- package/dist/server/registerProcessComparisonTool.js +17 -7
- package/dist/server/registerReplayMachineTool.js +20 -0
- package/dist/server/registerSessionTools.js +12 -7
- package/dist/server/sessionAvailabilityPolicy.js +2 -1
- package/dist/serverIdentity.js +19 -0
- package/package.json +62 -30
- package/scripts/hopper-demo-x11.py +459 -61
- package/scripts/prepack.mjs +55 -0
- package/scripts/prepare.mjs +28 -0
- package/scripts/rea.mjs +26 -5
- package/skills/reverse-engineer-anything/SKILL.md +2 -2
- package/skills/reverse-engineer-anything/references/javascript-applications.md +10 -0
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { createJavaScriptSemanticGraph, } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
+
import { JAVASCRIPT_SEMANTIC_RELATION_FAMILIES, JAVASCRIPT_SEMANTIC_RELATION_FAMILY, } from "../domain/javascriptSemanticGraphSchemas.js";
|
|
3
|
+
import { inferredSemanticEvidenceAt } from "./JavaScriptSemanticGraphEvidence.js";
|
|
4
|
+
import { projectSemanticClosureCaptures, projectSemanticFrontiers, projectSemanticReturnValues, semanticRecoveryLimits, } from "./JavaScriptSemanticGraphFlowProjection.js";
|
|
5
|
+
import { owningSemanticCallableNode, semanticFamilyStatus, semanticNodesWithinRange, semanticRangesEqual as rangesEqual, } from "./JavaScriptSemanticGraphProjection.js";
|
|
6
|
+
import { addSemanticFallbackRoot as addFallbackRoot, addSemanticGraphNode as addNode, addSemanticGraphRelation as addRelation, constructSemanticGraphNode as semanticNode, createSemanticGraphProjectionState as emptyState, MAX_APPLICATION_NODE_IDS_PER_SEMANTIC_NODE, MAX_SEMANTIC_GRAPH_NODES, MAX_SEMANTIC_GRAPH_RELATIONS, MAX_SEMANTIC_GRAPH_UNKNOWNS, } from "./JavaScriptSemanticGraphConstruction.js";
|
|
7
|
+
/** Project bounded per-file semantic IR into an artifact-bound companion graph. */
|
|
8
|
+
export const buildJavaScriptSemanticGraph = ({ rootArtifactSha256, applicationGraph, analysis, }) => {
|
|
9
|
+
const state = emptyState(applicationGraph);
|
|
10
|
+
for (const analyzed of analysis.files) {
|
|
11
|
+
if (analyzed.semantic === null)
|
|
12
|
+
continue;
|
|
13
|
+
projectFile(analyzed.file, analyzed.semantic.ir, state);
|
|
14
|
+
}
|
|
15
|
+
if (state.roots.size === 0)
|
|
16
|
+
addFallbackRoot(rootArtifactSha256, state);
|
|
17
|
+
const semanticTruncated = analysis.files.some(({ semantic }) => semantic?.ir.coverage.status === "truncated");
|
|
18
|
+
const truncated = state.limitsReached.size > 0 || semanticTruncated;
|
|
19
|
+
const unknowns = [...state.unknowns.values()];
|
|
20
|
+
return createJavaScriptSemanticGraph({
|
|
21
|
+
schema: "JavaScriptSemanticRelationGraph",
|
|
22
|
+
schema_version: 1,
|
|
23
|
+
root_artifact_sha256: rootArtifactSha256,
|
|
24
|
+
application_graph_id: applicationGraph.graph_id,
|
|
25
|
+
root_node_ids: [...state.roots],
|
|
26
|
+
nodes: [...state.nodes.values()],
|
|
27
|
+
relations: [...state.relations.values()],
|
|
28
|
+
fingerprints: [],
|
|
29
|
+
unknowns,
|
|
30
|
+
coverage: {
|
|
31
|
+
status: truncated ? "partial" : "unknown",
|
|
32
|
+
truncated,
|
|
33
|
+
omitted_nodes: truncated ? null : 0,
|
|
34
|
+
omitted_relations: truncated ? null : 0,
|
|
35
|
+
limits: [
|
|
36
|
+
{
|
|
37
|
+
name: "max_nodes",
|
|
38
|
+
value: MAX_SEMANTIC_GRAPH_NODES,
|
|
39
|
+
unit: "items",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
name: "max_relations",
|
|
43
|
+
value: MAX_SEMANTIC_GRAPH_RELATIONS,
|
|
44
|
+
unit: "items",
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
name: "max_unknowns",
|
|
48
|
+
value: MAX_SEMANTIC_GRAPH_UNKNOWNS,
|
|
49
|
+
unit: "items",
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
name: "max_application_node_ids_per_semantic_node",
|
|
53
|
+
value: MAX_APPLICATION_NODE_IDS_PER_SEMANTIC_NODE,
|
|
54
|
+
unit: "items",
|
|
55
|
+
},
|
|
56
|
+
...semanticRecoveryLimits(analysis),
|
|
57
|
+
],
|
|
58
|
+
families: JAVASCRIPT_SEMANTIC_RELATION_FAMILIES.map((family) => ({
|
|
59
|
+
family,
|
|
60
|
+
status: semanticFamilyStatus(family, analysis, truncated),
|
|
61
|
+
retained_relations: [...state.relations.values()].filter((relation) => JAVASCRIPT_SEMANTIC_RELATION_FAMILY[relation.relation] === family).length,
|
|
62
|
+
omitted_relations: truncated ? null : 0,
|
|
63
|
+
unknown_ids: unknowns
|
|
64
|
+
.filter((unknown) => unknown.family === family)
|
|
65
|
+
.map(({ unknown_id: identifier }) => identifier),
|
|
66
|
+
})),
|
|
67
|
+
},
|
|
68
|
+
limitations: [
|
|
69
|
+
"The semantic graph contains static syntax observations and conservative relationship candidates; it does not claim runtime execution.",
|
|
70
|
+
"Local data flow does not claim control-flow-sensitive reaching definitions or arbitrary dynamic property resolution.",
|
|
71
|
+
"Function fingerprints and promise, event, process, request, boundary, timer, configuration, and resource extraction are not available in this graph version's current extractor coverage.",
|
|
72
|
+
...(truncated
|
|
73
|
+
? [
|
|
74
|
+
"Semantic graph projection reached one or more explicit hard limits.",
|
|
75
|
+
]
|
|
76
|
+
: []),
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
};
|
|
80
|
+
const projectFile = (file, ir, state) => {
|
|
81
|
+
const moduleNode = addNode(state, semanticNode(file, {
|
|
82
|
+
kind: "module",
|
|
83
|
+
roleKey: "module",
|
|
84
|
+
location: null,
|
|
85
|
+
label: file.path,
|
|
86
|
+
functionNodeId: null,
|
|
87
|
+
}, state));
|
|
88
|
+
if (moduleNode === null)
|
|
89
|
+
return;
|
|
90
|
+
state.roots.add(moduleNode.node_id);
|
|
91
|
+
const callableNodes = new Map(ir.callables.flatMap((callable) => {
|
|
92
|
+
const node = addNode(state, semanticNode(file, {
|
|
93
|
+
kind: "function",
|
|
94
|
+
roleKey: `callable:${callable.callableId}`,
|
|
95
|
+
location: callable.location,
|
|
96
|
+
label: callable.name,
|
|
97
|
+
functionNodeId: null,
|
|
98
|
+
}, state));
|
|
99
|
+
return node === null ? [] : [[callable.callableId, node]];
|
|
100
|
+
}));
|
|
101
|
+
const bindingNodes = new Map(ir.bindings.flatMap((binding) => {
|
|
102
|
+
const location = binding.definitions[0]?.location ?? null;
|
|
103
|
+
const kind = binding.kind === "parameter" ? "parameter" : "binding";
|
|
104
|
+
const owner = owningSemanticCallableNode(location, ir, callableNodes);
|
|
105
|
+
const node = addNode(state, semanticNode(file, {
|
|
106
|
+
kind,
|
|
107
|
+
roleKey: `binding:${binding.bindingId}`,
|
|
108
|
+
location,
|
|
109
|
+
label: binding.name,
|
|
110
|
+
functionNodeId: owner?.node_id ?? null,
|
|
111
|
+
}, state));
|
|
112
|
+
return node === null ? [] : [[binding.bindingId, node]];
|
|
113
|
+
}));
|
|
114
|
+
const returnSiteNodes = createReturnSiteNodes(file, ir, callableNodes, state);
|
|
115
|
+
const callSiteNodes = createCallSiteNodes(file, ir, callableNodes, state);
|
|
116
|
+
const context = {
|
|
117
|
+
file,
|
|
118
|
+
ir,
|
|
119
|
+
state,
|
|
120
|
+
moduleNode,
|
|
121
|
+
bindingNodes,
|
|
122
|
+
callableNodes,
|
|
123
|
+
callSiteNodes,
|
|
124
|
+
returnSiteNodes,
|
|
125
|
+
argumentNodes: new Map(),
|
|
126
|
+
referenceNodes: [],
|
|
127
|
+
callResolutions: new Map(ir.callSites.map((call) => [
|
|
128
|
+
call.callSiteId,
|
|
129
|
+
call.resolution === "exact" ? "resolved" : "candidate",
|
|
130
|
+
])),
|
|
131
|
+
};
|
|
132
|
+
projectDefinitionsAndReferences(context);
|
|
133
|
+
projectCalls(context);
|
|
134
|
+
projectSemanticClosureCaptures(context);
|
|
135
|
+
projectSemanticFrontiers(context);
|
|
136
|
+
};
|
|
137
|
+
const createReturnSiteNodes = (file, ir, callables, state) => {
|
|
138
|
+
const result = new Map();
|
|
139
|
+
for (const callable of ir.callables) {
|
|
140
|
+
const owner = callables.get(callable.callableId);
|
|
141
|
+
if (owner === undefined)
|
|
142
|
+
continue;
|
|
143
|
+
for (const site of callable.returnSites) {
|
|
144
|
+
const identifier = site.returnSiteId;
|
|
145
|
+
const node = addNode(state, semanticNode(file, {
|
|
146
|
+
kind: "return-site",
|
|
147
|
+
roleKey: identifier,
|
|
148
|
+
location: site.location,
|
|
149
|
+
label: "return",
|
|
150
|
+
functionNodeId: owner.node_id,
|
|
151
|
+
}, state));
|
|
152
|
+
if (node !== null)
|
|
153
|
+
result.set(identifier, node);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
};
|
|
158
|
+
const createCallSiteNodes = (file, ir, callables, state) => {
|
|
159
|
+
const result = new Map();
|
|
160
|
+
for (const call of ir.callSites) {
|
|
161
|
+
const owner = call.callerCallableId === null
|
|
162
|
+
? null
|
|
163
|
+
: (callables.get(call.callerCallableId)?.node_id ?? null);
|
|
164
|
+
const node = addNode(state, semanticNode(file, {
|
|
165
|
+
kind: "call-site",
|
|
166
|
+
roleKey: `call:${call.callSiteId}`,
|
|
167
|
+
location: call.location,
|
|
168
|
+
label: call.kind,
|
|
169
|
+
functionNodeId: owner,
|
|
170
|
+
}, state));
|
|
171
|
+
if (node !== null)
|
|
172
|
+
result.set(call.callSiteId, node);
|
|
173
|
+
}
|
|
174
|
+
return result;
|
|
175
|
+
};
|
|
176
|
+
const projectDefinitionsAndReferences = (context) => {
|
|
177
|
+
for (const binding of context.ir.bindings) {
|
|
178
|
+
const bindingNode = context.bindingNodes.get(binding.bindingId);
|
|
179
|
+
if (bindingNode === undefined)
|
|
180
|
+
continue;
|
|
181
|
+
for (const [index, definition] of binding.definitions.entries()) {
|
|
182
|
+
const definitionNode = addNode(context.state, semanticNode(context.file, {
|
|
183
|
+
kind: "expression",
|
|
184
|
+
roleKey: `definition:${binding.bindingId}:${String(index)}`,
|
|
185
|
+
location: definition.location,
|
|
186
|
+
label: definition.kind,
|
|
187
|
+
functionNodeId: bindingNode.function_node_id,
|
|
188
|
+
}, context.state));
|
|
189
|
+
addRelation(context.state, {
|
|
190
|
+
source: definitionNode,
|
|
191
|
+
target: bindingNode,
|
|
192
|
+
relation: "defines",
|
|
193
|
+
resolution: "resolved",
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
for (const [index, reference] of context.ir.references.entries()) {
|
|
198
|
+
const expression = addNode(context.state, semanticNode(context.file, {
|
|
199
|
+
kind: "expression",
|
|
200
|
+
roleKey: `reference:${String(index)}:${reference.role}:${reference.name}`,
|
|
201
|
+
location: reference.location,
|
|
202
|
+
label: reference.name,
|
|
203
|
+
functionNodeId: owningSemanticCallableNode(reference.location, context.ir, context.callableNodes)?.node_id ?? null,
|
|
204
|
+
}, context.state));
|
|
205
|
+
const binding = reference.bindingId === null
|
|
206
|
+
? undefined
|
|
207
|
+
: context.bindingNodes.get(reference.bindingId);
|
|
208
|
+
if (expression !== null)
|
|
209
|
+
context.referenceNodes.push(expression);
|
|
210
|
+
if (binding === undefined || expression === null)
|
|
211
|
+
continue;
|
|
212
|
+
if (reference.role === "read")
|
|
213
|
+
addRelation(context.state, {
|
|
214
|
+
source: binding,
|
|
215
|
+
target: expression,
|
|
216
|
+
relation: "reads",
|
|
217
|
+
resolution: "resolved",
|
|
218
|
+
evidence: inferredSemanticEvidenceAt(context.file, reference.location),
|
|
219
|
+
});
|
|
220
|
+
else
|
|
221
|
+
addRelation(context.state, {
|
|
222
|
+
source: expression,
|
|
223
|
+
target: binding,
|
|
224
|
+
relation: "writes",
|
|
225
|
+
resolution: "resolved",
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
const projectCalls = (context) => {
|
|
230
|
+
for (const call of context.ir.callSites) {
|
|
231
|
+
const callNode = context.callSiteNodes.get(call.callSiteId);
|
|
232
|
+
if (callNode === undefined)
|
|
233
|
+
continue;
|
|
234
|
+
for (const callableId of call.calleeCallableIds) {
|
|
235
|
+
const callable = context.callableNodes.get(callableId);
|
|
236
|
+
addRelation(context.state, {
|
|
237
|
+
source: callNode,
|
|
238
|
+
target: callable,
|
|
239
|
+
relation: "calls",
|
|
240
|
+
resolution: call.resolution === "exact" ? "resolved" : "candidate",
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
for (const argument of call.arguments) {
|
|
244
|
+
const argumentNode = createArgumentNode(context, callNode, call.callSiteId, argument);
|
|
245
|
+
if (argumentNode !== null)
|
|
246
|
+
context.argumentNodes.set(`${call.callSiteId}\u0000${String(argument.index)}`, argumentNode);
|
|
247
|
+
const references = semanticNodesWithinRange(context.referenceNodes, argument.location);
|
|
248
|
+
for (const reference of references)
|
|
249
|
+
addRelation(context.state, {
|
|
250
|
+
source: reference,
|
|
251
|
+
target: argumentNode,
|
|
252
|
+
relation: "aliases",
|
|
253
|
+
resolution: references.length === 1 &&
|
|
254
|
+
rangesEqual(reference.identity.source_range, argument.location)
|
|
255
|
+
? "resolved"
|
|
256
|
+
: "candidate",
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
for (const flow of context.ir.argumentFlows) {
|
|
261
|
+
const argument = context.argumentNodes.get(`${flow.callSiteId}\u0000${String(flow.argumentIndex)}`);
|
|
262
|
+
const parameter = context.bindingNodes.get(flow.parameterBindingId);
|
|
263
|
+
addRelation(context.state, {
|
|
264
|
+
source: argument,
|
|
265
|
+
target: parameter,
|
|
266
|
+
relation: "argument-to-parameter",
|
|
267
|
+
resolution: context.callResolutions.get(flow.callSiteId) ?? "candidate",
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
for (const flow of context.ir.callReturnFlows)
|
|
271
|
+
addRelation(context.state, {
|
|
272
|
+
source: context.returnSiteNodes.get(flow.returnSiteId),
|
|
273
|
+
target: context.callSiteNodes.get(flow.callSiteId),
|
|
274
|
+
relation: "returns-to-call",
|
|
275
|
+
resolution: context.callResolutions.get(flow.callSiteId) ?? "candidate",
|
|
276
|
+
});
|
|
277
|
+
for (const flow of context.ir.callResultFlows)
|
|
278
|
+
addRelation(context.state, {
|
|
279
|
+
source: context.callSiteNodes.get(flow.callSiteId),
|
|
280
|
+
target: context.bindingNodes.get(flow.bindingId),
|
|
281
|
+
relation: "aliases",
|
|
282
|
+
resolution: context.callResolutions.get(flow.callSiteId) ?? "candidate",
|
|
283
|
+
});
|
|
284
|
+
projectSemanticReturnValues(context);
|
|
285
|
+
};
|
|
286
|
+
const createArgumentNode = (context, callNode, callSiteId, argument) => addNode(context.state, semanticNode(context.file, {
|
|
287
|
+
kind: "expression",
|
|
288
|
+
roleKey: `argument:${callSiteId}:${String(argument.index)}`,
|
|
289
|
+
location: argument.location,
|
|
290
|
+
label: argument.spread
|
|
291
|
+
? "spread argument"
|
|
292
|
+
: `argument ${String(argument.index)}`,
|
|
293
|
+
functionNodeId: callNode.function_node_id,
|
|
294
|
+
}, context.state));
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { createJavaScriptSemanticGraphNode, createJavaScriptSemanticGraphRelation, } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
+
import { inferredSemanticEvidence, observedSemanticEvidence, unavailableSemanticRootEvidence, } from "./JavaScriptSemanticGraphEvidence.js";
|
|
3
|
+
/** Hard semantic graph node bound enforced during artifact projection. */
|
|
4
|
+
export const MAX_SEMANTIC_GRAPH_NODES = 100_000;
|
|
5
|
+
/** Hard semantic graph relation bound enforced during artifact projection. */
|
|
6
|
+
export const MAX_SEMANTIC_GRAPH_RELATIONS = 200_000;
|
|
7
|
+
/** Hard unresolved-frontier bound enforced across all analyzed files. */
|
|
8
|
+
export const MAX_SEMANTIC_GRAPH_UNKNOWNS = 100_000;
|
|
9
|
+
/** Maximum structural nodes linked to one exact semantic location. */
|
|
10
|
+
export const MAX_APPLICATION_NODE_IDS_PER_SEMANTIC_NODE = 64;
|
|
11
|
+
/** Create empty projection state with structural application-node mappings. */
|
|
12
|
+
export const createSemanticGraphProjectionState = (applicationGraph) => {
|
|
13
|
+
const index = indexApplicationNodes(applicationGraph.nodes);
|
|
14
|
+
return {
|
|
15
|
+
nodes: new Map(),
|
|
16
|
+
relations: new Map(),
|
|
17
|
+
unknowns: new Map(),
|
|
18
|
+
roots: new Set(),
|
|
19
|
+
limitsReached: new Set(index.truncated ? ["max_application_node_ids_per_semantic_node"] : []),
|
|
20
|
+
applicationNodeIdsByLocation: index.identifiers,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
/** Construct one canonical semantic node backed by an exact artifact file. */
|
|
24
|
+
export const constructSemanticGraphNode = (file, input, state) => createJavaScriptSemanticGraphNode({
|
|
25
|
+
kind: input.kind,
|
|
26
|
+
identity: {
|
|
27
|
+
artifact_sha256: file.sha256,
|
|
28
|
+
module_path: file.path,
|
|
29
|
+
source_range: input.location,
|
|
30
|
+
role_key: input.roleKey,
|
|
31
|
+
},
|
|
32
|
+
function_node_id: input.functionNodeId,
|
|
33
|
+
application_node_ids: matchingApplicationNodeIds(file, input, state),
|
|
34
|
+
label: input.label,
|
|
35
|
+
properties: {},
|
|
36
|
+
evidence: observedSemanticEvidence(file, input.location),
|
|
37
|
+
});
|
|
38
|
+
/** Retain one canonical node unless the hard graph bound was reached. */
|
|
39
|
+
export const addSemanticGraphNode = (state, node) => {
|
|
40
|
+
const existing = state.nodes.get(node.node_id);
|
|
41
|
+
if (existing !== undefined)
|
|
42
|
+
return existing;
|
|
43
|
+
if (state.nodes.size >= MAX_SEMANTIC_GRAPH_NODES) {
|
|
44
|
+
state.limitsReached.add("max_nodes");
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
state.nodes.set(node.node_id, node);
|
|
48
|
+
return node;
|
|
49
|
+
};
|
|
50
|
+
/** Retain one non-self semantic relationship unless its hard bound was reached. */
|
|
51
|
+
export const addSemanticGraphRelation = (state, input) => {
|
|
52
|
+
if (input.source === undefined ||
|
|
53
|
+
input.source === null ||
|
|
54
|
+
input.target === undefined ||
|
|
55
|
+
input.target === null ||
|
|
56
|
+
input.source.node_id === input.target.node_id)
|
|
57
|
+
return;
|
|
58
|
+
const value = createJavaScriptSemanticGraphRelation({
|
|
59
|
+
source_node_id: input.source.node_id,
|
|
60
|
+
target_node_id: input.target.node_id,
|
|
61
|
+
relation: input.relation,
|
|
62
|
+
resolution: input.resolution ?? "candidate",
|
|
63
|
+
properties: {},
|
|
64
|
+
evidence: input.evidence ?? inferredSemanticEvidence(input.source),
|
|
65
|
+
});
|
|
66
|
+
if (state.relations.has(value.relation_id))
|
|
67
|
+
return;
|
|
68
|
+
if (state.relations.size >= MAX_SEMANTIC_GRAPH_RELATIONS) {
|
|
69
|
+
state.limitsReached.add("max_relations");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
state.relations.set(value.relation_id, value);
|
|
73
|
+
};
|
|
74
|
+
/** Retain one unresolved frontier unless the global graph bound was reached. */
|
|
75
|
+
export const addSemanticGraphUnknown = (state, unknown) => {
|
|
76
|
+
if (state.unknowns.has(unknown.unknown_id))
|
|
77
|
+
return;
|
|
78
|
+
if (state.unknowns.size >= MAX_SEMANTIC_GRAPH_UNKNOWNS) {
|
|
79
|
+
state.limitsReached.add("max_unknowns");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
state.unknowns.set(unknown.unknown_id, unknown);
|
|
83
|
+
};
|
|
84
|
+
/** Add a truthful root when no source file could produce semantic IR. */
|
|
85
|
+
export const addSemanticFallbackRoot = (rootArtifactSha256, state) => {
|
|
86
|
+
const node = addSemanticGraphNode(state, createJavaScriptSemanticGraphNode({
|
|
87
|
+
kind: "module",
|
|
88
|
+
identity: {
|
|
89
|
+
artifact_sha256: rootArtifactSha256,
|
|
90
|
+
module_path: "unknown-semantic-root",
|
|
91
|
+
source_range: null,
|
|
92
|
+
role_key: "artifact-root",
|
|
93
|
+
},
|
|
94
|
+
function_node_id: null,
|
|
95
|
+
application_node_ids: [],
|
|
96
|
+
label: "unavailable semantic root",
|
|
97
|
+
properties: {},
|
|
98
|
+
evidence: unavailableSemanticRootEvidence(rootArtifactSha256),
|
|
99
|
+
}));
|
|
100
|
+
if (node !== null)
|
|
101
|
+
state.roots.add(node.node_id);
|
|
102
|
+
};
|
|
103
|
+
const indexApplicationNodes = (nodes) => {
|
|
104
|
+
const identifiers = new Map();
|
|
105
|
+
for (const node of nodes) {
|
|
106
|
+
for (const observation of node.observations) {
|
|
107
|
+
const { artifact, location } = observation.evidence;
|
|
108
|
+
if (!artifact.available || !location.available)
|
|
109
|
+
continue;
|
|
110
|
+
if (location.value.kind === "artifact-path") {
|
|
111
|
+
addApplicationNodeIdentifier(identifiers, applicationLocationKey(artifact.sha256, location.value.path, null), node.node_id);
|
|
112
|
+
}
|
|
113
|
+
if (location.value.kind === "source-range") {
|
|
114
|
+
addApplicationNodeIdentifier(identifiers, applicationLocationKey(artifact.sha256, location.value.source, {
|
|
115
|
+
start: location.value.start,
|
|
116
|
+
end: location.value.end,
|
|
117
|
+
}), node.node_id);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
let truncated = false;
|
|
122
|
+
const indexed = new Map([...identifiers].map(([key, values]) => {
|
|
123
|
+
const sorted = [...values].sort();
|
|
124
|
+
if (sorted.length > MAX_APPLICATION_NODE_IDS_PER_SEMANTIC_NODE)
|
|
125
|
+
truncated = true;
|
|
126
|
+
return [key, sorted.slice(0, MAX_APPLICATION_NODE_IDS_PER_SEMANTIC_NODE)];
|
|
127
|
+
}));
|
|
128
|
+
return { identifiers: indexed, truncated };
|
|
129
|
+
};
|
|
130
|
+
const matchingApplicationNodeIds = (file, input, state) => [
|
|
131
|
+
...(state.applicationNodeIdsByLocation.get(applicationLocationKey(file.sha256, file.path, input.location)) ?? []),
|
|
132
|
+
];
|
|
133
|
+
const addApplicationNodeIdentifier = (identifiers, key, nodeId) => {
|
|
134
|
+
const values = identifiers.get(key) ?? new Set();
|
|
135
|
+
values.add(nodeId);
|
|
136
|
+
identifiers.set(key, values);
|
|
137
|
+
};
|
|
138
|
+
const applicationLocationKey = (artifactSha256, path, range) => [
|
|
139
|
+
artifactSha256,
|
|
140
|
+
path,
|
|
141
|
+
range === null
|
|
142
|
+
? "artifact-path"
|
|
143
|
+
: `${range.start.line}:${range.start.column}-${range.end.line}:${range.end.column}`,
|
|
144
|
+
].join("\u0000");
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const INFERENCE_LIMITATION = "This relationship is a bounded static candidate; it does not prove runtime execution or causal flow.";
|
|
2
|
+
/** Exact artifact-backed syntax evidence for one semantic graph node. */
|
|
3
|
+
export const observedSemanticEvidence = (file, location) => ({
|
|
4
|
+
authority: "ast-static-analysis",
|
|
5
|
+
state: "observed",
|
|
6
|
+
confidence: "exact",
|
|
7
|
+
artifact: {
|
|
8
|
+
available: true,
|
|
9
|
+
artifact_id: `art_${file.sha256}`,
|
|
10
|
+
sha256: file.sha256,
|
|
11
|
+
},
|
|
12
|
+
location: location === null
|
|
13
|
+
? { available: true, value: { kind: "artifact-path", path: file.path } }
|
|
14
|
+
: {
|
|
15
|
+
available: true,
|
|
16
|
+
value: { kind: "source-range", source: file.path, ...location },
|
|
17
|
+
},
|
|
18
|
+
extractor: {
|
|
19
|
+
name: "rea-javascript-semantic-analysis",
|
|
20
|
+
version: "1",
|
|
21
|
+
operation: "analyze_javascript_application",
|
|
22
|
+
executable_sha256: null,
|
|
23
|
+
},
|
|
24
|
+
coverage: {
|
|
25
|
+
status: "complete",
|
|
26
|
+
truncated: false,
|
|
27
|
+
omitted_count: 0,
|
|
28
|
+
limits: [],
|
|
29
|
+
},
|
|
30
|
+
limitations: [],
|
|
31
|
+
evidence_ids: [],
|
|
32
|
+
});
|
|
33
|
+
/** Conservative static relationship evidence derived from one syntax node. */
|
|
34
|
+
export const inferredSemanticEvidence = (source) => ({
|
|
35
|
+
...source.evidence,
|
|
36
|
+
authority: "static-relationship-inference",
|
|
37
|
+
state: "inferred",
|
|
38
|
+
confidence: "high",
|
|
39
|
+
limitations: [INFERENCE_LIMITATION],
|
|
40
|
+
});
|
|
41
|
+
/** Static relationship evidence anchored at the syntax that proves the link. */
|
|
42
|
+
export const inferredSemanticEvidenceAt = (file, location) => ({
|
|
43
|
+
...observedSemanticEvidence(file, location),
|
|
44
|
+
authority: "static-relationship-inference",
|
|
45
|
+
state: "inferred",
|
|
46
|
+
confidence: "high",
|
|
47
|
+
limitations: [INFERENCE_LIMITATION],
|
|
48
|
+
});
|
|
49
|
+
/** Explicit absence of semantic syntax for an artifact-level fallback root. */
|
|
50
|
+
export const unavailableSemanticRootEvidence = (rootArtifactSha256) => ({
|
|
51
|
+
authority: "unknown",
|
|
52
|
+
state: "unavailable",
|
|
53
|
+
confidence: "unknown",
|
|
54
|
+
artifact: {
|
|
55
|
+
available: true,
|
|
56
|
+
artifact_id: `art_${rootArtifactSha256}`,
|
|
57
|
+
sha256: rootArtifactSha256,
|
|
58
|
+
},
|
|
59
|
+
location: {
|
|
60
|
+
available: false,
|
|
61
|
+
reason: "not-observed",
|
|
62
|
+
detail: "No admitted JavaScript source produced semantic IR.",
|
|
63
|
+
},
|
|
64
|
+
extractor: {
|
|
65
|
+
name: "rea-javascript-semantic-analysis",
|
|
66
|
+
version: "1",
|
|
67
|
+
operation: "analyze_javascript_application",
|
|
68
|
+
executable_sha256: null,
|
|
69
|
+
},
|
|
70
|
+
coverage: {
|
|
71
|
+
status: "unavailable",
|
|
72
|
+
truncated: false,
|
|
73
|
+
omitted_count: null,
|
|
74
|
+
limits: [],
|
|
75
|
+
},
|
|
76
|
+
limitations: ["This synthetic graph root does not claim a source location."],
|
|
77
|
+
evidence_ids: [],
|
|
78
|
+
});
|
|
79
|
+
/** Explicit unknown evidence at one unresolved dynamic syntax location. */
|
|
80
|
+
export const unknownSemanticEvidence = (file, location) => ({
|
|
81
|
+
...observedSemanticEvidence(file, location),
|
|
82
|
+
authority: "unknown",
|
|
83
|
+
state: "unknown",
|
|
84
|
+
confidence: "unknown",
|
|
85
|
+
limitations: ["Dynamic syntax prevents an exact static relationship."],
|
|
86
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { createJavaScriptSemanticGraphUnknown } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
+
import { addSemanticGraphRelation, addSemanticGraphUnknown, } from "./JavaScriptSemanticGraphConstruction.js";
|
|
3
|
+
import { inferredSemanticEvidenceAt, unknownSemanticEvidence, } from "./JavaScriptSemanticGraphEvidence.js";
|
|
4
|
+
import { semanticNodesWithinRange } from "./JavaScriptSemanticGraphProjection.js";
|
|
5
|
+
/** Link direct return expressions to their retained semantic references. */
|
|
6
|
+
export const projectSemanticReturnValues = (context) => {
|
|
7
|
+
for (const callable of context.ir.callables)
|
|
8
|
+
for (const site of callable.returnSites) {
|
|
9
|
+
const returnNode = context.returnSiteNodes.get(site.returnSiteId);
|
|
10
|
+
const references = semanticNodesWithinRange(context.referenceNodes, site.location);
|
|
11
|
+
for (const reference of references)
|
|
12
|
+
addSemanticGraphRelation(context.state, {
|
|
13
|
+
source: reference,
|
|
14
|
+
target: returnNode,
|
|
15
|
+
relation: "aliases",
|
|
16
|
+
resolution: site.identityReferenceLocation !== null &&
|
|
17
|
+
rangesEqual(reference.identity.source_range, site.identityReferenceLocation)
|
|
18
|
+
? "resolved"
|
|
19
|
+
: "candidate",
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const rangesEqual = (left, right) => left !== null &&
|
|
24
|
+
right !== null &&
|
|
25
|
+
left.start.line === right.start.line &&
|
|
26
|
+
left.start.column === right.start.column &&
|
|
27
|
+
left.end.line === right.end.line &&
|
|
28
|
+
left.end.column === right.end.column;
|
|
29
|
+
/** Link each captured binding to the callable at the exact reference site. */
|
|
30
|
+
export const projectSemanticClosureCaptures = (context) => {
|
|
31
|
+
for (const capture of context.ir.closureCaptures)
|
|
32
|
+
addSemanticGraphRelation(context.state, {
|
|
33
|
+
source: context.bindingNodes.get(capture.bindingId),
|
|
34
|
+
target: context.callableNodes.get(capture.callableId),
|
|
35
|
+
relation: "captures",
|
|
36
|
+
resolution: "resolved",
|
|
37
|
+
evidence: inferredSemanticEvidenceAt(context.file, capture.referenceLocation),
|
|
38
|
+
});
|
|
39
|
+
};
|
|
40
|
+
/** Retain bounded unresolved dynamic-call and dynamic-property frontiers. */
|
|
41
|
+
export const projectSemanticFrontiers = (context) => {
|
|
42
|
+
for (const frontier of context.ir.frontiers) {
|
|
43
|
+
const unknown = createJavaScriptSemanticGraphUnknown({
|
|
44
|
+
node_id: frontier.callableId === null
|
|
45
|
+
? context.moduleNode.node_id
|
|
46
|
+
: (context.callableNodes.get(frontier.callableId)?.node_id ??
|
|
47
|
+
context.moduleNode.node_id),
|
|
48
|
+
family: frontier.kind === "dynamic-call" ? "call-flow" : "object-flow",
|
|
49
|
+
relation_kinds: frontier.kind === "dynamic-call"
|
|
50
|
+
? ["calls"]
|
|
51
|
+
: ["reads-property", "writes-property"],
|
|
52
|
+
reason: frontier.kind,
|
|
53
|
+
detail: frontier.reason,
|
|
54
|
+
candidate_node_ids: [],
|
|
55
|
+
evidence: unknownSemanticEvidence(context.file, frontier.location),
|
|
56
|
+
});
|
|
57
|
+
addSemanticGraphUnknown(context.state, unknown);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
/** Publish exact semantic analyzer limits reached by any admitted source file. */
|
|
61
|
+
export const semanticRecoveryLimits = (analysis) => {
|
|
62
|
+
const limits = new Map();
|
|
63
|
+
for (const { semantic } of analysis.files) {
|
|
64
|
+
if (semantic === null)
|
|
65
|
+
continue;
|
|
66
|
+
for (const name of semantic.ir.coverage.limitsReached)
|
|
67
|
+
limits.set(`semantic.${name}`, semantic.limits[name]);
|
|
68
|
+
}
|
|
69
|
+
return [...limits]
|
|
70
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
71
|
+
.map(([name, value]) => ({ name, value, unit: "items" }));
|
|
72
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { compareCodePoints } from "../domain/javascriptApplicationGraph.js";
|
|
2
|
+
import { JAVASCRIPT_SEMANTIC_RELATION_FAMILIES } from "../domain/javascriptSemanticGraphSchemas.js";
|
|
3
|
+
/** Find the innermost callable that contains one exact source range. */
|
|
4
|
+
export const owningSemanticCallableNode = (location, ir, nodes) => {
|
|
5
|
+
if (location === null)
|
|
6
|
+
return undefined;
|
|
7
|
+
return ir.callables
|
|
8
|
+
.filter((callable) => contains(callable.location, location))
|
|
9
|
+
.sort((left, right) => compareCodePoints(positionKey(right.location), positionKey(left.location)))
|
|
10
|
+
.map(({ callableId }) => nodes.get(callableId))
|
|
11
|
+
.find((node) => node !== undefined);
|
|
12
|
+
};
|
|
13
|
+
/** Find retained semantic nodes whose exact ranges are within one expression. */
|
|
14
|
+
export const semanticNodesWithinRange = (nodes, range) => nodes.filter((node) => node.identity.source_range !== null &&
|
|
15
|
+
contains(range, node.identity.source_range));
|
|
16
|
+
/** Report extractor support without treating missing families as absence. */
|
|
17
|
+
export const semanticFamilyStatus = (family, analysis, truncated) => {
|
|
18
|
+
if (!["call-flow", "closure", "data-flow", "object-flow"].includes(family))
|
|
19
|
+
return "unsupported";
|
|
20
|
+
if (truncated)
|
|
21
|
+
return "unknown";
|
|
22
|
+
return analysis.truncated_scopes === 0 ? "partial" : "unknown";
|
|
23
|
+
};
|
|
24
|
+
/** Compare two exact semantic source ranges. */
|
|
25
|
+
export const semanticRangesEqual = (left, right) => left !== null &&
|
|
26
|
+
left.start.line === right.start.line &&
|
|
27
|
+
left.start.column === right.start.column &&
|
|
28
|
+
left.end.line === right.end.line &&
|
|
29
|
+
left.end.column === right.end.column;
|
|
30
|
+
const contains = (outer, inner) => pointCompare(outer.start, inner.start) <= 0 &&
|
|
31
|
+
pointCompare(outer.end, inner.end) >= 0;
|
|
32
|
+
const pointCompare = (left, right) => left.line - right.line || left.column - right.column;
|
|
33
|
+
const positionKey = (range) => `${String(range.start.line).padStart(12, "0")}:${String(range.start.column).padStart(12, "0")}`;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { AnalysisInputError, AnalysisProtocolError, } from "../domain/errors.js";
|
|
3
|
+
import { jsonValueSchema } from "../domain/jsonValue.js";
|
|
4
|
+
import { queryJavaScriptSemanticGraph } from "../domain/javascriptSemanticQuery.js";
|
|
5
|
+
import { JavaScriptSemanticQueryCursorError } from "../domain/javascriptSemanticQueryIdentity.js";
|
|
6
|
+
import { javaScriptSemanticTraceResultSchema, traceJavaScriptSemanticsInputSchema, } from "../domain/javascriptSemanticTraceSchemas.js";
|
|
7
|
+
import { err, ok } from "../domain/result.js";
|
|
8
|
+
import { parseApplicationGraphEvidence } from "./JavaScriptApplicationEvidenceGraph.js";
|
|
9
|
+
import { createJavaScriptSemanticTraceEvidence } from "./JavaScriptApplicationWorkflowEvidence.js";
|
|
10
|
+
const OPERATION = "trace_javascript_semantics";
|
|
11
|
+
/** Trace semantic relations from input already parsed by a trusted adapter. */
|
|
12
|
+
export const traceJavaScriptSemanticsEvidenceValidated = (input) => {
|
|
13
|
+
try {
|
|
14
|
+
const source = parseApplicationGraphEvidence(input.application);
|
|
15
|
+
if (source.semanticGraph === null)
|
|
16
|
+
return err(new AnalysisInputError(OPERATION, undefined, [
|
|
17
|
+
{
|
|
18
|
+
path: ["application"],
|
|
19
|
+
reason: "invalid_value",
|
|
20
|
+
expected: "analyze_javascript_application v2 Evidence; reanalyze the artifact with the current REA version",
|
|
21
|
+
},
|
|
22
|
+
]));
|
|
23
|
+
const query = queryJavaScriptSemanticGraph(source.semanticGraph, input.query);
|
|
24
|
+
const result = javaScriptSemanticTraceResultSchema.parse({
|
|
25
|
+
...query,
|
|
26
|
+
source_evidence_id: source.evidence.evidence_id,
|
|
27
|
+
evidence_links: [source.evidence.evidence_id],
|
|
28
|
+
});
|
|
29
|
+
return ok(createJavaScriptSemanticTraceEvidence({
|
|
30
|
+
application_evidence_id: source.evidence.evidence_id,
|
|
31
|
+
query: jsonValueSchema.parse(input.query),
|
|
32
|
+
}, result));
|
|
33
|
+
}
|
|
34
|
+
catch (cause) {
|
|
35
|
+
if (cause instanceof JavaScriptSemanticQueryCursorError)
|
|
36
|
+
return err(new AnalysisInputError(OPERATION, undefined, [
|
|
37
|
+
{
|
|
38
|
+
path: ["query", "cursor"],
|
|
39
|
+
reason: "invalid_value",
|
|
40
|
+
expected: "a cursor returned by the same graph, query, and limits",
|
|
41
|
+
},
|
|
42
|
+
]));
|
|
43
|
+
return err(new AnalysisProtocolError("JavaScript semantic trace failed", { cause }));
|
|
44
|
+
}
|
|
45
|
+
};
|