@grafema/util 0.3.0-beta
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/LICENSE +190 -0
- package/dist/api/GraphAPI.d.ts +87 -0
- package/dist/api/GraphAPI.d.ts.map +1 -0
- package/dist/api/GraphAPI.js +212 -0
- package/dist/api/GraphAPI.js.map +1 -0
- package/dist/api/GuaranteeAPI.d.ts +147 -0
- package/dist/api/GuaranteeAPI.d.ts.map +1 -0
- package/dist/api/GuaranteeAPI.js +290 -0
- package/dist/api/GuaranteeAPI.js.map +1 -0
- package/dist/config/ConfigLoader.d.ts +214 -0
- package/dist/config/ConfigLoader.d.ts.map +1 -0
- package/dist/config/ConfigLoader.js +441 -0
- package/dist/config/ConfigLoader.js.map +1 -0
- package/dist/config/index.d.ts +6 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +5 -0
- package/dist/config/index.js.map +1 -0
- package/dist/core/CoverageAnalyzer.d.ts +65 -0
- package/dist/core/CoverageAnalyzer.d.ts.map +1 -0
- package/dist/core/CoverageAnalyzer.js +199 -0
- package/dist/core/CoverageAnalyzer.js.map +1 -0
- package/dist/core/FileExplainer.d.ts +101 -0
- package/dist/core/FileExplainer.d.ts.map +1 -0
- package/dist/core/FileExplainer.js +140 -0
- package/dist/core/FileExplainer.js.map +1 -0
- package/dist/core/FileOverview.d.ts +124 -0
- package/dist/core/FileOverview.d.ts.map +1 -0
- package/dist/core/FileOverview.js +279 -0
- package/dist/core/FileOverview.js.map +1 -0
- package/dist/core/GrafemaUri.d.ts +66 -0
- package/dist/core/GrafemaUri.d.ts.map +1 -0
- package/dist/core/GrafemaUri.js +191 -0
- package/dist/core/GrafemaUri.js.map +1 -0
- package/dist/core/GraphBackend.d.ts +158 -0
- package/dist/core/GraphBackend.d.ts.map +1 -0
- package/dist/core/GraphBackend.js +85 -0
- package/dist/core/GraphBackend.js.map +1 -0
- package/dist/core/GraphFreshnessChecker.d.ts +33 -0
- package/dist/core/GraphFreshnessChecker.d.ts.map +1 -0
- package/dist/core/GraphFreshnessChecker.js +104 -0
- package/dist/core/GraphFreshnessChecker.js.map +1 -0
- package/dist/core/GuaranteeManager.d.ts +254 -0
- package/dist/core/GuaranteeManager.d.ts.map +1 -0
- package/dist/core/GuaranteeManager.js +447 -0
- package/dist/core/GuaranteeManager.js.map +1 -0
- package/dist/core/HashUtils.d.ts +24 -0
- package/dist/core/HashUtils.d.ts.map +1 -0
- package/dist/core/HashUtils.js +46 -0
- package/dist/core/HashUtils.js.map +1 -0
- package/dist/core/IncrementalReanalyzer.d.ts +33 -0
- package/dist/core/IncrementalReanalyzer.d.ts.map +1 -0
- package/dist/core/IncrementalReanalyzer.js +67 -0
- package/dist/core/IncrementalReanalyzer.js.map +1 -0
- package/dist/core/ResourceRegistry.d.ts +17 -0
- package/dist/core/ResourceRegistry.d.ts.map +1 -0
- package/dist/core/ResourceRegistry.js +32 -0
- package/dist/core/ResourceRegistry.js.map +1 -0
- package/dist/core/SemanticId.d.ts +159 -0
- package/dist/core/SemanticId.d.ts.map +1 -0
- package/dist/core/SemanticId.js +291 -0
- package/dist/core/SemanticId.js.map +1 -0
- package/dist/core/VersionManager.d.ts +166 -0
- package/dist/core/VersionManager.d.ts.map +1 -0
- package/dist/core/VersionManager.js +239 -0
- package/dist/core/VersionManager.js.map +1 -0
- package/dist/core/brandNodeInternal.d.ts +14 -0
- package/dist/core/brandNodeInternal.d.ts.map +1 -0
- package/dist/core/brandNodeInternal.js +4 -0
- package/dist/core/brandNodeInternal.js.map +1 -0
- package/dist/core/nodes/GuaranteeNode.d.ts +76 -0
- package/dist/core/nodes/GuaranteeNode.d.ts.map +1 -0
- package/dist/core/nodes/GuaranteeNode.js +118 -0
- package/dist/core/nodes/GuaranteeNode.js.map +1 -0
- package/dist/core/nodes/IssueNode.d.ts +73 -0
- package/dist/core/nodes/IssueNode.d.ts.map +1 -0
- package/dist/core/nodes/IssueNode.js +130 -0
- package/dist/core/nodes/IssueNode.js.map +1 -0
- package/dist/core/nodes/NodeKind.d.ts +104 -0
- package/dist/core/nodes/NodeKind.d.ts.map +1 -0
- package/dist/core/nodes/NodeKind.js +166 -0
- package/dist/core/nodes/NodeKind.js.map +1 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts +103 -0
- package/dist/diagnostics/DiagnosticCollector.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticCollector.js +133 -0
- package/dist/diagnostics/DiagnosticCollector.js.map +1 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts +122 -0
- package/dist/diagnostics/DiagnosticReporter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticReporter.js +300 -0
- package/dist/diagnostics/DiagnosticReporter.js.map +1 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts +31 -0
- package/dist/diagnostics/DiagnosticWriter.d.ts.map +1 -0
- package/dist/diagnostics/DiagnosticWriter.js +44 -0
- package/dist/diagnostics/DiagnosticWriter.js.map +1 -0
- package/dist/diagnostics/categories.d.ts +57 -0
- package/dist/diagnostics/categories.d.ts.map +1 -0
- package/dist/diagnostics/categories.js +71 -0
- package/dist/diagnostics/categories.js.map +1 -0
- package/dist/diagnostics/index.d.ts +17 -0
- package/dist/diagnostics/index.d.ts.map +1 -0
- package/dist/diagnostics/index.js +15 -0
- package/dist/diagnostics/index.js.map +1 -0
- package/dist/errors/GrafemaError.d.ts +200 -0
- package/dist/errors/GrafemaError.d.ts.map +1 -0
- package/dist/errors/GrafemaError.js +209 -0
- package/dist/errors/GrafemaError.js.map +1 -0
- package/dist/index.d.ts +75 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +76 -0
- package/dist/index.js.map +1 -0
- package/dist/instructions/index.d.ts +8 -0
- package/dist/instructions/index.d.ts.map +1 -0
- package/dist/instructions/index.js +20 -0
- package/dist/instructions/index.js.map +1 -0
- package/dist/instructions/onboarding.md +133 -0
- package/dist/knowledge/KnowledgeBase.d.ts +113 -0
- package/dist/knowledge/KnowledgeBase.d.ts.map +1 -0
- package/dist/knowledge/KnowledgeBase.js +420 -0
- package/dist/knowledge/KnowledgeBase.js.map +1 -0
- package/dist/knowledge/SemanticAddressResolver.d.ts +59 -0
- package/dist/knowledge/SemanticAddressResolver.d.ts.map +1 -0
- package/dist/knowledge/SemanticAddressResolver.js +160 -0
- package/dist/knowledge/SemanticAddressResolver.js.map +1 -0
- package/dist/knowledge/git-ingest.d.ts +58 -0
- package/dist/knowledge/git-ingest.d.ts.map +1 -0
- package/dist/knowledge/git-ingest.js +301 -0
- package/dist/knowledge/git-ingest.js.map +1 -0
- package/dist/knowledge/git-queries.d.ts +86 -0
- package/dist/knowledge/git-queries.d.ts.map +1 -0
- package/dist/knowledge/git-queries.js +177 -0
- package/dist/knowledge/git-queries.js.map +1 -0
- package/dist/knowledge/index.d.ts +14 -0
- package/dist/knowledge/index.d.ts.map +1 -0
- package/dist/knowledge/index.js +10 -0
- package/dist/knowledge/index.js.map +1 -0
- package/dist/knowledge/parser.d.ts +39 -0
- package/dist/knowledge/parser.d.ts.map +1 -0
- package/dist/knowledge/parser.js +292 -0
- package/dist/knowledge/parser.js.map +1 -0
- package/dist/knowledge/types.d.ts +133 -0
- package/dist/knowledge/types.d.ts.map +1 -0
- package/dist/knowledge/types.js +8 -0
- package/dist/knowledge/types.js.map +1 -0
- package/dist/logging/Logger.d.ts +98 -0
- package/dist/logging/Logger.d.ts.map +1 -0
- package/dist/logging/Logger.js +274 -0
- package/dist/logging/Logger.js.map +1 -0
- package/dist/notation/archetypes.d.ts +36 -0
- package/dist/notation/archetypes.d.ts.map +1 -0
- package/dist/notation/archetypes.js +173 -0
- package/dist/notation/archetypes.js.map +1 -0
- package/dist/notation/fold.d.ts +25 -0
- package/dist/notation/fold.d.ts.map +1 -0
- package/dist/notation/fold.js +598 -0
- package/dist/notation/fold.js.map +1 -0
- package/dist/notation/index.d.ts +18 -0
- package/dist/notation/index.d.ts.map +1 -0
- package/dist/notation/index.js +16 -0
- package/dist/notation/index.js.map +1 -0
- package/dist/notation/lodExtractor.d.ts +32 -0
- package/dist/notation/lodExtractor.d.ts.map +1 -0
- package/dist/notation/lodExtractor.js +149 -0
- package/dist/notation/lodExtractor.js.map +1 -0
- package/dist/notation/nameShortener.d.ts +22 -0
- package/dist/notation/nameShortener.d.ts.map +1 -0
- package/dist/notation/nameShortener.js +24 -0
- package/dist/notation/nameShortener.js.map +1 -0
- package/dist/notation/perspectives.d.ts +11 -0
- package/dist/notation/perspectives.d.ts.map +1 -0
- package/dist/notation/perspectives.js +16 -0
- package/dist/notation/perspectives.js.map +1 -0
- package/dist/notation/renderer.d.ts +31 -0
- package/dist/notation/renderer.d.ts.map +1 -0
- package/dist/notation/renderer.js +315 -0
- package/dist/notation/renderer.js.map +1 -0
- package/dist/notation/traceRenderer.d.ts +39 -0
- package/dist/notation/traceRenderer.d.ts.map +1 -0
- package/dist/notation/traceRenderer.js +358 -0
- package/dist/notation/traceRenderer.js.map +1 -0
- package/dist/notation/types.d.ts +66 -0
- package/dist/notation/types.d.ts.map +1 -0
- package/dist/notation/types.js +10 -0
- package/dist/notation/types.js.map +1 -0
- package/dist/queries/NodeContext.d.ts +81 -0
- package/dist/queries/NodeContext.d.ts.map +1 -0
- package/dist/queries/NodeContext.js +196 -0
- package/dist/queries/NodeContext.js.map +1 -0
- package/dist/queries/findCallsInFunction.d.ts +62 -0
- package/dist/queries/findCallsInFunction.d.ts.map +1 -0
- package/dist/queries/findCallsInFunction.js +169 -0
- package/dist/queries/findCallsInFunction.js.map +1 -0
- package/dist/queries/findContainingFunction.d.ts +57 -0
- package/dist/queries/findContainingFunction.d.ts.map +1 -0
- package/dist/queries/findContainingFunction.js +91 -0
- package/dist/queries/findContainingFunction.js.map +1 -0
- package/dist/queries/index.d.ts +18 -0
- package/dist/queries/index.d.ts.map +1 -0
- package/dist/queries/index.js +14 -0
- package/dist/queries/index.js.map +1 -0
- package/dist/queries/traceDataflow.d.ts +65 -0
- package/dist/queries/traceDataflow.d.ts.map +1 -0
- package/dist/queries/traceDataflow.js +754 -0
- package/dist/queries/traceDataflow.js.map +1 -0
- package/dist/queries/traceValues.d.ts +70 -0
- package/dist/queries/traceValues.d.ts.map +1 -0
- package/dist/queries/traceValues.js +373 -0
- package/dist/queries/traceValues.js.map +1 -0
- package/dist/queries/types.d.ts +166 -0
- package/dist/queries/types.d.ts.map +1 -0
- package/dist/queries/types.js +10 -0
- package/dist/queries/types.js.map +1 -0
- package/dist/schema/GraphSchemaExtractor.d.ts +53 -0
- package/dist/schema/GraphSchemaExtractor.d.ts.map +1 -0
- package/dist/schema/GraphSchemaExtractor.js +125 -0
- package/dist/schema/GraphSchemaExtractor.js.map +1 -0
- package/dist/schema/InterfaceSchemaExtractor.d.ts +73 -0
- package/dist/schema/InterfaceSchemaExtractor.d.ts.map +1 -0
- package/dist/schema/InterfaceSchemaExtractor.js +113 -0
- package/dist/schema/InterfaceSchemaExtractor.js.map +1 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +3 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts +356 -0
- package/dist/storage/backends/RFDBServerBackend.d.ts.map +1 -0
- package/dist/storage/backends/RFDBServerBackend.js +748 -0
- package/dist/storage/backends/RFDBServerBackend.js.map +1 -0
- package/dist/storage/backends/typeValidation.d.ts +47 -0
- package/dist/storage/backends/typeValidation.d.ts.map +1 -0
- package/dist/storage/backends/typeValidation.js +141 -0
- package/dist/storage/backends/typeValidation.js.map +1 -0
- package/dist/utils/findRfdbBinary.d.ts +67 -0
- package/dist/utils/findRfdbBinary.d.ts.map +1 -0
- package/dist/utils/findRfdbBinary.js +261 -0
- package/dist/utils/findRfdbBinary.js.map +1 -0
- package/dist/utils/lazyDownload.d.ts +43 -0
- package/dist/utils/lazyDownload.d.ts.map +1 -0
- package/dist/utils/lazyDownload.js +175 -0
- package/dist/utils/lazyDownload.js.map +1 -0
- package/dist/utils/moduleResolution.d.ts +134 -0
- package/dist/utils/moduleResolution.d.ts.map +1 -0
- package/dist/utils/moduleResolution.js +189 -0
- package/dist/utils/moduleResolution.js.map +1 -0
- package/dist/utils/resolveNodeFile.d.ts +13 -0
- package/dist/utils/resolveNodeFile.d.ts.map +1 -0
- package/dist/utils/resolveNodeFile.js +18 -0
- package/dist/utils/resolveNodeFile.js.map +1 -0
- package/dist/utils/startRfdbServer.d.ts +63 -0
- package/dist/utils/startRfdbServer.d.ts.map +1 -0
- package/dist/utils/startRfdbServer.js +142 -0
- package/dist/utils/startRfdbServer.js.map +1 -0
- package/dist/validation/PathValidator.d.ts +80 -0
- package/dist/validation/PathValidator.d.ts.map +1 -0
- package/dist/validation/PathValidator.js +252 -0
- package/dist/validation/PathValidator.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +26 -0
- package/dist/version.js.map +1 -0
- package/package.json +50 -0
- package/src/api/GraphAPI.ts +307 -0
- package/src/api/GuaranteeAPI.ts +402 -0
- package/src/config/ConfigLoader.ts +653 -0
- package/src/config/index.ts +13 -0
- package/src/core/CoverageAnalyzer.ts +243 -0
- package/src/core/FileExplainer.ts +179 -0
- package/src/core/FileOverview.ts +397 -0
- package/src/core/GrafemaUri.ts +216 -0
- package/src/core/GraphBackend.ts +266 -0
- package/src/core/GraphFreshnessChecker.ts +145 -0
- package/src/core/GuaranteeManager.ts +684 -0
- package/src/core/HashUtils.ts +48 -0
- package/src/core/IncrementalReanalyzer.ts +106 -0
- package/src/core/ResourceRegistry.ts +39 -0
- package/src/core/SemanticId.ts +423 -0
- package/src/core/VersionManager.ts +405 -0
- package/src/core/brandNodeInternal.ts +16 -0
- package/src/core/nodes/GuaranteeNode.ts +162 -0
- package/src/core/nodes/IssueNode.ts +177 -0
- package/src/core/nodes/NodeKind.ts +192 -0
- package/src/diagnostics/DiagnosticCollector.ts +170 -0
- package/src/diagnostics/DiagnosticReporter.ts +395 -0
- package/src/diagnostics/DiagnosticWriter.ts +50 -0
- package/src/diagnostics/categories.ts +104 -0
- package/src/diagnostics/index.ts +30 -0
- package/src/errors/GrafemaError.ts +297 -0
- package/src/index.ts +261 -0
- package/src/instructions/index.ts +21 -0
- package/src/instructions/onboarding.md +133 -0
- package/src/knowledge/KnowledgeBase.ts +486 -0
- package/src/knowledge/SemanticAddressResolver.ts +191 -0
- package/src/knowledge/git-ingest.ts +402 -0
- package/src/knowledge/git-queries.ts +269 -0
- package/src/knowledge/index.ts +29 -0
- package/src/knowledge/parser.ts +294 -0
- package/src/knowledge/types.ts +146 -0
- package/src/logging/Logger.ts +303 -0
- package/src/notation/archetypes.ts +189 -0
- package/src/notation/fold.ts +696 -0
- package/src/notation/index.ts +27 -0
- package/src/notation/lodExtractor.ts +177 -0
- package/src/notation/nameShortener.ts +24 -0
- package/src/notation/perspectives.ts +18 -0
- package/src/notation/renderer.ts +394 -0
- package/src/notation/traceRenderer.ts +458 -0
- package/src/notation/types.ts +79 -0
- package/src/queries/NodeContext.ts +280 -0
- package/src/queries/findCallsInFunction.ts +249 -0
- package/src/queries/findContainingFunction.ts +124 -0
- package/src/queries/index.ts +44 -0
- package/src/queries/traceDataflow.ts +838 -0
- package/src/queries/traceValues.ts +531 -0
- package/src/queries/types.ts +191 -0
- package/src/schema/GraphSchemaExtractor.ts +177 -0
- package/src/schema/InterfaceSchemaExtractor.ts +173 -0
- package/src/schema/index.ts +5 -0
- package/src/storage/backends/RFDBServerBackend.ts +895 -0
- package/src/storage/backends/typeValidation.ts +154 -0
- package/src/utils/findRfdbBinary.ts +288 -0
- package/src/utils/lazyDownload.ts +206 -0
- package/src/utils/moduleResolution.ts +271 -0
- package/src/utils/resolveNodeFile.ts +18 -0
- package/src/utils/startRfdbServer.ts +197 -0
- package/src/validation/PathValidator.ts +334 -0
- package/src/version.ts +28 -0
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value Tracing Utility (REG-244)
|
|
3
|
+
*
|
|
4
|
+
* Traces a node through ASSIGNED_FROM/DERIVES_FROM edges to find
|
|
5
|
+
* all possible literal values or mark as unknown.
|
|
6
|
+
*
|
|
7
|
+
* Graph structure:
|
|
8
|
+
* ```
|
|
9
|
+
* VARIABLE -[ASSIGNED_FROM]-> LITERAL (concrete value)
|
|
10
|
+
* VARIABLE -[ASSIGNED_FROM]-> PARAMETER -[DERIVES_FROM]-> argument source (interprocedural)
|
|
11
|
+
* VARIABLE -[ASSIGNED_FROM]-> CALL (unknown: function return)
|
|
12
|
+
* VARIABLE -[DERIVES_FROM]-> EXPRESSION (check nondeterministic patterns)
|
|
13
|
+
* VARIABLE -[ASSIGNED_FROM]-> VARIABLE (chain - recurse)
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* Used by:
|
|
17
|
+
* - CLI trace command (sink-based tracing)
|
|
18
|
+
* - ValueDomainAnalyzer (computed member access resolution)
|
|
19
|
+
*
|
|
20
|
+
* @module queries/traceValues
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
TracedValue,
|
|
25
|
+
TraceValuesOptions,
|
|
26
|
+
TraceValuesGraphBackend,
|
|
27
|
+
ValueSetResult,
|
|
28
|
+
NondeterministicPattern,
|
|
29
|
+
} from './types.js';
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// NONDETERMINISTIC PATTERNS (moved from ValueDomainAnalyzer)
|
|
33
|
+
// =============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Nondeterministic MemberExpression patterns.
|
|
37
|
+
* object.property combinations that represent external/user input.
|
|
38
|
+
*/
|
|
39
|
+
export const NONDETERMINISTIC_PATTERNS: NondeterministicPattern[] = [
|
|
40
|
+
// Environment variables
|
|
41
|
+
{ object: 'process', property: 'env' },
|
|
42
|
+
// HTTP request data (Express.js patterns)
|
|
43
|
+
{ object: 'req', property: 'body' },
|
|
44
|
+
{ object: 'req', property: 'query' },
|
|
45
|
+
{ object: 'req', property: 'params' },
|
|
46
|
+
{ object: 'req', property: 'headers' },
|
|
47
|
+
{ object: 'req', property: 'cookies' },
|
|
48
|
+
{ object: 'request', property: 'body' },
|
|
49
|
+
{ object: 'request', property: 'query' },
|
|
50
|
+
{ object: 'request', property: 'params' },
|
|
51
|
+
// Context patterns (Koa, etc.)
|
|
52
|
+
{ object: 'ctx', property: 'request' },
|
|
53
|
+
{ object: 'ctx', property: 'body' },
|
|
54
|
+
{ object: 'ctx', property: 'query' },
|
|
55
|
+
{ object: 'ctx', property: 'params' },
|
|
56
|
+
];
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Nondeterministic object prefixes.
|
|
60
|
+
* Any property access on these is nondeterministic.
|
|
61
|
+
*/
|
|
62
|
+
export const NONDETERMINISTIC_OBJECTS: string[] = [
|
|
63
|
+
'process.env', // process.env.ANY_VAR
|
|
64
|
+
'req.body', // req.body.userId
|
|
65
|
+
'req.query', // req.query.filter
|
|
66
|
+
'req.params', // req.params.id
|
|
67
|
+
'request.body',
|
|
68
|
+
'ctx.request',
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
// =============================================================================
|
|
72
|
+
// MAIN FUNCTION
|
|
73
|
+
// =============================================================================
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Trace a node to all its possible literal values.
|
|
77
|
+
*
|
|
78
|
+
* Starting from the given node, follows ASSIGNED_FROM (and optionally
|
|
79
|
+
* DERIVES_FROM) edges backwards to find:
|
|
80
|
+
* - LITERAL nodes: concrete values
|
|
81
|
+
* - PARAMETER nodes: runtime inputs (unknown)
|
|
82
|
+
* - CALL nodes: function return values (unknown)
|
|
83
|
+
* - EXPRESSION nodes: checks for nondeterministic patterns
|
|
84
|
+
*
|
|
85
|
+
* @param backend - Graph backend for queries
|
|
86
|
+
* @param nodeId - Starting node ID
|
|
87
|
+
* @param options - Traversal options
|
|
88
|
+
* @returns Array of traced values with sources
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* const values = await traceValues(backend, variableId);
|
|
92
|
+
* for (const v of values) {
|
|
93
|
+
* if (v.isUnknown) {
|
|
94
|
+
* console.log(`Unknown from ${v.source.file}:${v.source.line} (${v.reason})`);
|
|
95
|
+
* } else {
|
|
96
|
+
* console.log(`Value: ${v.value} from ${v.source.file}:${v.source.line}`);
|
|
97
|
+
* }
|
|
98
|
+
* }
|
|
99
|
+
*/
|
|
100
|
+
export async function traceValues(
|
|
101
|
+
backend: TraceValuesGraphBackend,
|
|
102
|
+
nodeId: string,
|
|
103
|
+
options?: TraceValuesOptions
|
|
104
|
+
): Promise<TracedValue[]> {
|
|
105
|
+
const results: TracedValue[] = [];
|
|
106
|
+
const visited = new Set<string>();
|
|
107
|
+
|
|
108
|
+
const maxDepth = options?.maxDepth ?? 10;
|
|
109
|
+
const followDerivesFrom = options?.followDerivesFrom ?? true;
|
|
110
|
+
const detectNondeterministic = options?.detectNondeterministic ?? true;
|
|
111
|
+
const followCallReturns = options?.followCallReturns ?? true;
|
|
112
|
+
|
|
113
|
+
await traceRecursive(
|
|
114
|
+
backend,
|
|
115
|
+
nodeId,
|
|
116
|
+
visited,
|
|
117
|
+
0,
|
|
118
|
+
maxDepth,
|
|
119
|
+
followDerivesFrom,
|
|
120
|
+
detectNondeterministic,
|
|
121
|
+
followCallReturns,
|
|
122
|
+
results
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return results;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Recursive tracing function
|
|
130
|
+
*/
|
|
131
|
+
async function traceRecursive(
|
|
132
|
+
backend: TraceValuesGraphBackend,
|
|
133
|
+
nodeId: string,
|
|
134
|
+
visited: Set<string>,
|
|
135
|
+
depth: number,
|
|
136
|
+
maxDepth: number,
|
|
137
|
+
followDerivesFrom: boolean,
|
|
138
|
+
detectNondeterministic: boolean,
|
|
139
|
+
followCallReturns: boolean,
|
|
140
|
+
results: TracedValue[]
|
|
141
|
+
): Promise<void> {
|
|
142
|
+
// Cycle protection
|
|
143
|
+
if (visited.has(nodeId)) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
visited.add(nodeId);
|
|
147
|
+
|
|
148
|
+
// Get node
|
|
149
|
+
const node = await backend.getNode(nodeId);
|
|
150
|
+
if (!node) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const nodeType = node.type || node.nodeType;
|
|
155
|
+
const source = {
|
|
156
|
+
id: node.id,
|
|
157
|
+
file: node.file || '',
|
|
158
|
+
line: node.line || 0,
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
// Depth protection - check after getting node for source info
|
|
162
|
+
if (depth > maxDepth) {
|
|
163
|
+
results.push({
|
|
164
|
+
value: undefined,
|
|
165
|
+
source,
|
|
166
|
+
isUnknown: true,
|
|
167
|
+
reason: 'max_depth',
|
|
168
|
+
});
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Terminal: LITERAL - found concrete value
|
|
173
|
+
if (nodeType === 'LITERAL') {
|
|
174
|
+
results.push({
|
|
175
|
+
value: node.value,
|
|
176
|
+
source,
|
|
177
|
+
isUnknown: false,
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// PARAMETER - try to follow DERIVES_FROM edges to call-site arguments
|
|
183
|
+
if (nodeType === 'PARAMETER') {
|
|
184
|
+
if (followDerivesFrom) {
|
|
185
|
+
const derivesEdges = await backend.getOutgoingEdges(nodeId, ['DERIVES_FROM']);
|
|
186
|
+
if (derivesEdges.length > 0) {
|
|
187
|
+
for (const edge of derivesEdges) {
|
|
188
|
+
await traceRecursive(
|
|
189
|
+
backend,
|
|
190
|
+
edge.dst,
|
|
191
|
+
visited,
|
|
192
|
+
depth + 1,
|
|
193
|
+
maxDepth,
|
|
194
|
+
followDerivesFrom,
|
|
195
|
+
detectNondeterministic,
|
|
196
|
+
followCallReturns,
|
|
197
|
+
results
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// No DERIVES_FROM edges or followDerivesFrom disabled
|
|
205
|
+
results.push({
|
|
206
|
+
value: undefined,
|
|
207
|
+
source,
|
|
208
|
+
isUnknown: true,
|
|
209
|
+
reason: 'parameter',
|
|
210
|
+
});
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Terminal: CALL / METHOD_CALL - function return value
|
|
215
|
+
if (nodeType === 'CALL' || nodeType === 'METHOD_CALL') {
|
|
216
|
+
// REG-576: Follow CALL_RETURNS to called function's return values
|
|
217
|
+
if (followCallReturns) {
|
|
218
|
+
const callReturnsEdges = await backend.getOutgoingEdges(nodeId, ['CALL_RETURNS']);
|
|
219
|
+
if (callReturnsEdges.length > 0) {
|
|
220
|
+
for (const crEdge of callReturnsEdges) {
|
|
221
|
+
// Get RETURNS edges from the target function
|
|
222
|
+
const returnsEdges = await backend.getOutgoingEdges(crEdge.dst, ['RETURNS']);
|
|
223
|
+
if (returnsEdges.length > 0) {
|
|
224
|
+
for (const retEdge of returnsEdges) {
|
|
225
|
+
await traceRecursive(
|
|
226
|
+
backend,
|
|
227
|
+
retEdge.dst,
|
|
228
|
+
visited,
|
|
229
|
+
depth + 1,
|
|
230
|
+
maxDepth,
|
|
231
|
+
followDerivesFrom,
|
|
232
|
+
detectNondeterministic,
|
|
233
|
+
followCallReturns,
|
|
234
|
+
results
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
// Function has no RETURNS edges → implicit undefined
|
|
239
|
+
const fnNode = await backend.getNode(crEdge.dst);
|
|
240
|
+
results.push({
|
|
241
|
+
value: undefined,
|
|
242
|
+
source: { id: crEdge.dst, file: fnNode?.file || '', line: fnNode?.line || 0 },
|
|
243
|
+
isUnknown: true,
|
|
244
|
+
reason: 'implicit_return',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check for HTTP_RECEIVES edges (cross-service data flow)
|
|
253
|
+
const httpEdges = await backend.getOutgoingEdges(nodeId, ['HTTP_RECEIVES']);
|
|
254
|
+
|
|
255
|
+
if (httpEdges.length > 0) {
|
|
256
|
+
// Follow HTTP boundary to backend response
|
|
257
|
+
for (const edge of httpEdges) {
|
|
258
|
+
await traceRecursive(
|
|
259
|
+
backend,
|
|
260
|
+
edge.dst,
|
|
261
|
+
visited,
|
|
262
|
+
depth + 1,
|
|
263
|
+
maxDepth,
|
|
264
|
+
followDerivesFrom,
|
|
265
|
+
detectNondeterministic,
|
|
266
|
+
followCallReturns,
|
|
267
|
+
results
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return; // Traced through HTTP boundary, don't mark as unknown
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// No CALL_RETURNS or HTTP_RECEIVES → mark as unknown
|
|
274
|
+
results.push({
|
|
275
|
+
value: undefined,
|
|
276
|
+
source,
|
|
277
|
+
isUnknown: true,
|
|
278
|
+
reason: 'call_result',
|
|
279
|
+
});
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Check nondeterministic EXPRESSION patterns
|
|
284
|
+
if (nodeType === 'EXPRESSION' && detectNondeterministic) {
|
|
285
|
+
if (isNondeterministicExpression(node)) {
|
|
286
|
+
results.push({
|
|
287
|
+
value: undefined,
|
|
288
|
+
source,
|
|
289
|
+
isUnknown: true,
|
|
290
|
+
reason: 'nondeterministic',
|
|
291
|
+
});
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// REG-574: Conditional value sets — ternary expressions
|
|
297
|
+
// EXPRESSION(ternary) → follow HAS_CONSEQUENT + HAS_ALTERNATE (skip HAS_CONDITION)
|
|
298
|
+
if (nodeType === 'EXPRESSION' && node.name === 'ternary') {
|
|
299
|
+
const branches = await backend.getOutgoingEdges(nodeId, ['HAS_CONSEQUENT', 'HAS_ALTERNATE']);
|
|
300
|
+
if (branches.length > 0) {
|
|
301
|
+
for (const edge of branches) {
|
|
302
|
+
await traceRecursive(
|
|
303
|
+
backend,
|
|
304
|
+
edge.dst,
|
|
305
|
+
visited,
|
|
306
|
+
depth + 1,
|
|
307
|
+
maxDepth,
|
|
308
|
+
followDerivesFrom,
|
|
309
|
+
detectNondeterministic,
|
|
310
|
+
followCallReturns,
|
|
311
|
+
results
|
|
312
|
+
);
|
|
313
|
+
}
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// REG-574: Conditional value sets — logical expressions (||, &&, ??)
|
|
319
|
+
// These represent alternative values, follow USES edges
|
|
320
|
+
if (nodeType === 'EXPRESSION' && isLogicalOperator(node.name)) {
|
|
321
|
+
const operands = await backend.getOutgoingEdges(nodeId, ['USES']);
|
|
322
|
+
if (operands.length > 0) {
|
|
323
|
+
for (const edge of operands) {
|
|
324
|
+
await traceRecursive(
|
|
325
|
+
backend,
|
|
326
|
+
edge.dst,
|
|
327
|
+
visited,
|
|
328
|
+
depth + 1,
|
|
329
|
+
maxDepth,
|
|
330
|
+
followDerivesFrom,
|
|
331
|
+
detectNondeterministic,
|
|
332
|
+
followCallReturns,
|
|
333
|
+
results
|
|
334
|
+
);
|
|
335
|
+
}
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Terminal: OBJECT_LITERAL - a valid structured value
|
|
341
|
+
// OBJECT_LITERAL without edges is valid (e.g., {} or {key: value})
|
|
342
|
+
if (nodeType === 'OBJECT_LITERAL') {
|
|
343
|
+
results.push({
|
|
344
|
+
value: node.value,
|
|
345
|
+
source,
|
|
346
|
+
isUnknown: false,
|
|
347
|
+
});
|
|
348
|
+
return;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// REG-334: Special case - CONSTRUCTOR_CALL for Promise
|
|
352
|
+
// Follow RESOLVES_TO edges to find actual data sources from resolve() calls
|
|
353
|
+
if (nodeType === 'CONSTRUCTOR_CALL') {
|
|
354
|
+
const className = (node as { className?: string }).className;
|
|
355
|
+
|
|
356
|
+
if (className === 'Promise') {
|
|
357
|
+
// Look for incoming RESOLVES_TO edges (resolve/reject calls)
|
|
358
|
+
const resolveEdges = await backend.getIncomingEdges(nodeId, ['RESOLVES_TO']);
|
|
359
|
+
|
|
360
|
+
if (resolveEdges.length > 0) {
|
|
361
|
+
// Follow resolve/reject calls to their arguments
|
|
362
|
+
for (const edge of resolveEdges) {
|
|
363
|
+
// edge.src is the resolve(value) CALL node
|
|
364
|
+
// We need to find what value was passed to resolve()
|
|
365
|
+
// The CALL node should have PASSES_ARGUMENT edge to the value
|
|
366
|
+
const argEdges = await backend.getOutgoingEdges(edge.src, ['PASSES_ARGUMENT']);
|
|
367
|
+
|
|
368
|
+
for (const argEdge of argEdges) {
|
|
369
|
+
// Check if this is the first argument (argIndex 0)
|
|
370
|
+
const argIndex = (argEdge.metadata as { argIndex?: number } | undefined)?.argIndex;
|
|
371
|
+
if (argIndex === 0) {
|
|
372
|
+
// Recursively trace the argument value
|
|
373
|
+
await traceRecursive(
|
|
374
|
+
backend,
|
|
375
|
+
argEdge.dst,
|
|
376
|
+
visited,
|
|
377
|
+
depth + 1,
|
|
378
|
+
maxDepth,
|
|
379
|
+
followDerivesFrom,
|
|
380
|
+
detectNondeterministic,
|
|
381
|
+
followCallReturns,
|
|
382
|
+
results
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return; // Traced through resolve, don't mark as unknown
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Non-Promise constructor or no resolve edges - mark as unknown
|
|
392
|
+
results.push({
|
|
393
|
+
value: undefined,
|
|
394
|
+
source,
|
|
395
|
+
isUnknown: true,
|
|
396
|
+
reason: 'constructor_call',
|
|
397
|
+
});
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Get outgoing data flow edges
|
|
402
|
+
const edgeTypes = ['ASSIGNED_FROM'];
|
|
403
|
+
if (followDerivesFrom) {
|
|
404
|
+
edgeTypes.push('DERIVES_FROM');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const edges = await backend.getOutgoingEdges(nodeId, edgeTypes);
|
|
408
|
+
|
|
409
|
+
// REG-574: Also check incoming WRITES_TO edges (if/else reassignment)
|
|
410
|
+
const writesToEdges = await backend.getIncomingEdges(nodeId, ['WRITES_TO']);
|
|
411
|
+
|
|
412
|
+
// No edges case - unknown
|
|
413
|
+
if (edges.length === 0 && writesToEdges.length === 0) {
|
|
414
|
+
results.push({
|
|
415
|
+
value: undefined,
|
|
416
|
+
source,
|
|
417
|
+
isUnknown: true,
|
|
418
|
+
reason: 'no_sources',
|
|
419
|
+
});
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Recurse through ASSIGNED_FROM/DERIVES_FROM targets
|
|
424
|
+
for (const edge of edges) {
|
|
425
|
+
await traceRecursive(
|
|
426
|
+
backend,
|
|
427
|
+
edge.dst,
|
|
428
|
+
visited,
|
|
429
|
+
depth + 1,
|
|
430
|
+
maxDepth,
|
|
431
|
+
followDerivesFrom,
|
|
432
|
+
detectNondeterministic,
|
|
433
|
+
followCallReturns,
|
|
434
|
+
results
|
|
435
|
+
);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
// REG-574: Recurse through WRITES_TO sources (the EXPRESSION(=) node,
|
|
439
|
+
// which will naturally follow its ASSIGNED_FROM to the value)
|
|
440
|
+
for (const edge of writesToEdges) {
|
|
441
|
+
await traceRecursive(
|
|
442
|
+
backend,
|
|
443
|
+
edge.src,
|
|
444
|
+
visited,
|
|
445
|
+
depth + 1,
|
|
446
|
+
maxDepth,
|
|
447
|
+
followDerivesFrom,
|
|
448
|
+
detectNondeterministic,
|
|
449
|
+
followCallReturns,
|
|
450
|
+
results
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// =============================================================================
|
|
456
|
+
// HELPERS
|
|
457
|
+
// =============================================================================
|
|
458
|
+
|
|
459
|
+
/**
|
|
460
|
+
* Check if an expression name is a logical operator (alternative values).
|
|
461
|
+
* Used to distinguish logical OR/AND/nullish from arithmetic operators.
|
|
462
|
+
*/
|
|
463
|
+
function isLogicalOperator(name: string | undefined): boolean {
|
|
464
|
+
return name === '||' || name === '&&' || name === '??';
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
/**
|
|
468
|
+
* Check if an EXPRESSION node represents a nondeterministic pattern.
|
|
469
|
+
* E.g., process.env.VAR, req.body.userId, etc.
|
|
470
|
+
*/
|
|
471
|
+
function isNondeterministicExpression(node: {
|
|
472
|
+
expressionType?: string;
|
|
473
|
+
object?: string;
|
|
474
|
+
property?: string;
|
|
475
|
+
}): boolean {
|
|
476
|
+
if (node.expressionType !== 'MemberExpression') {
|
|
477
|
+
return false;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const object = node.object;
|
|
481
|
+
const property = node.property;
|
|
482
|
+
|
|
483
|
+
if (!object || !property) {
|
|
484
|
+
return false;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// Check exact patterns (object.property)
|
|
488
|
+
for (const pattern of NONDETERMINISTIC_PATTERNS) {
|
|
489
|
+
if (object === pattern.object && property === pattern.property) {
|
|
490
|
+
return true;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// Check if object is a known nondeterministic prefix
|
|
495
|
+
// e.g., process.env.VAR where object is 'process.env'
|
|
496
|
+
for (const prefix of NONDETERMINISTIC_OBJECTS) {
|
|
497
|
+
if (object === prefix || object.startsWith(prefix + '.')) {
|
|
498
|
+
return true;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return false;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Aggregate traced values into a simplified result.
|
|
507
|
+
* Useful for consumers who don't need source locations.
|
|
508
|
+
*
|
|
509
|
+
* Note: null and undefined values are NOT included in the values array.
|
|
510
|
+
* If you need to detect "assigned to null", check the raw TracedValue[] instead.
|
|
511
|
+
*
|
|
512
|
+
* @param traced - Array of traced values
|
|
513
|
+
* @returns Aggregated result with unique values and hasUnknown flag
|
|
514
|
+
*/
|
|
515
|
+
export function aggregateValues(traced: TracedValue[]): ValueSetResult {
|
|
516
|
+
const valueSet = new Set<unknown>();
|
|
517
|
+
let hasUnknown = false;
|
|
518
|
+
|
|
519
|
+
for (const t of traced) {
|
|
520
|
+
if (t.isUnknown) {
|
|
521
|
+
hasUnknown = true;
|
|
522
|
+
} else if (t.value !== undefined && t.value !== null) {
|
|
523
|
+
valueSet.add(t.value);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
return {
|
|
528
|
+
values: Array.from(valueSet),
|
|
529
|
+
hasUnknown,
|
|
530
|
+
};
|
|
531
|
+
}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Types for Graph Query Utilities
|
|
3
|
+
*
|
|
4
|
+
* These types are used by findCallsInFunction, findContainingFunction,
|
|
5
|
+
* and other query utilities.
|
|
6
|
+
*
|
|
7
|
+
* @module queries/types
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Information about a function/method call found in code
|
|
12
|
+
*/
|
|
13
|
+
export interface CallInfo {
|
|
14
|
+
/** Node ID of the call site */
|
|
15
|
+
id: string;
|
|
16
|
+
/** Called function/method name */
|
|
17
|
+
name: string;
|
|
18
|
+
/** Node type: 'CALL' or 'METHOD_CALL' */
|
|
19
|
+
type: 'CALL' | 'METHOD_CALL';
|
|
20
|
+
/** Object name for method calls (e.g., 'response' for response.json()) */
|
|
21
|
+
object?: string;
|
|
22
|
+
/** Whether the call target was resolved (has CALLS edge) */
|
|
23
|
+
resolved: boolean;
|
|
24
|
+
/** Target function info if resolved */
|
|
25
|
+
target?: {
|
|
26
|
+
id: string;
|
|
27
|
+
name: string;
|
|
28
|
+
file?: string;
|
|
29
|
+
line?: number;
|
|
30
|
+
};
|
|
31
|
+
/** File where call occurs */
|
|
32
|
+
file?: string;
|
|
33
|
+
/** Line number of call */
|
|
34
|
+
line?: number;
|
|
35
|
+
/** Depth in transitive call chain (0 = direct call) */
|
|
36
|
+
depth?: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Information about a function that calls another function
|
|
41
|
+
*/
|
|
42
|
+
export interface CallerInfo {
|
|
43
|
+
/** Caller function ID */
|
|
44
|
+
id: string;
|
|
45
|
+
/** Caller function name */
|
|
46
|
+
name: string;
|
|
47
|
+
/** Caller function type (FUNCTION, CLASS, MODULE) */
|
|
48
|
+
type: string;
|
|
49
|
+
/** File containing the caller */
|
|
50
|
+
file?: string;
|
|
51
|
+
/** Line of the call site */
|
|
52
|
+
line?: number;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Options for finding calls in a function
|
|
57
|
+
*/
|
|
58
|
+
export interface FindCallsOptions {
|
|
59
|
+
/** Maximum depth for scope traversal (default: 10) */
|
|
60
|
+
maxDepth?: number;
|
|
61
|
+
/** Follow transitive calls (default: false) */
|
|
62
|
+
transitive?: boolean;
|
|
63
|
+
/** Maximum depth for transitive traversal (default: 5) */
|
|
64
|
+
transitiveDepth?: number;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// =============================================================================
|
|
68
|
+
// VALUE TRACING TYPES (REG-244)
|
|
69
|
+
// =============================================================================
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Location of a value source in the graph
|
|
73
|
+
*/
|
|
74
|
+
export interface ValueSource {
|
|
75
|
+
/** Node ID in the graph */
|
|
76
|
+
id: string;
|
|
77
|
+
/** File path */
|
|
78
|
+
file: string;
|
|
79
|
+
/** Line number (1-based) */
|
|
80
|
+
line: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Reason why a value could not be determined statically.
|
|
85
|
+
* Used for debugging and user-facing messages.
|
|
86
|
+
*/
|
|
87
|
+
export type UnknownReason =
|
|
88
|
+
| 'parameter' // Function parameter (runtime input)
|
|
89
|
+
| 'call_result' // Return value from function call
|
|
90
|
+
| 'implicit_return' // Function has no return statement (void/undefined)
|
|
91
|
+
| 'constructor_call' // Constructor call without traceable data (REG-334)
|
|
92
|
+
| 'nondeterministic' // process.env, req.body, etc.
|
|
93
|
+
| 'max_depth' // Hit depth limit during traversal
|
|
94
|
+
| 'no_sources'; // No ASSIGNED_FROM/DERIVES_FROM edges found
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A single traced value from the graph.
|
|
98
|
+
* Represents either a concrete value (from LITERAL) or an unknown value
|
|
99
|
+
* (from PARAMETER, CALL, nondeterministic source, etc.)
|
|
100
|
+
*/
|
|
101
|
+
export interface TracedValue {
|
|
102
|
+
/** The literal value (undefined if unknown) */
|
|
103
|
+
value: unknown;
|
|
104
|
+
/** Source location in the codebase */
|
|
105
|
+
source: ValueSource;
|
|
106
|
+
/** Whether value could not be determined statically */
|
|
107
|
+
isUnknown: boolean;
|
|
108
|
+
/** Why the value is unknown (for debugging/display) */
|
|
109
|
+
reason?: UnknownReason;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Options for traceValues()
|
|
114
|
+
*/
|
|
115
|
+
export interface TraceValuesOptions {
|
|
116
|
+
/** Maximum traversal depth (default: 10) */
|
|
117
|
+
maxDepth?: number;
|
|
118
|
+
/** Follow DERIVES_FROM edges in addition to ASSIGNED_FROM (default: true) */
|
|
119
|
+
followDerivesFrom?: boolean;
|
|
120
|
+
/** Detect nondeterministic patterns like process.env (default: true) */
|
|
121
|
+
detectNondeterministic?: boolean;
|
|
122
|
+
/** Follow CALL_RETURNS edges to trace through function calls (default: true) */
|
|
123
|
+
followCallReturns?: boolean;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Aggregated result from tracing.
|
|
128
|
+
* Convenience type for consumers who don't need individual sources.
|
|
129
|
+
*/
|
|
130
|
+
export interface ValueSetResult {
|
|
131
|
+
/** All unique concrete values found */
|
|
132
|
+
values: unknown[];
|
|
133
|
+
/** Whether any path led to unknown value */
|
|
134
|
+
hasUnknown: boolean;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Edge record for traceValues
|
|
139
|
+
*/
|
|
140
|
+
export interface TraceValuesEdge {
|
|
141
|
+
src: string;
|
|
142
|
+
dst: string;
|
|
143
|
+
type: string;
|
|
144
|
+
metadata?: { argIndex?: number; isReject?: boolean };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Node record for traceValues
|
|
149
|
+
*/
|
|
150
|
+
export interface TraceValuesNode {
|
|
151
|
+
id: string;
|
|
152
|
+
type?: string;
|
|
153
|
+
nodeType?: string;
|
|
154
|
+
value?: unknown;
|
|
155
|
+
file?: string;
|
|
156
|
+
line?: number;
|
|
157
|
+
expressionType?: string;
|
|
158
|
+
object?: string;
|
|
159
|
+
property?: string;
|
|
160
|
+
className?: string;
|
|
161
|
+
name?: string;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Minimal graph backend interface for traceValues().
|
|
166
|
+
* Works with both RFDBServerBackend and internal Graph interface.
|
|
167
|
+
*/
|
|
168
|
+
export interface TraceValuesGraphBackend {
|
|
169
|
+
getNode(id: string): Promise<TraceValuesNode | null>;
|
|
170
|
+
getOutgoingEdges(
|
|
171
|
+
nodeId: string,
|
|
172
|
+
edgeTypes: string[] | null
|
|
173
|
+
): Promise<TraceValuesEdge[]>;
|
|
174
|
+
/**
|
|
175
|
+
* Get incoming edges to a node (REG-334: needed for RESOLVES_TO)
|
|
176
|
+
* Required for Promise tracing - must be implemented by all backends
|
|
177
|
+
*/
|
|
178
|
+
getIncomingEdges(
|
|
179
|
+
nodeId: string,
|
|
180
|
+
edgeTypes: string[] | null
|
|
181
|
+
): Promise<TraceValuesEdge[]>;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Nondeterministic MemberExpression pattern.
|
|
186
|
+
* object.property combinations that represent external/user input.
|
|
187
|
+
*/
|
|
188
|
+
export interface NondeterministicPattern {
|
|
189
|
+
object: string;
|
|
190
|
+
property: string;
|
|
191
|
+
}
|