rea-agents 2.4.0 → 2.5.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 +80 -18
- package/bridge/ghidra/ReaGhidraBridge.java +48 -1
- package/bridge/hopper_bridge.py +259 -24
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/BinarySession.js +15 -4
- package/dist/application/BinarySessionStatus.js +31 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +68 -12
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +7 -2
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +35 -121
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/ProcessReactiveEffects.js +6 -0
- package/dist/application/ProcessReactiveHarness.js +16 -1
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +253 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +8 -0
- package/dist/cli/artifactCommands.js +39 -0
- package/dist/cli/coreAnalysisCommands.js +37 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +8 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +22 -0
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +25 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +5 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +13 -21
- package/dist/contracts/toolEffects.js +21 -0
- package/dist/contracts/toolOutputSchemaGroups.js +70 -20
- package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
- package/dist/domain/analysisErrorProjection.js +8 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -0
- package/dist/domain/hopperValues.js +10 -0
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +5 -4
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptStaticAnalysis.js +4 -3
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- package/dist/domain/processCapture.js +8 -2
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +9 -4
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- package/dist/generatedPackageMetadata.js +1 -1
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +13 -1
- package/dist/ghidra/GhidraProviderCapabilities.js +7 -0
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +69 -99
- package/dist/hopper/HopperProvider.js +20 -1
- package/dist/hopper/HopperRequestQueue.js +187 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +1 -0
- package/dist/main/transport.js +24 -24
- package/dist/main.js +6 -0
- package/dist/mcpDoctor.js +25 -2
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/createServer.js +46 -1
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +16 -5
- package/dist/server/registerEvidenceResources.js +4 -0
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +39 -0
- package/dist/server/registerReconstructionReadinessResource.js +39 -0
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +11 -57
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/package.json +12 -6
- package/skills/reverse-engineer-anything/SKILL.md +2 -2
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createJavaScriptSemanticGraphUnknown } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
+
import { addSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, constructSemanticGraphNode, } from "./JavaScriptSemanticGraphConstruction.js";
|
|
3
|
+
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
|
4
|
+
import { semanticCallSiteAt, } from "./JavaScriptSemanticGraphFlowProjection.js";
|
|
5
|
+
/** Project child creation, argv/env/stdio, listeners, and signals. */
|
|
6
|
+
export const projectSemanticChildProcesses = (context) => {
|
|
7
|
+
const processNodes = new Map(context.ir.childProcessSpawns.flatMap((spawn) => {
|
|
8
|
+
const node = addChildProcessNode(context, spawn);
|
|
9
|
+
return node === null ? [] : [[spawn.processId, node]];
|
|
10
|
+
}));
|
|
11
|
+
for (const spawn of context.ir.childProcessSpawns) {
|
|
12
|
+
const child = processNodes.get(spawn.processId);
|
|
13
|
+
if (child === undefined)
|
|
14
|
+
continue;
|
|
15
|
+
projectSpawn(context, spawn, child);
|
|
16
|
+
}
|
|
17
|
+
for (const interaction of context.ir.childProcessInteractions)
|
|
18
|
+
projectInteraction(context, interaction, processNodes);
|
|
19
|
+
};
|
|
20
|
+
const addChildProcessNode = (context, spawn) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
21
|
+
kind: "child-process",
|
|
22
|
+
roleKey: spawn.processId,
|
|
23
|
+
location: spawn.location,
|
|
24
|
+
label: spawn.command ?? spawn.method,
|
|
25
|
+
functionNodeId: spawn.ownerCallableId === null
|
|
26
|
+
? null
|
|
27
|
+
: (context.callableNodes.get(spawn.ownerCallableId)?.node_id ??
|
|
28
|
+
null),
|
|
29
|
+
properties: {
|
|
30
|
+
argv_count: spawn.argvCount,
|
|
31
|
+
command: spawn.command,
|
|
32
|
+
environment_supplied: spawn.environmentSupplied,
|
|
33
|
+
method: spawn.method,
|
|
34
|
+
stdio_mode: spawn.stdioMode,
|
|
35
|
+
},
|
|
36
|
+
}, context.state));
|
|
37
|
+
const projectSpawn = (context, spawn, child) => {
|
|
38
|
+
const callSite = semanticCallSiteAt(context, spawn.location);
|
|
39
|
+
addSemanticGraphRelation(context.state, {
|
|
40
|
+
source: callSite,
|
|
41
|
+
target: child,
|
|
42
|
+
relation: "spawns",
|
|
43
|
+
resolution: spawn.resolution === "complete" ? "resolved" : "candidate",
|
|
44
|
+
});
|
|
45
|
+
if (spawn.argvCount !== null)
|
|
46
|
+
addSemanticGraphRelation(context.state, {
|
|
47
|
+
source: callSite,
|
|
48
|
+
target: child,
|
|
49
|
+
relation: "supplies-argv",
|
|
50
|
+
resolution: "resolved",
|
|
51
|
+
properties: { argument_count: spawn.argvCount },
|
|
52
|
+
});
|
|
53
|
+
if (spawn.environmentSupplied)
|
|
54
|
+
addSemanticGraphRelation(context.state, {
|
|
55
|
+
source: callSite,
|
|
56
|
+
target: child,
|
|
57
|
+
relation: "supplies-env",
|
|
58
|
+
resolution: "resolved",
|
|
59
|
+
});
|
|
60
|
+
const stdio = addStdioNode(context, spawn, child);
|
|
61
|
+
addSemanticGraphRelation(context.state, {
|
|
62
|
+
source: child,
|
|
63
|
+
target: stdio,
|
|
64
|
+
relation: "connects-stdio",
|
|
65
|
+
resolution: spawn.stdioMode === "dynamic" ? "candidate" : "resolved",
|
|
66
|
+
});
|
|
67
|
+
if (spawn.resolution !== "complete")
|
|
68
|
+
addChildUnknown({
|
|
69
|
+
context,
|
|
70
|
+
node: child,
|
|
71
|
+
location: spawn.location,
|
|
72
|
+
relationKinds: ["spawns"],
|
|
73
|
+
detail: `Static ${spawn.method} command remains unresolved.`,
|
|
74
|
+
candidateNodeIds: [],
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const addStdioNode = (context, spawn, child) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
78
|
+
kind: "stdio",
|
|
79
|
+
roleKey: `stdio:${spawn.processId}`,
|
|
80
|
+
location: spawn.location,
|
|
81
|
+
label: spawn.stdioMode,
|
|
82
|
+
functionNodeId: child.function_node_id,
|
|
83
|
+
properties: { mode: spawn.stdioMode },
|
|
84
|
+
}, context.state));
|
|
85
|
+
const projectInteraction = (context, interaction, processNodes) => {
|
|
86
|
+
const children = interaction.linkedProcessIds.flatMap((identifier) => {
|
|
87
|
+
const child = processNodes.get(identifier);
|
|
88
|
+
return child === undefined ? [] : [child];
|
|
89
|
+
});
|
|
90
|
+
const effect = interaction.kind === "listener"
|
|
91
|
+
? addChildListenerNode(context, interaction)
|
|
92
|
+
: addSignalNode(context, interaction);
|
|
93
|
+
for (const child of children)
|
|
94
|
+
addSemanticGraphRelation(context.state, {
|
|
95
|
+
source: interaction.kind === "listener" ? child : effect,
|
|
96
|
+
target: interaction.kind === "listener" ? effect : child,
|
|
97
|
+
relation: interaction.kind === "signal"
|
|
98
|
+
? "forwards-signal"
|
|
99
|
+
: interaction.eventName === "exit"
|
|
100
|
+
? "listens-exit"
|
|
101
|
+
: "listens-error",
|
|
102
|
+
resolution: interaction.resolution === "complete" ? "resolved" : "candidate",
|
|
103
|
+
});
|
|
104
|
+
if (interaction.resolution !== "complete")
|
|
105
|
+
addChildUnknown({
|
|
106
|
+
context,
|
|
107
|
+
node: effect,
|
|
108
|
+
location: interaction.location,
|
|
109
|
+
relationKinds: [
|
|
110
|
+
interaction.kind === "signal"
|
|
111
|
+
? "forwards-signal"
|
|
112
|
+
: interaction.eventName === "exit"
|
|
113
|
+
? "listens-exit"
|
|
114
|
+
: "listens-error",
|
|
115
|
+
],
|
|
116
|
+
detail: `Static child ${interaction.method} ownership is ambiguous.`,
|
|
117
|
+
candidateNodeIds: children.map(({ node_id: identifier }) => identifier),
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
const addChildListenerNode = (context, interaction) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
121
|
+
kind: "listener",
|
|
122
|
+
roleKey: interaction.interactionId,
|
|
123
|
+
location: interaction.listenerLocation ?? interaction.location,
|
|
124
|
+
label: interaction.eventName,
|
|
125
|
+
functionNodeId: interaction.ownerCallableId === null
|
|
126
|
+
? null
|
|
127
|
+
: (context.callableNodes.get(interaction.ownerCallableId)
|
|
128
|
+
?.node_id ?? null),
|
|
129
|
+
properties: {
|
|
130
|
+
event_name: interaction.eventName,
|
|
131
|
+
method: interaction.method,
|
|
132
|
+
},
|
|
133
|
+
}, context.state));
|
|
134
|
+
const addSignalNode = (context, interaction) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
135
|
+
kind: "signal",
|
|
136
|
+
roleKey: interaction.interactionId,
|
|
137
|
+
location: interaction.location,
|
|
138
|
+
label: interaction.signalName,
|
|
139
|
+
functionNodeId: interaction.ownerCallableId === null
|
|
140
|
+
? null
|
|
141
|
+
: (context.callableNodes.get(interaction.ownerCallableId)
|
|
142
|
+
?.node_id ?? null),
|
|
143
|
+
properties: { signal_name: interaction.signalName },
|
|
144
|
+
}, context.state));
|
|
145
|
+
const addChildUnknown = (input) => {
|
|
146
|
+
const { context, node, location, relationKinds, detail, candidateNodeIds } = input;
|
|
147
|
+
addSemanticGraphUnknown(context.state, createJavaScriptSemanticGraphUnknown({
|
|
148
|
+
node_id: node?.node_id ?? null,
|
|
149
|
+
family: "child-process",
|
|
150
|
+
relation_kinds: relationKinds,
|
|
151
|
+
reason: "ambiguous-target",
|
|
152
|
+
detail,
|
|
153
|
+
candidate_node_ids: candidateNodeIds,
|
|
154
|
+
evidence: unknownSemanticEvidence(context.file, location),
|
|
155
|
+
}));
|
|
156
|
+
};
|
|
@@ -32,7 +32,7 @@ export const constructSemanticGraphNode = (file, input, state) => createJavaScri
|
|
|
32
32
|
function_node_id: input.functionNodeId,
|
|
33
33
|
application_node_ids: matchingApplicationNodeIds(file, input, state),
|
|
34
34
|
label: input.label,
|
|
35
|
-
properties: {},
|
|
35
|
+
properties: input.properties ?? {},
|
|
36
36
|
evidence: observedSemanticEvidence(file, input.location),
|
|
37
37
|
});
|
|
38
38
|
/** Retain one canonical node unless the hard graph bound was reached. */
|
|
@@ -60,7 +60,7 @@ export const addSemanticGraphRelation = (state, input) => {
|
|
|
60
60
|
target_node_id: input.target.node_id,
|
|
61
61
|
relation: input.relation,
|
|
62
62
|
resolution: input.resolution ?? "candidate",
|
|
63
|
-
properties: {},
|
|
63
|
+
properties: input.properties ?? {},
|
|
64
64
|
evidence: input.evidence ?? inferredSemanticEvidence(input.source),
|
|
65
65
|
});
|
|
66
66
|
if (state.relations.has(value.relation_id))
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { createJavaScriptSemanticGraphUnknown } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
+
import { addSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, constructSemanticGraphNode, } from "./JavaScriptSemanticGraphConstruction.js";
|
|
3
|
+
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
|
4
|
+
import { semanticCallSiteAt, } from "./JavaScriptSemanticGraphFlowProjection.js";
|
|
5
|
+
/** Project environment, argv, file, default, and precedence candidates. */
|
|
6
|
+
export const projectSemanticConfiguration = (context) => {
|
|
7
|
+
const configNodes = new Map(context.ir.configurationOperations.flatMap((operation) => {
|
|
8
|
+
const node = addConfigurationNode(context, operation);
|
|
9
|
+
return node === null ? [] : [[operation.configId, node]];
|
|
10
|
+
}));
|
|
11
|
+
for (const operation of context.ir.configurationOperations) {
|
|
12
|
+
const config = configNodes.get(operation.configId);
|
|
13
|
+
if (config === undefined)
|
|
14
|
+
continue;
|
|
15
|
+
const target = bindingOrOwner(context, operation.resultBindingId, operation.ownerCallableId);
|
|
16
|
+
addSemanticGraphRelation(context.state, {
|
|
17
|
+
source: config,
|
|
18
|
+
target,
|
|
19
|
+
relation: configurationRelation(operation),
|
|
20
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
21
|
+
});
|
|
22
|
+
if (operation.kind === "default" && operation.sourceConfigId !== null)
|
|
23
|
+
addSemanticGraphRelation(context.state, {
|
|
24
|
+
source: configNodes.get(operation.sourceConfigId),
|
|
25
|
+
target: config,
|
|
26
|
+
relation: "overrides",
|
|
27
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
28
|
+
});
|
|
29
|
+
if (operation.resolution !== "complete")
|
|
30
|
+
addDataUnknown({
|
|
31
|
+
context,
|
|
32
|
+
node: config,
|
|
33
|
+
family: "configuration",
|
|
34
|
+
relationKinds: [
|
|
35
|
+
configurationRelation(operation),
|
|
36
|
+
...(operation.kind === "default" ? ["overrides"] : []),
|
|
37
|
+
],
|
|
38
|
+
location: operation.location,
|
|
39
|
+
detail: `Static ${operation.kind} configuration identity is partial.`,
|
|
40
|
+
candidateNodeIds: [],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const addConfigurationNode = (context, operation) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
45
|
+
kind: "config-source",
|
|
46
|
+
roleKey: operation.configId,
|
|
47
|
+
location: operation.location,
|
|
48
|
+
label: operation.key ?? operation.kind,
|
|
49
|
+
functionNodeId: operation.ownerCallableId === null
|
|
50
|
+
? null
|
|
51
|
+
: (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
|
|
52
|
+
null),
|
|
53
|
+
properties: {
|
|
54
|
+
key: operation.key,
|
|
55
|
+
source_kind: operation.kind,
|
|
56
|
+
value: operation.value,
|
|
57
|
+
},
|
|
58
|
+
}, context.state));
|
|
59
|
+
const configurationRelation = (operation) => operation.kind === "environment"
|
|
60
|
+
? "reads-environment"
|
|
61
|
+
: operation.kind === "argv"
|
|
62
|
+
? "reads-argv"
|
|
63
|
+
: operation.kind === "file"
|
|
64
|
+
? "reads-config"
|
|
65
|
+
: "defaults";
|
|
66
|
+
/** Project request construction, field provenance, and response consumers. */
|
|
67
|
+
export const projectSemanticRequests = (context) => {
|
|
68
|
+
const requestNodes = new Map(context.ir.requestOperations.flatMap((operation) => {
|
|
69
|
+
const node = addRequestNode(context, operation);
|
|
70
|
+
return node === null ? [] : [[operation.requestId, node]];
|
|
71
|
+
}));
|
|
72
|
+
for (const operation of context.ir.requestOperations) {
|
|
73
|
+
const request = requestNodes.get(operation.requestId);
|
|
74
|
+
if (request === undefined)
|
|
75
|
+
continue;
|
|
76
|
+
if (operation.kind === "request")
|
|
77
|
+
projectRequestConstruction(context, operation, request);
|
|
78
|
+
else
|
|
79
|
+
projectResponseConsumer(context, operation, request);
|
|
80
|
+
if (operation.resolution !== "complete")
|
|
81
|
+
addDataUnknown({
|
|
82
|
+
context,
|
|
83
|
+
node: request,
|
|
84
|
+
family: "request",
|
|
85
|
+
relationKinds: operation.kind === "request"
|
|
86
|
+
? ["constructs-request", "supplies-request-field"]
|
|
87
|
+
: ["consumed-by"],
|
|
88
|
+
location: operation.location,
|
|
89
|
+
detail: `Static ${operation.method} request resolution was ${operation.resolution}.`,
|
|
90
|
+
candidateNodeIds: operation.linkedRequestIds.flatMap((identifier) => {
|
|
91
|
+
const candidate = requestNodes.get(identifier);
|
|
92
|
+
return candidate === undefined ? [] : [candidate.node_id];
|
|
93
|
+
}),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const addRequestNode = (context, operation) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
98
|
+
kind: operation.kind === "request" ? "request" : "response",
|
|
99
|
+
roleKey: operation.requestId,
|
|
100
|
+
location: operation.location,
|
|
101
|
+
label: operation.endpoint ?? operation.method,
|
|
102
|
+
functionNodeId: operation.ownerCallableId === null
|
|
103
|
+
? null
|
|
104
|
+
: (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
|
|
105
|
+
null),
|
|
106
|
+
properties: {
|
|
107
|
+
endpoint: operation.endpoint,
|
|
108
|
+
method: operation.method,
|
|
109
|
+
operation_kind: operation.kind,
|
|
110
|
+
},
|
|
111
|
+
}, context.state));
|
|
112
|
+
const projectRequestConstruction = (context, operation, request) => {
|
|
113
|
+
const callSite = semanticCallSiteAt(context, operation.location);
|
|
114
|
+
addSemanticGraphRelation(context.state, {
|
|
115
|
+
source: callSite,
|
|
116
|
+
target: request,
|
|
117
|
+
relation: "constructs-request",
|
|
118
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
119
|
+
});
|
|
120
|
+
for (const field of operation.fields)
|
|
121
|
+
addSemanticGraphRelation(context.state, {
|
|
122
|
+
source: field.sourceBindingId === null
|
|
123
|
+
? callSite
|
|
124
|
+
: context.bindingNodes.get(field.sourceBindingId),
|
|
125
|
+
target: request,
|
|
126
|
+
relation: "supplies-request-field",
|
|
127
|
+
resolution: field.sourceBindingId === null ? "candidate" : "resolved",
|
|
128
|
+
properties: { field: field.name },
|
|
129
|
+
});
|
|
130
|
+
};
|
|
131
|
+
const projectResponseConsumer = (context, operation, response) => {
|
|
132
|
+
addSemanticGraphRelation(context.state, {
|
|
133
|
+
source: response,
|
|
134
|
+
target: semanticCallSiteAt(context, operation.location),
|
|
135
|
+
relation: "consumed-by",
|
|
136
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
137
|
+
properties: { method: operation.method },
|
|
138
|
+
});
|
|
139
|
+
};
|
|
140
|
+
/** Project parse, coercion, and validation consumers as static boundaries. */
|
|
141
|
+
export const projectSemanticBoundaries = (context) => {
|
|
142
|
+
for (const operation of context.ir.boundaryOperations) {
|
|
143
|
+
const boundary = addBoundaryNode(context, operation);
|
|
144
|
+
if (boundary === null)
|
|
145
|
+
continue;
|
|
146
|
+
addSemanticGraphRelation(context.state, {
|
|
147
|
+
source: bindingOrOwner(context, operation.sourceBindingId, operation.ownerCallableId),
|
|
148
|
+
target: boundary,
|
|
149
|
+
relation: operation.kind === "parse"
|
|
150
|
+
? "parses"
|
|
151
|
+
: operation.kind === "coerce"
|
|
152
|
+
? "coerces"
|
|
153
|
+
: "validates",
|
|
154
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
155
|
+
});
|
|
156
|
+
if (operation.resolution !== "complete")
|
|
157
|
+
addDataUnknown({
|
|
158
|
+
context,
|
|
159
|
+
node: boundary,
|
|
160
|
+
family: "boundary",
|
|
161
|
+
relationKinds: [
|
|
162
|
+
operation.kind === "parse"
|
|
163
|
+
? "parses"
|
|
164
|
+
: operation.kind === "coerce"
|
|
165
|
+
? "coerces"
|
|
166
|
+
: "validates",
|
|
167
|
+
],
|
|
168
|
+
location: operation.location,
|
|
169
|
+
detail: `Static ${operation.method} boundary kind is a candidate.`,
|
|
170
|
+
candidateNodeIds: [],
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
const addBoundaryNode = (context, operation) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
175
|
+
kind: "boundary",
|
|
176
|
+
roleKey: operation.boundaryId,
|
|
177
|
+
location: operation.location,
|
|
178
|
+
label: operation.method,
|
|
179
|
+
functionNodeId: operation.ownerCallableId === null
|
|
180
|
+
? null
|
|
181
|
+
: (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
|
|
182
|
+
null),
|
|
183
|
+
properties: {
|
|
184
|
+
boundary_kind: operation.kind,
|
|
185
|
+
method: operation.method,
|
|
186
|
+
},
|
|
187
|
+
}, context.state));
|
|
188
|
+
const bindingOrOwner = (context, bindingId, callableId) => (bindingId === null ? undefined : context.bindingNodes.get(bindingId)) ??
|
|
189
|
+
(callableId === null
|
|
190
|
+
? context.moduleNode
|
|
191
|
+
: (context.callableNodes.get(callableId) ?? context.moduleNode));
|
|
192
|
+
const addDataUnknown = (input) => {
|
|
193
|
+
const { context, node, family, relationKinds, location, detail, candidateNodeIds, } = input;
|
|
194
|
+
addSemanticGraphUnknown(context.state, createJavaScriptSemanticGraphUnknown({
|
|
195
|
+
node_id: node.node_id,
|
|
196
|
+
family,
|
|
197
|
+
relation_kinds: relationKinds,
|
|
198
|
+
reason: "ambiguous-target",
|
|
199
|
+
detail,
|
|
200
|
+
candidate_node_ids: candidateNodeIds,
|
|
201
|
+
evidence: unknownSemanticEvidence(context.file, location),
|
|
202
|
+
}));
|
|
203
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { createJavaScriptSemanticFingerprint, } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
+
import { observedSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
|
3
|
+
/** Bind domain fingerprint components to exact semantic function nodes. */
|
|
4
|
+
export const projectSemanticFunctionFingerprints = (file, ir, callableNodes) => ir.functionFingerprints.flatMap((fingerprint) => {
|
|
5
|
+
const callable = callableNodes.get(fingerprint.callableId);
|
|
6
|
+
if (callable === undefined)
|
|
7
|
+
return [];
|
|
8
|
+
return [
|
|
9
|
+
createJavaScriptSemanticFingerprint({
|
|
10
|
+
function_node_id: callable.node_id,
|
|
11
|
+
algorithm: "rea.javascript-semantic-function/v1",
|
|
12
|
+
status: fingerprint.status,
|
|
13
|
+
components: {
|
|
14
|
+
parameter_arity: fingerprint.components.parameterArity,
|
|
15
|
+
normalized_ast_sha256: fingerprint.components.normalizedAstSha256,
|
|
16
|
+
control_flow_sha256: fingerprint.components.controlFlowSha256,
|
|
17
|
+
relation_shape_sha256: fingerprint.components.relationShapeSha256,
|
|
18
|
+
literal_set_sha256: fingerprint.components.literalSetSha256,
|
|
19
|
+
effects: fingerprint.components.effects,
|
|
20
|
+
},
|
|
21
|
+
limitations: fingerprint.limitations,
|
|
22
|
+
evidence: observedSemanticEvidence(file, callable.identity.source_range),
|
|
23
|
+
}),
|
|
24
|
+
];
|
|
25
|
+
});
|
|
@@ -1,7 +1,134 @@
|
|
|
1
1
|
import { createJavaScriptSemanticGraphUnknown } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
-
import {
|
|
2
|
+
import { sourceRangesEqual } from "../domain/javascriptStaticAnalysisHelpers.js";
|
|
3
|
+
import { addSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, constructSemanticGraphNode, } from "./JavaScriptSemanticGraphConstruction.js";
|
|
3
4
|
import { inferredSemanticEvidenceAt, unknownSemanticEvidence, } from "./JavaScriptSemanticGraphEvidence.js";
|
|
4
5
|
import { semanticNodesWithinRange } from "./JavaScriptSemanticGraphProjection.js";
|
|
6
|
+
/** Find the retained call-site node at one exact semantic source range. */
|
|
7
|
+
export const semanticCallSiteAt = (context, location) => {
|
|
8
|
+
const call = context.ir.callSites.find(({ location: candidate }) => sourceRangesEqual(candidate, location));
|
|
9
|
+
return call === undefined
|
|
10
|
+
? undefined
|
|
11
|
+
: context.callSiteNodes.get(call.callSiteId);
|
|
12
|
+
};
|
|
13
|
+
/** Project explicit static Promise/task ownership without runtime claims. */
|
|
14
|
+
export const projectSemanticPromises = (context) => {
|
|
15
|
+
const promiseNodes = new Map(context.ir.promiseOperations.flatMap((operation) => {
|
|
16
|
+
const owner = operation.ownerCallableId === null
|
|
17
|
+
? undefined
|
|
18
|
+
: context.callableNodes.get(operation.ownerCallableId);
|
|
19
|
+
const node = addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
20
|
+
kind: operation.kind === "awaited-expression" ? "task" : "promise",
|
|
21
|
+
roleKey: operation.promiseId,
|
|
22
|
+
location: operation.location,
|
|
23
|
+
label: promiseLabel(operation),
|
|
24
|
+
functionNodeId: owner?.node_id ?? null,
|
|
25
|
+
properties: {
|
|
26
|
+
method: operation.method,
|
|
27
|
+
operation_kind: operation.kind,
|
|
28
|
+
ownership: operation.ownership,
|
|
29
|
+
source_resolution: operation.sourceResolution,
|
|
30
|
+
},
|
|
31
|
+
}, context.state));
|
|
32
|
+
return node === null ? [] : [[operation.promiseId, node]];
|
|
33
|
+
}));
|
|
34
|
+
for (const operation of context.ir.promiseOperations) {
|
|
35
|
+
const promise = promiseNodes.get(operation.promiseId);
|
|
36
|
+
if (promise === undefined)
|
|
37
|
+
continue;
|
|
38
|
+
projectPromiseCreation(context, operation, promise);
|
|
39
|
+
projectPromiseSources(context, operation, promise, promiseNodes);
|
|
40
|
+
projectPromiseOwnership(context, operation, promise);
|
|
41
|
+
if (operation.sourceResolution !== "complete" &&
|
|
42
|
+
["chain", "aggregate", "awaited-expression"].includes(operation.kind))
|
|
43
|
+
addPromiseUnknown(context, operation, promise, promiseNodes);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
const promiseLabel = (operation) => operation.kind === "awaited-expression"
|
|
47
|
+
? "awaited expression"
|
|
48
|
+
: `Promise.${operation.method}`;
|
|
49
|
+
const projectPromiseCreation = (context, operation, promise) => {
|
|
50
|
+
if (operation.kind === "awaited-expression")
|
|
51
|
+
return;
|
|
52
|
+
addSemanticGraphRelation(context.state, {
|
|
53
|
+
source: semanticCallSiteAt(context, operation.location),
|
|
54
|
+
target: promise,
|
|
55
|
+
relation: "creates-promise",
|
|
56
|
+
resolution: operation.kind === "chain" || operation.sourceResolution !== "complete"
|
|
57
|
+
? "candidate"
|
|
58
|
+
: "resolved",
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const projectPromiseSources = (context, operation, promise, promiseNodes) => {
|
|
62
|
+
for (const sourceId of operation.sourcePromiseIds) {
|
|
63
|
+
const source = promiseNodes.get(sourceId);
|
|
64
|
+
if (operation.kind === "aggregate")
|
|
65
|
+
addSemanticGraphRelation(context.state, {
|
|
66
|
+
source: promise,
|
|
67
|
+
target: source,
|
|
68
|
+
relation: "aggregates",
|
|
69
|
+
resolution: operation.sourceResolution === "complete" ? "resolved" : "candidate",
|
|
70
|
+
});
|
|
71
|
+
else
|
|
72
|
+
addSemanticGraphRelation(context.state, {
|
|
73
|
+
source,
|
|
74
|
+
target: promise,
|
|
75
|
+
relation: "chains",
|
|
76
|
+
resolution: operation.sourceResolution === "complete" ? "resolved" : "candidate",
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
const projectPromiseOwnership = (context, operation, promise) => {
|
|
81
|
+
const owner = operation.ownerCallableId === null
|
|
82
|
+
? context.moduleNode
|
|
83
|
+
: context.callableNodes.get(operation.ownerCallableId);
|
|
84
|
+
if (operation.ownership === "awaited")
|
|
85
|
+
addSemanticGraphRelation(context.state, {
|
|
86
|
+
source: owner,
|
|
87
|
+
target: promise,
|
|
88
|
+
relation: "awaits",
|
|
89
|
+
resolution: "resolved",
|
|
90
|
+
});
|
|
91
|
+
else if (operation.ownership === "assigned")
|
|
92
|
+
addSemanticGraphRelation(context.state, {
|
|
93
|
+
source: operation.ownerBindingId === null
|
|
94
|
+
? undefined
|
|
95
|
+
: context.bindingNodes.get(operation.ownerBindingId),
|
|
96
|
+
target: promise,
|
|
97
|
+
relation: "owns",
|
|
98
|
+
resolution: "resolved",
|
|
99
|
+
});
|
|
100
|
+
else if (operation.ownership === "returned")
|
|
101
|
+
addSemanticGraphRelation(context.state, {
|
|
102
|
+
source: operation.returnSiteId === null
|
|
103
|
+
? undefined
|
|
104
|
+
: context.returnSiteNodes.get(operation.returnSiteId),
|
|
105
|
+
target: promise,
|
|
106
|
+
relation: "returns-task",
|
|
107
|
+
resolution: "resolved",
|
|
108
|
+
});
|
|
109
|
+
else if (operation.ownership === "detached")
|
|
110
|
+
addSemanticGraphRelation(context.state, {
|
|
111
|
+
source: owner,
|
|
112
|
+
target: promise,
|
|
113
|
+
relation: "detaches-task",
|
|
114
|
+
resolution: "resolved",
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
const addPromiseUnknown = (context, operation, promise, promiseNodes) => {
|
|
118
|
+
const relation = operation.kind === "aggregate" ? "aggregates" : "chains";
|
|
119
|
+
addSemanticGraphUnknown(context.state, createJavaScriptSemanticGraphUnknown({
|
|
120
|
+
node_id: promise.node_id,
|
|
121
|
+
family: "promise-ownership",
|
|
122
|
+
relation_kinds: [relation],
|
|
123
|
+
reason: "ambiguous-target",
|
|
124
|
+
detail: `Static ${operation.method} source resolution was ${operation.sourceResolution}.`,
|
|
125
|
+
candidate_node_ids: operation.sourcePromiseIds.flatMap((identifier) => {
|
|
126
|
+
const candidate = promiseNodes.get(identifier);
|
|
127
|
+
return candidate === undefined ? [] : [candidate.node_id];
|
|
128
|
+
}),
|
|
129
|
+
evidence: unknownSemanticEvidence(context.file, operation.location),
|
|
130
|
+
}));
|
|
131
|
+
};
|
|
5
132
|
/** Link direct return expressions to their retained semantic references. */
|
|
6
133
|
export const projectSemanticReturnValues = (context) => {
|
|
7
134
|
for (const callable of context.ir.callables)
|
|
@@ -14,18 +141,12 @@ export const projectSemanticReturnValues = (context) => {
|
|
|
14
141
|
target: returnNode,
|
|
15
142
|
relation: "aliases",
|
|
16
143
|
resolution: site.identityReferenceLocation !== null &&
|
|
17
|
-
|
|
144
|
+
sourceRangesEqual(reference.identity.source_range, site.identityReferenceLocation)
|
|
18
145
|
? "resolved"
|
|
19
146
|
: "candidate",
|
|
20
147
|
});
|
|
21
148
|
}
|
|
22
149
|
};
|
|
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
150
|
/** Link each captured binding to the callable at the exact reference site. */
|
|
30
151
|
export const projectSemanticClosureCaptures = (context) => {
|
|
31
152
|
for (const capture of context.ir.closureCaptures)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createJavaScriptSemanticGraphUnknown } from "../domain/javascriptSemanticGraph.js";
|
|
2
|
+
import { addSemanticGraphNode, addSemanticGraphRelation, addSemanticGraphUnknown, constructSemanticGraphNode, } from "./JavaScriptSemanticGraphConstruction.js";
|
|
3
|
+
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
|
4
|
+
import { semanticPropertySlot } from "./JavaScriptSemanticGraphValueProjection.js";
|
|
5
|
+
/** Project static object reads, writes, spreads, and destructuring. */
|
|
6
|
+
export const projectSemanticObjects = (context) => {
|
|
7
|
+
for (const operation of context.ir.objectOperations) {
|
|
8
|
+
const occurrence = addObjectOccurrence(context, operation);
|
|
9
|
+
const slot = operation.objectBindingId === null || operation.propertyName === null
|
|
10
|
+
? null
|
|
11
|
+
: semanticPropertySlot(context, operation.objectBindingId, operation.propertyName);
|
|
12
|
+
if (operation.kind === "read")
|
|
13
|
+
addSemanticGraphRelation(context.state, {
|
|
14
|
+
source: slot,
|
|
15
|
+
target: occurrence,
|
|
16
|
+
relation: "reads-property",
|
|
17
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
18
|
+
});
|
|
19
|
+
else if (operation.kind === "write")
|
|
20
|
+
addSemanticGraphRelation(context.state, {
|
|
21
|
+
source: occurrence,
|
|
22
|
+
target: slot,
|
|
23
|
+
relation: "writes-property",
|
|
24
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
25
|
+
});
|
|
26
|
+
else if (operation.kind === "destructure")
|
|
27
|
+
addSemanticGraphRelation(context.state, {
|
|
28
|
+
source: slot,
|
|
29
|
+
target: operation.targetBindingId === null
|
|
30
|
+
? undefined
|
|
31
|
+
: context.bindingNodes.get(operation.targetBindingId),
|
|
32
|
+
relation: "destructures",
|
|
33
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
34
|
+
});
|
|
35
|
+
else
|
|
36
|
+
addSemanticGraphRelation(context.state, {
|
|
37
|
+
source: operation.objectBindingId === null
|
|
38
|
+
? undefined
|
|
39
|
+
: context.bindingNodes.get(operation.objectBindingId),
|
|
40
|
+
target: occurrence,
|
|
41
|
+
relation: "spreads",
|
|
42
|
+
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
|
43
|
+
});
|
|
44
|
+
if (operation.resolution !== "complete")
|
|
45
|
+
addObjectUnknown(context, operation, slot ?? occurrence);
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const addObjectOccurrence = (context, operation) => addSemanticGraphNode(context.state, constructSemanticGraphNode(context.file, {
|
|
49
|
+
kind: "expression",
|
|
50
|
+
roleKey: operation.objectOperationId,
|
|
51
|
+
location: operation.location,
|
|
52
|
+
label: operation.propertyName === null
|
|
53
|
+
? operation.kind
|
|
54
|
+
: `${operation.kind}:${operation.propertyName}`,
|
|
55
|
+
functionNodeId: operation.ownerCallableId === null
|
|
56
|
+
? null
|
|
57
|
+
: (context.callableNodes.get(operation.ownerCallableId)?.node_id ??
|
|
58
|
+
null),
|
|
59
|
+
properties: {
|
|
60
|
+
operation_kind: operation.kind,
|
|
61
|
+
property_name: operation.propertyName,
|
|
62
|
+
},
|
|
63
|
+
}, context.state));
|
|
64
|
+
const addObjectUnknown = (context, operation, node) => {
|
|
65
|
+
const relation = operation.kind === "read"
|
|
66
|
+
? "reads-property"
|
|
67
|
+
: operation.kind === "write"
|
|
68
|
+
? "writes-property"
|
|
69
|
+
: operation.kind === "spread"
|
|
70
|
+
? "spreads"
|
|
71
|
+
: "destructures";
|
|
72
|
+
addSemanticGraphUnknown(context.state, createJavaScriptSemanticGraphUnknown({
|
|
73
|
+
node_id: node?.node_id ?? null,
|
|
74
|
+
family: "object-flow",
|
|
75
|
+
relation_kinds: [relation],
|
|
76
|
+
reason: "ambiguous-target",
|
|
77
|
+
detail: `Static ${operation.kind} object identity is partial.`,
|
|
78
|
+
candidate_node_ids: [],
|
|
79
|
+
evidence: unknownSemanticEvidence(context.file, operation.location),
|
|
80
|
+
}));
|
|
81
|
+
};
|
|
@@ -15,18 +15,25 @@ export const semanticNodesWithinRange = (nodes, range) => nodes.filter((node) =>
|
|
|
15
15
|
contains(range, node.identity.source_range));
|
|
16
16
|
/** Report extractor support without treating missing families as absence. */
|
|
17
17
|
export const semanticFamilyStatus = (family, analysis, truncated) => {
|
|
18
|
-
if (![
|
|
18
|
+
if (![
|
|
19
|
+
"call-flow",
|
|
20
|
+
"boundary",
|
|
21
|
+
"child-process",
|
|
22
|
+
"closure",
|
|
23
|
+
"configuration",
|
|
24
|
+
"data-flow",
|
|
25
|
+
"event",
|
|
26
|
+
"object-flow",
|
|
27
|
+
"promise-ownership",
|
|
28
|
+
"request",
|
|
29
|
+
"resource-lifecycle",
|
|
30
|
+
"timer",
|
|
31
|
+
].includes(family))
|
|
19
32
|
return "unsupported";
|
|
20
33
|
if (truncated)
|
|
21
34
|
return "unknown";
|
|
22
35
|
return analysis.truncated_scopes === 0 ? "partial" : "unknown";
|
|
23
36
|
};
|
|
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
37
|
const contains = (outer, inner) => pointCompare(outer.start, inner.start) <= 0 &&
|
|
31
38
|
pointCompare(outer.end, inner.end) >= 0;
|
|
32
39
|
const pointCompare = (left, right) => left.line - right.line || left.column - right.column;
|