arcvision 0.2.14 → 0.2.15
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/ARCVISION_DIRECTORY_STRUCTURE.md +104 -0
- package/CLI_STRUCTURE.md +110 -0
- package/CONFIGURATION.md +119 -0
- package/IMPLEMENTATION_SUMMARY.md +99 -0
- package/README.md +149 -89
- package/architecture.authority.ledger.json +46 -0
- package/arcvision-0.2.3.tgz +0 -0
- package/arcvision-0.2.4.tgz +0 -0
- package/arcvision-0.2.5.tgz +0 -0
- package/arcvision.context.diff.json +2181 -0
- package/arcvision.context.json +1021 -0
- package/arcvision.context.v1.json +2163 -0
- package/arcvision.context.v2.json +2173 -0
- package/arcvision_context/README.md +93 -0
- package/arcvision_context/architecture.authority.ledger.json +83 -0
- package/arcvision_context/arcvision.context.json +6884 -0
- package/debug-cycle-detection.js +56 -0
- package/dist/index.js +1626 -25
- package/docs/ENHANCED_ACCURACY_SAFETY_PROTOCOL.md +172 -0
- package/docs/accuracy-enhancement-artifacts/enhanced-validation-config.json +98 -0
- package/docs/acig-robustness-guide.md +164 -0
- package/docs/authoritative-gate-implementation.md +168 -0
- package/docs/cli-strengthening-summary.md +232 -0
- package/docs/invariant-system-summary.md +100 -0
- package/docs/invariant-system.md +112 -0
- package/generate_large_test.js +42 -0
- package/large_test_repo.json +1 -0
- package/output1.json +2163 -0
- package/output2.json +2163 -0
- package/package.json +46 -36
- package/scan_calcom_report.txt +0 -0
- package/scan_leafmint_report.txt +0 -0
- package/scan_output.txt +0 -0
- package/scan_trigger_report.txt +0 -0
- package/schema/arcvision_context_schema_v1.json +136 -1
- package/src/arcvision-guard.js +433 -0
- package/src/core/authority-core-detector.js +382 -0
- package/src/core/authority-ledger.js +300 -0
- package/src/core/blastRadius.js +299 -0
- package/src/core/call-resolver.js +196 -0
- package/src/core/change-evaluator.js +509 -0
- package/src/core/change-evaluator.js.backup +424 -0
- package/src/core/change-evaluator.ts +285 -0
- package/src/core/chunked-uploader.js +180 -0
- package/src/core/circular-dependency-detector.js +404 -0
- package/src/core/cli-error-handler.js +458 -0
- package/src/core/cli-validator.js +458 -0
- package/src/core/compression.js +64 -0
- package/src/core/context_builder.js +741 -0
- package/src/core/dependency-manager.js +134 -0
- package/src/core/di-detector.js +202 -0
- package/src/core/diff-analyzer.js +76 -0
- package/src/core/example-invariants.js +135 -0
- package/src/core/failure-mode-synthesizer.js +341 -0
- package/src/core/invariant-analyzer.js +294 -0
- package/src/core/invariant-detector.js +548 -0
- package/src/core/invariant-enforcer.js +171 -0
- package/src/core/invariant-evaluation-utils.js +172 -0
- package/src/core/invariant-hooks.js +152 -0
- package/src/core/invariant-integration-example.js +186 -0
- package/src/core/invariant-registry.js +298 -0
- package/src/core/invariant-registry.ts +100 -0
- package/src/core/invariant-types.js +66 -0
- package/src/core/invariants-index.js +88 -0
- package/src/core/method-tracker.js +170 -0
- package/src/core/override-handler.js +304 -0
- package/src/core/ownership-resolver.js +227 -0
- package/src/core/parser-enhanced.js +80 -0
- package/src/core/parser.js +610 -0
- package/src/core/path-resolver.js +240 -0
- package/src/core/pattern-matcher.js +246 -0
- package/src/core/progress-tracker.js +71 -0
- package/src/core/react-nextjs-detector.js +245 -0
- package/src/core/readme-generator.js +167 -0
- package/src/core/retry-handler.js +57 -0
- package/src/core/scanner.js +289 -0
- package/src/core/semantic-analyzer.js +204 -0
- package/src/core/structural-context-owner.js +442 -0
- package/src/core/symbol-indexer.js +164 -0
- package/src/core/tsconfig-utils.js +73 -0
- package/src/core/type-analyzer.js +272 -0
- package/src/core/watcher.js +18 -0
- package/src/core/workspace-scanner.js +88 -0
- package/src/engine/context_builder.js +280 -0
- package/src/engine/context_sorter.js +59 -0
- package/src/engine/context_validator.js +200 -0
- package/src/engine/id-generator.js +16 -0
- package/src/engine/pass1_facts.js +260 -0
- package/src/engine/pass2_semantics.js +333 -0
- package/src/engine/pass3_lifter.js +99 -0
- package/src/engine/pass4_signals.js +201 -0
- package/src/index.js +830 -0
- package/src/plugins/express-plugin.js +48 -0
- package/src/plugins/plugin-manager.js +58 -0
- package/src/plugins/react-plugin.js +54 -0
- package/temp_original.js +0 -0
- package/test/determinism-test.js +83 -0
- package/test-authoritative-context.js +53 -0
- package/test-real-authoritative-context.js +118 -0
- package/test-upload-enhancements.js +111 -0
- package/test_repos/allowed-clean-architecture/.arcvision/invariants.json +57 -0
- package/test_repos/allowed-clean-architecture/adapters/controllers/UserController.js +95 -0
- package/test_repos/allowed-clean-architecture/adapters/http/HttpServer.js +78 -0
- package/test_repos/allowed-clean-architecture/application/dtos/CreateUserRequest.js +37 -0
- package/test_repos/allowed-clean-architecture/application/services/UserService.js +61 -0
- package/test_repos/allowed-clean-architecture/arcvision_context/README.md +93 -0
- package/test_repos/allowed-clean-architecture/arcvision_context/arcvision.context.json +2796 -0
- package/test_repos/allowed-clean-architecture/domain/interfaces/UserRepository.js +25 -0
- package/test_repos/allowed-clean-architecture/domain/models/User.js +39 -0
- package/test_repos/allowed-clean-architecture/index.js +45 -0
- package/test_repos/allowed-clean-architecture/infrastructure/database/DatabaseConnection.js +56 -0
- package/test_repos/allowed-clean-architecture/infrastructure/repositories/InMemoryUserRepository.js +61 -0
- package/test_repos/allowed-clean-architecture/package.json +15 -0
- package/test_repos/blocked-legacy-monolith/.arcvision/invariants.json +78 -0
- package/test_repos/blocked-legacy-monolith/arcvision_context/README.md +93 -0
- package/test_repos/blocked-legacy-monolith/arcvision_context/arcvision.context.json +2882 -0
- package/test_repos/blocked-legacy-monolith/database/dbConnection.js +35 -0
- package/test_repos/blocked-legacy-monolith/index.js +38 -0
- package/test_repos/blocked-legacy-monolith/modules/emailService.js +31 -0
- package/test_repos/blocked-legacy-monolith/modules/paymentProcessor.js +37 -0
- package/test_repos/blocked-legacy-monolith/package.json +15 -0
- package/test_repos/blocked-legacy-monolith/shared/utils.js +19 -0
- package/test_repos/blocked-legacy-monolith/utils/helpers.js +23 -0
- package/test_repos/risky-microservices-concerns/.arcvision/invariants.json +69 -0
- package/test_repos/risky-microservices-concerns/arcvision_context/README.md +93 -0
- package/test_repos/risky-microservices-concerns/arcvision_context/arcvision.context.json +3070 -0
- package/test_repos/risky-microservices-concerns/common/utils.js +77 -0
- package/test_repos/risky-microservices-concerns/gateways/apiGateway.js +84 -0
- package/test_repos/risky-microservices-concerns/index.js +20 -0
- package/test_repos/risky-microservices-concerns/libs/deprecatedHelper.js +36 -0
- package/test_repos/risky-microservices-concerns/package.json +15 -0
- package/test_repos/risky-microservices-concerns/services/orderService.js +42 -0
- package/test_repos/risky-microservices-concerns/services/userService.js +48 -0
- package/verify_engine.js +116 -0
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { loadTSConfig } = require('./tsconfig-utils');
|
|
3
|
+
const { WorkspaceScanner } = require('./workspace-scanner');
|
|
4
|
+
|
|
5
|
+
// Import new engine passes
|
|
6
|
+
const { executePass1 } = require('../engine/pass1_facts');
|
|
7
|
+
const { executePass2 } = require('../engine/pass2_semantics');
|
|
8
|
+
const { executePass3 } = require('../engine/pass3_lifter');
|
|
9
|
+
const { executePass4 } = require('../engine/pass4_signals');
|
|
10
|
+
|
|
11
|
+
// Import authority core detection
|
|
12
|
+
const { detectAuthorityCores, detectHiddenCoupling, detectArchitecturalArchetype } = require('./authority-core-detector');
|
|
13
|
+
|
|
14
|
+
// Import structural context ownership
|
|
15
|
+
const { generateAuthoritativeContext } = require('./structural-context-owner');
|
|
16
|
+
|
|
17
|
+
// Import invariant detector
|
|
18
|
+
const { InvariantDetector } = require('./invariant-detector');
|
|
19
|
+
|
|
20
|
+
// Import invariant analyzer
|
|
21
|
+
const { InvariantAnalyzer } = require('./invariant-analyzer');
|
|
22
|
+
|
|
23
|
+
// Import git utilities
|
|
24
|
+
const { spawnSync } = require('child_process');
|
|
25
|
+
|
|
26
|
+
const { buildContext } = require('./context_builder');
|
|
27
|
+
const { validateContext } = require('../engine/context_validator');
|
|
28
|
+
const { sortContext } = require('../engine/context_sorter');
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Main Scanner Entry Point
|
|
32
|
+
* Orchestrates the 4-pass structural analysis
|
|
33
|
+
*/
|
|
34
|
+
async function scan(directory) {
|
|
35
|
+
console.log(`\n🚀 ARCVISION STRUCTURAL ENGINE (4-PASS)`);
|
|
36
|
+
console.log(` Target: ${directory}\n`);
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const start = Date.now();
|
|
40
|
+
|
|
41
|
+
// --- PRE-SCAN: WORKSPACE DISCOVERY ---
|
|
42
|
+
// Establish global resolution context
|
|
43
|
+
const workspaceScanner = new WorkspaceScanner(directory);
|
|
44
|
+
const workspaceContext = workspaceScanner.scan();
|
|
45
|
+
|
|
46
|
+
// --- PASS 1: FACTS ---
|
|
47
|
+
// Extract raw syntactic facts from files
|
|
48
|
+
const rawNodes = await executePass1(directory);
|
|
49
|
+
|
|
50
|
+
// --- PASS 2: SEMANTICS ---
|
|
51
|
+
// Resolve dependencies, DI, and method calls
|
|
52
|
+
const semanticData = await executePass2(rawNodes, directory, workspaceContext);
|
|
53
|
+
|
|
54
|
+
// --- PASS 3: LIFTING ---
|
|
55
|
+
// Infer structural roles (Service, Store, etc.)
|
|
56
|
+
const liftedData = await executePass3(semanticData);
|
|
57
|
+
|
|
58
|
+
// --- PASS 4: SIGNALS ---
|
|
59
|
+
// Compute graph metrics (Blast Radius, etc.)
|
|
60
|
+
const structuralContext = await executePass4(liftedData);
|
|
61
|
+
|
|
62
|
+
const { nodes, edges, contextSurface, symbols, invariants, ownership, failure_modes, decision_guidance } = structuralContext;
|
|
63
|
+
|
|
64
|
+
console.log(`\n🔍 Analysis Complete (${Date.now() - start}ms)`);
|
|
65
|
+
console.log(` Nodes: ${nodes.length}`);
|
|
66
|
+
console.log(` Edges: ${edges.length}`);
|
|
67
|
+
console.log(` Symbols: ${symbols ? symbols.length : 0}`);
|
|
68
|
+
|
|
69
|
+
// Report parsing errors if any occurred
|
|
70
|
+
if (global.parserErrors && global.parserErrors.length > 0) {
|
|
71
|
+
const uniqueErrors = new Map();
|
|
72
|
+
global.parserErrors.forEach(err => {
|
|
73
|
+
const key = `${err.error}`;
|
|
74
|
+
if (!uniqueErrors.has(key)) {
|
|
75
|
+
uniqueErrors.set(key, { count: 0, examples: [] });
|
|
76
|
+
}
|
|
77
|
+
const errorGroup = uniqueErrors.get(key);
|
|
78
|
+
errorGroup.count++;
|
|
79
|
+
if (errorGroup.examples.length < 3) {
|
|
80
|
+
errorGroup.examples.push(`${err.file}:${err.line}:${err.column}`);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log(`\n⚠️ Parsing Issues Detected (${global.parserErrors.length} files affected):`);
|
|
85
|
+
uniqueErrors.forEach((info, errorType) => {
|
|
86
|
+
console.log(` ${info.count} files: ${errorType}`);
|
|
87
|
+
info.examples.forEach(example => {
|
|
88
|
+
console.log(` - ${example}`);
|
|
89
|
+
});
|
|
90
|
+
if (info.examples.length < info.count) {
|
|
91
|
+
console.log(` ... and ${info.count - info.examples.length} more`);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Clear errors for next run
|
|
96
|
+
delete global.parserErrors;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// --- AUTHORITATIVE STRUCTURAL CONTEXT GENERATION ---
|
|
100
|
+
|
|
101
|
+
// Generate the canonical structural context that ArcVision owns
|
|
102
|
+
const authoritativeContext = generateAuthoritativeContext(structuralContext, {
|
|
103
|
+
projectName: path.basename(directory),
|
|
104
|
+
rootPath: path.resolve(directory),
|
|
105
|
+
commitHash: getGitCommitHash(directory),
|
|
106
|
+
timestamp: new Date().toISOString(),
|
|
107
|
+
version: process.env.npm_package_version || '0.1.0'
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
// Extract specific insights from authoritative context
|
|
111
|
+
const authorityCores = authoritativeContext.structural_context.authority_cores;
|
|
112
|
+
const structuralHubs = authoritativeContext.structural_context.structural_hubs;
|
|
113
|
+
const couplingPatterns = authoritativeContext.structural_context.coupling_patterns;
|
|
114
|
+
const architecturalBoundaries = authoritativeContext.structural_context.architectural_boundaries;
|
|
115
|
+
const structuralInvariants = authoritativeContext.structural_context.structural_invariants;
|
|
116
|
+
|
|
117
|
+
// Legacy format for backward compatibility
|
|
118
|
+
const hiddenCoupling = couplingPatterns;
|
|
119
|
+
const archetype = authoritativeContext.system_identity.architectural_archetype || {
|
|
120
|
+
type: 'general_purpose',
|
|
121
|
+
confidence: 'low',
|
|
122
|
+
description: 'General purpose application',
|
|
123
|
+
authorityPattern: 'distributed_authority'
|
|
124
|
+
};
|
|
125
|
+
const completenessMetrics = authoritativeContext.reliability_indicators.analysis_coverage;
|
|
126
|
+
|
|
127
|
+
// --- DETECT SYSTEM INVARIANTS ---
|
|
128
|
+
|
|
129
|
+
// Detect invariants in the scanned repository
|
|
130
|
+
const invariantDetector = new InvariantDetector();
|
|
131
|
+
const detectedInvariants = await invariantDetector.detectInvariants(
|
|
132
|
+
{ nodes, edges },
|
|
133
|
+
directory
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// Only analyze if we found invariants
|
|
137
|
+
let invariantAnalysis = null;
|
|
138
|
+
let architecturalHealth = null;
|
|
139
|
+
|
|
140
|
+
if (detectedInvariants && detectedInvariants.length > 0) {
|
|
141
|
+
// Analyze the detected invariants
|
|
142
|
+
const invariantAnalyzer = new InvariantAnalyzer();
|
|
143
|
+
invariantAnalysis = invariantAnalyzer.analyzeInvariants(detectedInvariants, {
|
|
144
|
+
directory,
|
|
145
|
+
projectName: path.basename(directory),
|
|
146
|
+
nodeCount: nodes.length,
|
|
147
|
+
edgeCount: edges.length
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Assess architectural health based on invariants
|
|
151
|
+
architecturalHealth = invariantAnalyzer.assessArchitecturalHealth(detectedInvariants, {
|
|
152
|
+
directory,
|
|
153
|
+
projectName: path.basename(directory)
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
// Even if no invariants are detected, create a baseline analysis
|
|
157
|
+
const invariantAnalyzer = new InvariantAnalyzer();
|
|
158
|
+
invariantAnalysis = invariantAnalyzer.analyzeInvariants([], {
|
|
159
|
+
directory,
|
|
160
|
+
projectName: path.basename(directory),
|
|
161
|
+
nodeCount: nodes.length,
|
|
162
|
+
edgeCount: edges.length
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
architecturalHealth = invariantAnalyzer.assessArchitecturalHealth([], {
|
|
166
|
+
directory,
|
|
167
|
+
projectName: path.basename(directory)
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// --- FINAL BUILD & VALIDATION ---
|
|
172
|
+
|
|
173
|
+
// Build the final context object conforming to the schema
|
|
174
|
+
const contextOptions = {
|
|
175
|
+
directory: directory,
|
|
176
|
+
projectName: path.basename(directory),
|
|
177
|
+
contextSurface: contextSurface,
|
|
178
|
+
arcvisionVersion: process.env.npm_package_version || '0.1.0', // Pass version from package.json
|
|
179
|
+
authorityCores,
|
|
180
|
+
hiddenCoupling,
|
|
181
|
+
archetype,
|
|
182
|
+
completenessMetrics,
|
|
183
|
+
// New authoritative context fields
|
|
184
|
+
structuralHubs,
|
|
185
|
+
architecturalBoundaries,
|
|
186
|
+
structuralInvariants,
|
|
187
|
+
authoritativeContext,
|
|
188
|
+
// Include detected invariants
|
|
189
|
+
detectedInvariants,
|
|
190
|
+
// Include invariant analysis results
|
|
191
|
+
invariantAnalysis,
|
|
192
|
+
// Include architectural health assessment
|
|
193
|
+
architecturalHealth,
|
|
194
|
+
// Pass additional metadata
|
|
195
|
+
commitHash: getGitCommitHash(directory),
|
|
196
|
+
rootPath: path.resolve(directory)
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Prepare context options without archetype initially
|
|
200
|
+
const { archetype: originalArchetype, ...otherContextOptions } = contextOptions;
|
|
201
|
+
|
|
202
|
+
const finalContextOptions = {
|
|
203
|
+
directory: directory,
|
|
204
|
+
projectName: path.basename(directory),
|
|
205
|
+
language: 'javascript',
|
|
206
|
+
// Include symbols in options
|
|
207
|
+
symbols: symbols,
|
|
208
|
+
...otherContextOptions,
|
|
209
|
+
// Include new canonical sections from Pass 4
|
|
210
|
+
invariants,
|
|
211
|
+
ownership,
|
|
212
|
+
failure_modes,
|
|
213
|
+
decision_guidance
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
// Explicitly set archetype to ensure it's a proper object
|
|
217
|
+
if (originalArchetype && typeof originalArchetype === 'object' &&
|
|
218
|
+
originalArchetype.type && originalArchetype.confidence) {
|
|
219
|
+
finalContextOptions.archetype = originalArchetype;
|
|
220
|
+
} else {
|
|
221
|
+
finalContextOptions.archetype = {
|
|
222
|
+
type: 'general_purpose',
|
|
223
|
+
confidence: 'low',
|
|
224
|
+
description: 'General purpose application',
|
|
225
|
+
authorityPattern: 'distributed_authority'
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Build context and ensure archetype is properly set
|
|
230
|
+
let context = buildContext(nodes, edges, finalContextOptions);
|
|
231
|
+
|
|
232
|
+
// Make sure archetype is set properly in the system section
|
|
233
|
+
if (!context.system.archetype || context.system.archetype === null) {
|
|
234
|
+
context.system.archetype = finalContextOptions.archetype || {
|
|
235
|
+
type: 'general_purpose',
|
|
236
|
+
confidence: 'low',
|
|
237
|
+
description: 'General purpose application',
|
|
238
|
+
authorityPattern: 'distributed_authority'
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log(' Validating structural context...');
|
|
243
|
+
|
|
244
|
+
// Validate against JSON schema
|
|
245
|
+
let validation = validateContext(context);
|
|
246
|
+
|
|
247
|
+
if (!validation.valid) {
|
|
248
|
+
console.warn(' ⚠️ Validation Issues Found:');
|
|
249
|
+
validation.errors.forEach(e => console.warn(' -', e));
|
|
250
|
+
console.warn(' ⚠️ Proceeding despite validation issues to see results');
|
|
251
|
+
// Continue processing instead of throwing error
|
|
252
|
+
// throw new Error("Context validation failed");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Deterministic sort for consistent hashing/snapshots
|
|
256
|
+
const sortedContext = sortContext(context);
|
|
257
|
+
|
|
258
|
+
console.log(' ✅ Context Verified & Ready');
|
|
259
|
+
|
|
260
|
+
return sortedContext;
|
|
261
|
+
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error('❌ Scanner Failed:', err);
|
|
264
|
+
throw err;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Get git commit hash for the repository
|
|
270
|
+
* @param {string} directory - Repository directory
|
|
271
|
+
* @returns {string} Commit hash or 'unknown'
|
|
272
|
+
*/
|
|
273
|
+
function getGitCommitHash(directory) {
|
|
274
|
+
try {
|
|
275
|
+
const result = spawnSync('git', ['rev-parse', 'HEAD'], {
|
|
276
|
+
cwd: directory,
|
|
277
|
+
stdio: ['pipe', 'pipe', 'pipe']
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
if (result.status === 0) {
|
|
281
|
+
return result.stdout.toString().trim();
|
|
282
|
+
}
|
|
283
|
+
return 'unknown';
|
|
284
|
+
} catch (error) {
|
|
285
|
+
return 'unknown';
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
module.exports = { scan, getGitCommitHash };
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Builds cross-file symbol resolution and tracks actual usage of imported symbols.
|
|
5
|
+
* This enables detection of which files actually USE imported functions/classes,
|
|
6
|
+
* not just which files import them.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Build a global symbol table from all file exports
|
|
11
|
+
* @param {Array} nodes - All scanned file nodes with metadata
|
|
12
|
+
* @returns {Map} Symbol table mapping symbol names to their source files
|
|
13
|
+
*/
|
|
14
|
+
function buildSymbolTable(nodes) {
|
|
15
|
+
const symbolTable = new Map();
|
|
16
|
+
|
|
17
|
+
nodes.forEach(node => {
|
|
18
|
+
const filePath = node.id;
|
|
19
|
+
const metadata = node.metadata;
|
|
20
|
+
|
|
21
|
+
if (!metadata) return;
|
|
22
|
+
|
|
23
|
+
// Register all exports from this file
|
|
24
|
+
if (metadata.exports && Array.isArray(metadata.exports)) {
|
|
25
|
+
metadata.exports.forEach(exp => {
|
|
26
|
+
const symbolName = exp.name;
|
|
27
|
+
if (!symbolTable.has(symbolName)) {
|
|
28
|
+
symbolTable.set(symbolName, []);
|
|
29
|
+
}
|
|
30
|
+
symbolTable.get(symbolName).push({
|
|
31
|
+
file: filePath,
|
|
32
|
+
type: exp.type,
|
|
33
|
+
loc: exp.loc
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Register all functions
|
|
39
|
+
if (metadata.functions && Array.isArray(metadata.functions)) {
|
|
40
|
+
metadata.functions.forEach(func => {
|
|
41
|
+
const symbolName = func.name;
|
|
42
|
+
if (!symbolTable.has(symbolName)) {
|
|
43
|
+
symbolTable.set(symbolName, []);
|
|
44
|
+
}
|
|
45
|
+
symbolTable.get(symbolName).push({
|
|
46
|
+
file: filePath,
|
|
47
|
+
type: 'function',
|
|
48
|
+
loc: func.loc
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Register all classes
|
|
54
|
+
if (metadata.classes && Array.isArray(metadata.classes)) {
|
|
55
|
+
metadata.classes.forEach(cls => {
|
|
56
|
+
const symbolName = cls.name;
|
|
57
|
+
if (!symbolTable.has(symbolName)) {
|
|
58
|
+
symbolTable.set(symbolName, []);
|
|
59
|
+
}
|
|
60
|
+
symbolTable.get(symbolName).push({
|
|
61
|
+
file: filePath,
|
|
62
|
+
type: 'class',
|
|
63
|
+
loc: cls.loc
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return symbolTable;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Resolve symbol usage to their definitions
|
|
74
|
+
* @param {Object} node - File node with metadata
|
|
75
|
+
* @param {Map} symbolTable - Global symbol table
|
|
76
|
+
* @returns {Array} Array of resolved symbol usages
|
|
77
|
+
*/
|
|
78
|
+
function resolveSymbolUsage(node, symbolTable) {
|
|
79
|
+
const resolvedUsages = [];
|
|
80
|
+
const metadata = node.metadata;
|
|
81
|
+
|
|
82
|
+
if (!metadata) return resolvedUsages;
|
|
83
|
+
|
|
84
|
+
// Track which symbols this file imports
|
|
85
|
+
const importedSymbols = new Map();
|
|
86
|
+
if (metadata.imports && Array.isArray(metadata.imports)) {
|
|
87
|
+
metadata.imports.forEach(imp => {
|
|
88
|
+
if (imp.specifiers && Array.isArray(imp.specifiers)) {
|
|
89
|
+
imp.specifiers.forEach(spec => {
|
|
90
|
+
const localName = spec.local || spec.imported || spec;
|
|
91
|
+
const importedName = spec.imported || spec.local || spec;
|
|
92
|
+
importedSymbols.set(localName, {
|
|
93
|
+
source: imp.source,
|
|
94
|
+
originalName: importedName
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Check function calls against imported symbols
|
|
102
|
+
if (metadata.functionCalls && Array.isArray(metadata.functionCalls)) {
|
|
103
|
+
metadata.functionCalls.forEach(call => {
|
|
104
|
+
const callName = call.name;
|
|
105
|
+
if (importedSymbols.has(callName)) {
|
|
106
|
+
const importInfo = importedSymbols.get(callName);
|
|
107
|
+
resolvedUsages.push({
|
|
108
|
+
type: 'function_call',
|
|
109
|
+
symbol: callName,
|
|
110
|
+
source: importInfo.source,
|
|
111
|
+
loc: call.loc
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Check constructor calls against imported symbols
|
|
118
|
+
if (metadata.constructorCalls && Array.isArray(metadata.constructorCalls)) {
|
|
119
|
+
metadata.constructorCalls.forEach(call => {
|
|
120
|
+
const className = call.className;
|
|
121
|
+
if (importedSymbols.has(className)) {
|
|
122
|
+
const importInfo = importedSymbols.get(className);
|
|
123
|
+
resolvedUsages.push({
|
|
124
|
+
type: 'constructor_call',
|
|
125
|
+
symbol: className,
|
|
126
|
+
source: importInfo.source,
|
|
127
|
+
loc: call.loc
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Check component usage (React/JSX)
|
|
134
|
+
if (metadata.componentUsage && Array.isArray(metadata.componentUsage)) {
|
|
135
|
+
metadata.componentUsage.forEach(comp => {
|
|
136
|
+
const componentName = comp.component;
|
|
137
|
+
if (importedSymbols.has(componentName)) {
|
|
138
|
+
const importInfo = importedSymbols.get(componentName);
|
|
139
|
+
resolvedUsages.push({
|
|
140
|
+
type: 'component_usage',
|
|
141
|
+
symbol: componentName,
|
|
142
|
+
source: importInfo.source,
|
|
143
|
+
loc: comp.loc
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return resolvedUsages;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Track cross-file references and build usage edges
|
|
154
|
+
* @param {Array} nodes - All scanned file nodes
|
|
155
|
+
* @param {Map} symbolTable - Global symbol table
|
|
156
|
+
* @returns {Array} Array of usage edges
|
|
157
|
+
*/
|
|
158
|
+
function trackCrossFileReferences(nodes, symbolTable) {
|
|
159
|
+
const usageEdges = [];
|
|
160
|
+
|
|
161
|
+
nodes.forEach(node => {
|
|
162
|
+
const filePath = node.id;
|
|
163
|
+
const resolvedUsages = resolveSymbolUsage(node, symbolTable);
|
|
164
|
+
|
|
165
|
+
resolvedUsages.forEach(usage => {
|
|
166
|
+
// Create an edge from this file to the source of the symbol
|
|
167
|
+
usageEdges.push({
|
|
168
|
+
source: filePath,
|
|
169
|
+
target: usage.source,
|
|
170
|
+
type: usage.type,
|
|
171
|
+
symbol: usage.symbol,
|
|
172
|
+
loc: usage.loc
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return usageEdges;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Analyze semantic relationships in the codebase
|
|
182
|
+
* @param {Array} nodes - All scanned file nodes
|
|
183
|
+
* @returns {Object} Semantic analysis results
|
|
184
|
+
*/
|
|
185
|
+
function analyzeSemantics(nodes) {
|
|
186
|
+
const symbolTable = buildSymbolTable(nodes);
|
|
187
|
+
const usageEdges = trackCrossFileReferences(nodes, symbolTable);
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
symbolTable,
|
|
191
|
+
usageEdges,
|
|
192
|
+
stats: {
|
|
193
|
+
totalSymbols: symbolTable.size,
|
|
194
|
+
totalUsages: usageEdges.length
|
|
195
|
+
}
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
module.exports = {
|
|
200
|
+
buildSymbolTable,
|
|
201
|
+
resolveSymbolUsage,
|
|
202
|
+
trackCrossFileReferences,
|
|
203
|
+
analyzeSemantics
|
|
204
|
+
};
|