arcvision 0.2.14 → 0.2.16
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,548 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Invariant Detector - Detects architectural invariants in scanned repositories
|
|
3
|
+
* Analyzes code structure to identify system-level constraints that must always hold
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const fs = require('fs').promises;
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
class InvariantDetector {
|
|
10
|
+
constructor() {
|
|
11
|
+
this.detectedInvariants = [];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Detect invariants in a scanned codebase
|
|
18
|
+
* @param {Object} scanResult - The result from the scanner
|
|
19
|
+
* @param {string} directory - The directory that was scanned
|
|
20
|
+
* @returns {Array} - Array of detected invariants
|
|
21
|
+
*/
|
|
22
|
+
async detectInvariants(scanResult, directory) {
|
|
23
|
+
this.detectedInvariants = [];
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
// Analyze the structural context for potential invariants
|
|
27
|
+
await this.analyzeNodesForInvariants(scanResult.nodes, directory);
|
|
28
|
+
await this.analyzeEdgesForInvariants(scanResult.edges);
|
|
29
|
+
await this.analyzeCodeFilesForInvariants(directory);
|
|
30
|
+
|
|
31
|
+
console.log(`\n🛡️ Invariant Detection Complete`);
|
|
32
|
+
console.log(` Found ${this.detectedInvariants.length} potential system invariants`);
|
|
33
|
+
|
|
34
|
+
return this.detectedInvariants;
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error('Invariant detection failed:', error.message);
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Analyze nodes for invariant patterns
|
|
43
|
+
*/
|
|
44
|
+
async analyzeNodesForInvariants(nodes, directory) {
|
|
45
|
+
for (const node of nodes) {
|
|
46
|
+
if (!node.path) continue;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const filePath = path.join(directory, node.path);
|
|
50
|
+
if (await this.isFileAccessible(filePath)) {
|
|
51
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
52
|
+
|
|
53
|
+
// Look for validation/assertion patterns in the file
|
|
54
|
+
const fileInvariants = this.extractInvariantsFromFile(content, node.path);
|
|
55
|
+
|
|
56
|
+
for (const invariant of fileInvariants) {
|
|
57
|
+
const nodeId = node.path || node.id || 'unknown';
|
|
58
|
+
this.detectedInvariants.push({
|
|
59
|
+
id: this.generateInvariantId(invariant.pattern, nodeId),
|
|
60
|
+
statement: invariant.description,
|
|
61
|
+
description: invariant.description,
|
|
62
|
+
scope: this.determineScopeFromPath(nodeId),
|
|
63
|
+
critical_path: invariant.isCritical,
|
|
64
|
+
assertion: `Pattern: ${invariant.pattern}`,
|
|
65
|
+
status: 'suspected',
|
|
66
|
+
confidence: this.calculateConfidenceScore(invariant.pattern, nodeId, content),
|
|
67
|
+
source_file: nodeId,
|
|
68
|
+
detected_at: new Date().toISOString()
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// Skip files that can't be read
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Analyze edges for potential invariant relationships
|
|
81
|
+
*/
|
|
82
|
+
async analyzeEdgesForInvariants(edges) {
|
|
83
|
+
// Look for dependency patterns that suggest invariants
|
|
84
|
+
const dependencyConstraints = this.identifyDependencyInvariants(edges);
|
|
85
|
+
|
|
86
|
+
for (const constraint of dependencyConstraints) {
|
|
87
|
+
this.detectedInvariants.push({
|
|
88
|
+
id: this.generateInvariantId('dependency_constraint', constraint.from + '_' + constraint.to),
|
|
89
|
+
statement: constraint.description,
|
|
90
|
+
description: constraint.description,
|
|
91
|
+
scope: 'boundary',
|
|
92
|
+
critical_path: constraint.critical,
|
|
93
|
+
assertion: `Dependency constraint: ${constraint.from} -> ${constraint.to}`,
|
|
94
|
+
status: 'suspected',
|
|
95
|
+
confidence: this.calculateConfidenceScore('dependency_constraint', constraint.from, ''),
|
|
96
|
+
source_relationship: `${constraint.from} -> ${constraint.to}`,
|
|
97
|
+
detected_at: new Date().toISOString()
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Analyze code files directly for invariant patterns
|
|
104
|
+
*/
|
|
105
|
+
async analyzeCodeFilesForInvariants(directory) {
|
|
106
|
+
const invariantFiles = await this.findInvariantRelatedFiles(directory);
|
|
107
|
+
|
|
108
|
+
for (const filePath of invariantFiles) {
|
|
109
|
+
try {
|
|
110
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
111
|
+
const relativePath = path.relative(directory, filePath);
|
|
112
|
+
|
|
113
|
+
// Look for common invariant implementation patterns
|
|
114
|
+
const patterns = this.searchForInvariantPatterns(content);
|
|
115
|
+
|
|
116
|
+
for (const pattern of patterns) {
|
|
117
|
+
this.detectedInvariants.push({
|
|
118
|
+
id: this.generateInvariantId(pattern.type, relativePath),
|
|
119
|
+
statement: `System invariant implemented in ${relativePath}: ${pattern.description}`,
|
|
120
|
+
description: `System invariant implemented in ${relativePath}: ${pattern.description}`,
|
|
121
|
+
scope: 'system',
|
|
122
|
+
critical_path: true, // File specifically for invariants is likely critical
|
|
123
|
+
assertion: pattern.assertion,
|
|
124
|
+
status: 'suspected',
|
|
125
|
+
confidence: this.calculateConfidenceScore(pattern.type, relativePath, content),
|
|
126
|
+
source_file: relativePath,
|
|
127
|
+
implementation_type: pattern.type,
|
|
128
|
+
detected_at: new Date().toISOString()
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
} catch (error) {
|
|
132
|
+
// Skip files that can't be read
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Extract invariants from file content based on patterns
|
|
140
|
+
*/
|
|
141
|
+
extractInvariantsFromFile(content, filePath) {
|
|
142
|
+
const invariants = [];
|
|
143
|
+
|
|
144
|
+
// Check for validation function patterns
|
|
145
|
+
const validationPatterns = [
|
|
146
|
+
/function\s+validate\w+/gi,
|
|
147
|
+
/function\s+check\w+/gi,
|
|
148
|
+
/function\s+ensure\w+/gi,
|
|
149
|
+
/function\s+guard\w+/gi,
|
|
150
|
+
/function\s+verify\w+/gi,
|
|
151
|
+
/function\s+assert\w+/gi,
|
|
152
|
+
/function\s+enforce\w+/gi
|
|
153
|
+
];
|
|
154
|
+
|
|
155
|
+
for (const pattern of validationPatterns) {
|
|
156
|
+
const matches = content.match(pattern);
|
|
157
|
+
if (matches) {
|
|
158
|
+
invariants.push({
|
|
159
|
+
pattern: pattern.source,
|
|
160
|
+
description: `Validation function pattern found in ${filePath}: ${matches.length} matches`,
|
|
161
|
+
isCritical: true
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Check for error handling patterns that suggest invariant enforcement
|
|
167
|
+
const errorHandlingPatterns = [
|
|
168
|
+
/try\s*\{/gi,
|
|
169
|
+
/catch\s*\(/gi,
|
|
170
|
+
/throw\s+new/gi,
|
|
171
|
+
/if\s*\([^)]+\)\s*throw/gi,
|
|
172
|
+
/error\.message/gi
|
|
173
|
+
];
|
|
174
|
+
|
|
175
|
+
for (const pattern of errorHandlingPatterns) {
|
|
176
|
+
const match = content.match(pattern);
|
|
177
|
+
if (match) {
|
|
178
|
+
invariants.push({
|
|
179
|
+
pattern: pattern.source,
|
|
180
|
+
description: `Constraint enforcement pattern found in ${filePath}`,
|
|
181
|
+
isCritical: false
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Check for state management patterns
|
|
187
|
+
const stateManagementPatterns = [
|
|
188
|
+
/setState\s*\(/gi,
|
|
189
|
+
/dispatch\s*\(/gi,
|
|
190
|
+
/useReducer/gi,
|
|
191
|
+
/useState/gi,
|
|
192
|
+
/store\./gi,
|
|
193
|
+
/state\./gi,
|
|
194
|
+
/state\s*=\s*/gi
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
for (const pattern of stateManagementPatterns) {
|
|
198
|
+
const match = content.match(pattern);
|
|
199
|
+
if (match) {
|
|
200
|
+
invariants.push({
|
|
201
|
+
pattern: pattern.source,
|
|
202
|
+
description: `State transition constraint pattern found in ${filePath}`,
|
|
203
|
+
isCritical: true
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Check for boundary enforcement patterns
|
|
209
|
+
const boundaryCheckPatterns = [
|
|
210
|
+
/boundary\s*check/gi,
|
|
211
|
+
/range\s*check/gi,
|
|
212
|
+
/limit\s*check/gi,
|
|
213
|
+
/max\s*\w*\s*>/gi,
|
|
214
|
+
/min\s*\w*\s*</gi,
|
|
215
|
+
/within\s*bound/gi,
|
|
216
|
+
/validate\s*input/gi,
|
|
217
|
+
/sanitiz/gi
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
for (const pattern of boundaryCheckPatterns) {
|
|
221
|
+
const match = content.match(pattern);
|
|
222
|
+
if (match) {
|
|
223
|
+
invariants.push({
|
|
224
|
+
pattern: pattern.source,
|
|
225
|
+
description: `Boundary enforcement pattern found in ${filePath}`,
|
|
226
|
+
isCritical: true
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Check for invariant indicators from pass 1
|
|
232
|
+
const invariantIndicators = this.extractDetailedInvariantIndicators(content, filePath);
|
|
233
|
+
for (const indicator of invariantIndicators) {
|
|
234
|
+
invariants.push(indicator);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return invariants;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Extract detailed invariant indicators from content
|
|
242
|
+
*/
|
|
243
|
+
extractDetailedInvariantIndicators(content, filePath) {
|
|
244
|
+
const indicators = [];
|
|
245
|
+
|
|
246
|
+
// Look for common invariant/pattern validation patterns
|
|
247
|
+
const patterns = [
|
|
248
|
+
// Assertion and validation patterns
|
|
249
|
+
{ pattern: /assert\(/gi, description: 'Assertion-based invariant check', critical: true },
|
|
250
|
+
{ pattern: /console\.assert\(/gi, description: 'Console assertion pattern', critical: true },
|
|
251
|
+
{ pattern: /validate[A-Z]/gi, description: 'Validate function pattern', critical: false },
|
|
252
|
+
{ pattern: /check[A-Z]/gi, description: 'Check function pattern', critical: false },
|
|
253
|
+
{ pattern: /ensure[A-Z]/gi, description: 'Ensure function pattern', critical: true },
|
|
254
|
+
{ pattern: /guard[A-Z]/gi, description: 'Guard function pattern', critical: true },
|
|
255
|
+
{ pattern: /verify[A-Z]/gi, description: 'Verify function pattern', critical: false },
|
|
256
|
+
{ pattern: /validate\(/gi, description: 'Validation function call', critical: false },
|
|
257
|
+
|
|
258
|
+
// Constraint and invariant mentions
|
|
259
|
+
{ pattern: /Invariant/gi, description: 'Invariant mention', critical: true },
|
|
260
|
+
{ pattern: /constraint/gi, description: 'Constraint mention', critical: true },
|
|
261
|
+
{ pattern: /require\(/gi, description: 'Requirement assertion', critical: true },
|
|
262
|
+
{ pattern: /enforce/gi, description: 'Enforcement pattern', critical: true },
|
|
263
|
+
|
|
264
|
+
// Conditional guard patterns
|
|
265
|
+
{ pattern: /must.*be/gi, description: 'Must-be pattern', critical: true },
|
|
266
|
+
{ pattern: /should.*be/gi, description: 'Should-be pattern', critical: false },
|
|
267
|
+
{ pattern: /expected.*to/gi, description: 'Expected-to pattern', critical: false },
|
|
268
|
+
{ pattern: /if.*!\s*[^\s]+\s*throw/gi, description: 'Guard clause pattern', critical: true },
|
|
269
|
+
{ pattern: /if.*!\s*[^\s]+\s*reject/gi, description: 'Promise rejection guard', critical: true },
|
|
270
|
+
{ pattern: /throw.*Error/gi, description: 'Error throwing pattern', critical: true },
|
|
271
|
+
|
|
272
|
+
// Boundary and validation patterns
|
|
273
|
+
{ pattern: /boundary.*check/gi, description: 'Boundary check', critical: true },
|
|
274
|
+
{ pattern: /range.*check/gi, description: 'Range check', critical: true },
|
|
275
|
+
{ pattern: /within.*bounds/gi, description: 'Bounds check', critical: true },
|
|
276
|
+
{ pattern: /validat(e|ion)/gi, description: 'Validation pattern', critical: false },
|
|
277
|
+
{ pattern: /guard/i, description: 'Guard pattern', critical: true },
|
|
278
|
+
|
|
279
|
+
// Access control and security patterns
|
|
280
|
+
{ pattern: /access.*control/gi, description: 'Access control', critical: true },
|
|
281
|
+
{ pattern: /permission.*check/gi, description: 'Permission check', critical: true },
|
|
282
|
+
{ pattern: /auth(?:oriz|enticat)/gi, description: 'Authorization/authentication', critical: true },
|
|
283
|
+
{ pattern: /acl/gi, description: 'Access Control List', critical: true },
|
|
284
|
+
|
|
285
|
+
// State management patterns
|
|
286
|
+
{ pattern: /state.*transition/gi, description: 'State transition', critical: true },
|
|
287
|
+
{ pattern: /setState/gi, description: 'State setter', critical: true },
|
|
288
|
+
{ pattern: /update.*state/gi, description: 'State updater', critical: true },
|
|
289
|
+
{ pattern: /transition/gi, description: 'Transition pattern', critical: true },
|
|
290
|
+
{ pattern: /immutable/gi, description: 'Immutability pattern', critical: true },
|
|
291
|
+
|
|
292
|
+
// Type and schema validation patterns
|
|
293
|
+
{ pattern: /zod/gi, description: 'Zod validation schema', critical: false },
|
|
294
|
+
{ pattern: /joi/gi, description: 'Joi validation schema', critical: false },
|
|
295
|
+
{ pattern: /yup/gi, description: 'Yup validation schema', critical: false },
|
|
296
|
+
{ pattern: /ajv/gi, description: 'Ajv validation schema', critical: false },
|
|
297
|
+
{ pattern: /schema/gi, description: 'Schema definition', critical: false },
|
|
298
|
+
|
|
299
|
+
// Configuration patterns
|
|
300
|
+
{ pattern: /config.*environ/gi, description: 'Environment configuration', critical: true },
|
|
301
|
+
{ pattern: /env.*validation/gi, description: 'Environment validation', critical: true }
|
|
302
|
+
];
|
|
303
|
+
|
|
304
|
+
for (const item of patterns) {
|
|
305
|
+
if (!item.pattern || !content) continue; // Skip if pattern or content is undefined
|
|
306
|
+
let match;
|
|
307
|
+
try {
|
|
308
|
+
while ((match = item.pattern.exec(content)) !== null) {
|
|
309
|
+
// Avoid duplicate matches by position
|
|
310
|
+
const position = item.pattern.lastIndex;
|
|
311
|
+
const prevMatch = indicators.find(ind => ind.pattern === item.pattern.source &&
|
|
312
|
+
ind.description.includes(match[0]));
|
|
313
|
+
if (!prevMatch) {
|
|
314
|
+
indicators.push({
|
|
315
|
+
pattern: item.pattern.source,
|
|
316
|
+
description: `${item.description} found in ${filePath}: '${match[0]}' at position ${position}`,
|
|
317
|
+
isCritical: item.critical
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} catch (error) {
|
|
322
|
+
// Continue if regex execution fails
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return indicators;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Identify dependency-based invariants from edges
|
|
332
|
+
*/
|
|
333
|
+
identifyDependencyInvariants(edges) {
|
|
334
|
+
const constraints = [];
|
|
335
|
+
|
|
336
|
+
// Look for patterns like core modules that should not depend on peripheral modules
|
|
337
|
+
const coreModules = new Set();
|
|
338
|
+
const peripheralModules = new Set();
|
|
339
|
+
|
|
340
|
+
for (const edge of edges) {
|
|
341
|
+
// Identify potential architectural layers based on file paths
|
|
342
|
+
if (this.isCoreLayer(edge.from) && this.isPeripheralLayer(edge.to)) {
|
|
343
|
+
// This suggests an invariant: core modules shouldn't depend on peripheral modules
|
|
344
|
+
constraints.push({
|
|
345
|
+
from: edge.from,
|
|
346
|
+
to: edge.to,
|
|
347
|
+
description: `Potential architectural invariant: Core module ${edge.from} depends on peripheral module ${edge.to}`,
|
|
348
|
+
critical: true
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return constraints;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Find files that are likely related to invariant implementation
|
|
358
|
+
*/
|
|
359
|
+
async findInvariantRelatedFiles(directory) {
|
|
360
|
+
const invariantFiles = [];
|
|
361
|
+
|
|
362
|
+
const walk = async (dir) => {
|
|
363
|
+
try {
|
|
364
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
365
|
+
|
|
366
|
+
for (const entry of entries) {
|
|
367
|
+
const fullPath = path.join(dir, entry.name);
|
|
368
|
+
|
|
369
|
+
if (entry.isDirectory()) {
|
|
370
|
+
await walk(fullPath);
|
|
371
|
+
} else if (entry.isFile()) {
|
|
372
|
+
const fileName = entry.name.toLowerCase();
|
|
373
|
+
|
|
374
|
+
// Look for files that commonly contain invariants
|
|
375
|
+
if (
|
|
376
|
+
fileName.includes('invariant') ||
|
|
377
|
+
fileName.includes('validation') ||
|
|
378
|
+
fileName.includes('guard') ||
|
|
379
|
+
fileName.includes('assert') ||
|
|
380
|
+
fileName.includes('constraint') ||
|
|
381
|
+
fileName.includes('boundary') ||
|
|
382
|
+
fileName.includes('policy') ||
|
|
383
|
+
fileName.includes('rule')
|
|
384
|
+
) {
|
|
385
|
+
invariantFiles.push(fullPath);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
} catch (error) {
|
|
390
|
+
// Skip directories we can't access
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
|
|
394
|
+
await walk(directory);
|
|
395
|
+
return invariantFiles;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Search for specific invariant implementation patterns
|
|
400
|
+
*/
|
|
401
|
+
searchForInvariantPatterns(content) {
|
|
402
|
+
const patterns = [];
|
|
403
|
+
|
|
404
|
+
// Look for assertion/verification patterns
|
|
405
|
+
if (content.includes('assert(') || content.includes('AssertionError')) {
|
|
406
|
+
patterns.push({
|
|
407
|
+
type: 'assertion',
|
|
408
|
+
description: 'Assertion-based invariant checking found',
|
|
409
|
+
assertion: 'Code contains assert() calls for invariant enforcement'
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Look for validation patterns
|
|
414
|
+
if (content.includes('validate(') || content.includes('isValid') || content.includes('checkValid')) {
|
|
415
|
+
patterns.push({
|
|
416
|
+
type: 'validation',
|
|
417
|
+
description: 'Validation-based invariant checking found',
|
|
418
|
+
assertion: 'Code contains validation functions for invariant enforcement'
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Look for guard clauses
|
|
423
|
+
if (content.includes('if (!condition) throw') || content.includes('guard clause')) {
|
|
424
|
+
patterns.push({
|
|
425
|
+
type: 'guard_clause',
|
|
426
|
+
description: 'Guard clause invariant enforcement found',
|
|
427
|
+
assertion: 'Code contains early-exit guard clauses for invariant enforcement'
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Look for state machine patterns
|
|
432
|
+
if (content.includes('state') && (content.includes('transition') || content.includes('transitions'))) {
|
|
433
|
+
patterns.push({
|
|
434
|
+
type: 'state_machine',
|
|
435
|
+
description: 'State transition invariant found',
|
|
436
|
+
assertion: 'Code implements state machine with transition constraints'
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
return patterns;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* Determine scope based on file path
|
|
445
|
+
*/
|
|
446
|
+
determineScopeFromPath(filePath) {
|
|
447
|
+
if (!filePath) return 'module'; // Default to module if no path provided
|
|
448
|
+
if (filePath.includes('/core/') || filePath.includes('/lib/') || filePath.includes('/shared/')) {
|
|
449
|
+
return 'system';
|
|
450
|
+
} else if (filePath.includes('/modules/') || filePath.includes('/components/')) {
|
|
451
|
+
return 'boundary';
|
|
452
|
+
} else {
|
|
453
|
+
return 'module';
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
/**
|
|
458
|
+
* Check if file is accessible
|
|
459
|
+
*/
|
|
460
|
+
async isFileAccessible(filePath) {
|
|
461
|
+
try {
|
|
462
|
+
await fs.access(filePath);
|
|
463
|
+
return true;
|
|
464
|
+
} catch {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
/**
|
|
470
|
+
* Check if path represents a core layer
|
|
471
|
+
*/
|
|
472
|
+
isCoreLayer(filePath) {
|
|
473
|
+
if (!filePath) return false;
|
|
474
|
+
return filePath.includes('/core/') ||
|
|
475
|
+
filePath.includes('/lib/') ||
|
|
476
|
+
filePath.includes('/shared/') ||
|
|
477
|
+
filePath.includes('config') ||
|
|
478
|
+
filePath.includes('auth');
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Check if path represents a peripheral layer
|
|
483
|
+
*/
|
|
484
|
+
isPeripheralLayer(filePath) {
|
|
485
|
+
if (!filePath) return false;
|
|
486
|
+
return filePath.includes('/views/') ||
|
|
487
|
+
filePath.includes('/templates/') ||
|
|
488
|
+
filePath.includes('/ui/') ||
|
|
489
|
+
filePath.includes('/tests/') ||
|
|
490
|
+
filePath.includes('/mocks/');
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Generate unique invariant ID
|
|
495
|
+
*/
|
|
496
|
+
generateInvariantId(pattern, context) {
|
|
497
|
+
const baseId = `${pattern}_${context}`.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
498
|
+
return baseId.substring(0, 100); // Limit length
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Calculate confidence score for an invariant based on various factors
|
|
503
|
+
*/
|
|
504
|
+
calculateConfidenceScore(pattern, context, content = '') {
|
|
505
|
+
let score = 0.5; // Base confidence
|
|
506
|
+
|
|
507
|
+
// Increase confidence based on pattern type
|
|
508
|
+
if (pattern.includes('assert') || pattern.includes('validate') || pattern.includes('guard')) {
|
|
509
|
+
score += 0.2;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (pattern.includes('state') || pattern.includes('mutation') || pattern.includes('setState')) {
|
|
513
|
+
score += 0.15;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// Increase confidence if context suggests critical file
|
|
517
|
+
if (context.includes('core') || context.includes('lib') || context.includes('shared')) {
|
|
518
|
+
score += 0.1;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
// Increase confidence based on frequency of pattern in content
|
|
522
|
+
if (content && typeof content === 'string') {
|
|
523
|
+
const matches = content.match(new RegExp(pattern, 'gi'));
|
|
524
|
+
if (matches && matches.length > 1) {
|
|
525
|
+
score += Math.min(matches.length * 0.05, 0.2); // Up to 0.2 for multiple matches
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// Ensure score stays within bounds
|
|
530
|
+
return Math.min(Math.max(score, 0.1), 1.0);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Determine the owner of an invariant based on the source file
|
|
535
|
+
*/
|
|
536
|
+
determineOwnerFromPath(filePath) {
|
|
537
|
+
if (!filePath) return 'unknown';
|
|
538
|
+
|
|
539
|
+
// Extract function or module name from file path
|
|
540
|
+
const parts = filePath.split(/[\/]/);
|
|
541
|
+
const fileName = parts[parts.length - 1];
|
|
542
|
+
const baseName = fileName.replace(/\.[^/.]+$/, ''); // Remove extension
|
|
543
|
+
|
|
544
|
+
return baseName;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
module.exports = { InvariantDetector };
|