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.
Files changed (134) hide show
  1. package/ARCVISION_DIRECTORY_STRUCTURE.md +104 -0
  2. package/CLI_STRUCTURE.md +110 -0
  3. package/CONFIGURATION.md +119 -0
  4. package/IMPLEMENTATION_SUMMARY.md +99 -0
  5. package/README.md +149 -89
  6. package/architecture.authority.ledger.json +46 -0
  7. package/arcvision-0.2.3.tgz +0 -0
  8. package/arcvision-0.2.4.tgz +0 -0
  9. package/arcvision-0.2.5.tgz +0 -0
  10. package/arcvision.context.diff.json +2181 -0
  11. package/arcvision.context.json +1021 -0
  12. package/arcvision.context.v1.json +2163 -0
  13. package/arcvision.context.v2.json +2173 -0
  14. package/arcvision_context/README.md +93 -0
  15. package/arcvision_context/architecture.authority.ledger.json +83 -0
  16. package/arcvision_context/arcvision.context.json +6884 -0
  17. package/debug-cycle-detection.js +56 -0
  18. package/dist/index.js +1626 -25
  19. package/docs/ENHANCED_ACCURACY_SAFETY_PROTOCOL.md +172 -0
  20. package/docs/accuracy-enhancement-artifacts/enhanced-validation-config.json +98 -0
  21. package/docs/acig-robustness-guide.md +164 -0
  22. package/docs/authoritative-gate-implementation.md +168 -0
  23. package/docs/cli-strengthening-summary.md +232 -0
  24. package/docs/invariant-system-summary.md +100 -0
  25. package/docs/invariant-system.md +112 -0
  26. package/generate_large_test.js +42 -0
  27. package/large_test_repo.json +1 -0
  28. package/output1.json +2163 -0
  29. package/output2.json +2163 -0
  30. package/package.json +46 -36
  31. package/scan_calcom_report.txt +0 -0
  32. package/scan_leafmint_report.txt +0 -0
  33. package/scan_output.txt +0 -0
  34. package/scan_trigger_report.txt +0 -0
  35. package/schema/arcvision_context_schema_v1.json +136 -1
  36. package/src/arcvision-guard.js +433 -0
  37. package/src/core/authority-core-detector.js +382 -0
  38. package/src/core/authority-ledger.js +300 -0
  39. package/src/core/blastRadius.js +299 -0
  40. package/src/core/call-resolver.js +196 -0
  41. package/src/core/change-evaluator.js +509 -0
  42. package/src/core/change-evaluator.js.backup +424 -0
  43. package/src/core/change-evaluator.ts +285 -0
  44. package/src/core/chunked-uploader.js +180 -0
  45. package/src/core/circular-dependency-detector.js +404 -0
  46. package/src/core/cli-error-handler.js +458 -0
  47. package/src/core/cli-validator.js +458 -0
  48. package/src/core/compression.js +64 -0
  49. package/src/core/context_builder.js +741 -0
  50. package/src/core/dependency-manager.js +134 -0
  51. package/src/core/di-detector.js +202 -0
  52. package/src/core/diff-analyzer.js +76 -0
  53. package/src/core/example-invariants.js +135 -0
  54. package/src/core/failure-mode-synthesizer.js +341 -0
  55. package/src/core/invariant-analyzer.js +294 -0
  56. package/src/core/invariant-detector.js +548 -0
  57. package/src/core/invariant-enforcer.js +171 -0
  58. package/src/core/invariant-evaluation-utils.js +172 -0
  59. package/src/core/invariant-hooks.js +152 -0
  60. package/src/core/invariant-integration-example.js +186 -0
  61. package/src/core/invariant-registry.js +298 -0
  62. package/src/core/invariant-registry.ts +100 -0
  63. package/src/core/invariant-types.js +66 -0
  64. package/src/core/invariants-index.js +88 -0
  65. package/src/core/method-tracker.js +170 -0
  66. package/src/core/override-handler.js +304 -0
  67. package/src/core/ownership-resolver.js +227 -0
  68. package/src/core/parser-enhanced.js +80 -0
  69. package/src/core/parser.js +610 -0
  70. package/src/core/path-resolver.js +240 -0
  71. package/src/core/pattern-matcher.js +246 -0
  72. package/src/core/progress-tracker.js +71 -0
  73. package/src/core/react-nextjs-detector.js +245 -0
  74. package/src/core/readme-generator.js +167 -0
  75. package/src/core/retry-handler.js +57 -0
  76. package/src/core/scanner.js +289 -0
  77. package/src/core/semantic-analyzer.js +204 -0
  78. package/src/core/structural-context-owner.js +442 -0
  79. package/src/core/symbol-indexer.js +164 -0
  80. package/src/core/tsconfig-utils.js +73 -0
  81. package/src/core/type-analyzer.js +272 -0
  82. package/src/core/watcher.js +18 -0
  83. package/src/core/workspace-scanner.js +88 -0
  84. package/src/engine/context_builder.js +280 -0
  85. package/src/engine/context_sorter.js +59 -0
  86. package/src/engine/context_validator.js +200 -0
  87. package/src/engine/id-generator.js +16 -0
  88. package/src/engine/pass1_facts.js +260 -0
  89. package/src/engine/pass2_semantics.js +333 -0
  90. package/src/engine/pass3_lifter.js +99 -0
  91. package/src/engine/pass4_signals.js +201 -0
  92. package/src/index.js +830 -0
  93. package/src/plugins/express-plugin.js +48 -0
  94. package/src/plugins/plugin-manager.js +58 -0
  95. package/src/plugins/react-plugin.js +54 -0
  96. package/temp_original.js +0 -0
  97. package/test/determinism-test.js +83 -0
  98. package/test-authoritative-context.js +53 -0
  99. package/test-real-authoritative-context.js +118 -0
  100. package/test-upload-enhancements.js +111 -0
  101. package/test_repos/allowed-clean-architecture/.arcvision/invariants.json +57 -0
  102. package/test_repos/allowed-clean-architecture/adapters/controllers/UserController.js +95 -0
  103. package/test_repos/allowed-clean-architecture/adapters/http/HttpServer.js +78 -0
  104. package/test_repos/allowed-clean-architecture/application/dtos/CreateUserRequest.js +37 -0
  105. package/test_repos/allowed-clean-architecture/application/services/UserService.js +61 -0
  106. package/test_repos/allowed-clean-architecture/arcvision_context/README.md +93 -0
  107. package/test_repos/allowed-clean-architecture/arcvision_context/arcvision.context.json +2796 -0
  108. package/test_repos/allowed-clean-architecture/domain/interfaces/UserRepository.js +25 -0
  109. package/test_repos/allowed-clean-architecture/domain/models/User.js +39 -0
  110. package/test_repos/allowed-clean-architecture/index.js +45 -0
  111. package/test_repos/allowed-clean-architecture/infrastructure/database/DatabaseConnection.js +56 -0
  112. package/test_repos/allowed-clean-architecture/infrastructure/repositories/InMemoryUserRepository.js +61 -0
  113. package/test_repos/allowed-clean-architecture/package.json +15 -0
  114. package/test_repos/blocked-legacy-monolith/.arcvision/invariants.json +78 -0
  115. package/test_repos/blocked-legacy-monolith/arcvision_context/README.md +93 -0
  116. package/test_repos/blocked-legacy-monolith/arcvision_context/arcvision.context.json +2882 -0
  117. package/test_repos/blocked-legacy-monolith/database/dbConnection.js +35 -0
  118. package/test_repos/blocked-legacy-monolith/index.js +38 -0
  119. package/test_repos/blocked-legacy-monolith/modules/emailService.js +31 -0
  120. package/test_repos/blocked-legacy-monolith/modules/paymentProcessor.js +37 -0
  121. package/test_repos/blocked-legacy-monolith/package.json +15 -0
  122. package/test_repos/blocked-legacy-monolith/shared/utils.js +19 -0
  123. package/test_repos/blocked-legacy-monolith/utils/helpers.js +23 -0
  124. package/test_repos/risky-microservices-concerns/.arcvision/invariants.json +69 -0
  125. package/test_repos/risky-microservices-concerns/arcvision_context/README.md +93 -0
  126. package/test_repos/risky-microservices-concerns/arcvision_context/arcvision.context.json +3070 -0
  127. package/test_repos/risky-microservices-concerns/common/utils.js +77 -0
  128. package/test_repos/risky-microservices-concerns/gateways/apiGateway.js +84 -0
  129. package/test_repos/risky-microservices-concerns/index.js +20 -0
  130. package/test_repos/risky-microservices-concerns/libs/deprecatedHelper.js +36 -0
  131. package/test_repos/risky-microservices-concerns/package.json +15 -0
  132. package/test_repos/risky-microservices-concerns/services/orderService.js +42 -0
  133. package/test_repos/risky-microservices-concerns/services/userService.js +48 -0
  134. package/verify_engine.js +116 -0
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Sort the context object deterministically to ensure consistent output
3
+ * @param {Object} context - The context object to sort
4
+ * @returns {Object} - The sorted context object
5
+ */
6
+ function sortContext(context) {
7
+ // Create a copy of the context to avoid modifying the original
8
+ const sortedContext = { ...context };
9
+
10
+ // Sort nodes by ID
11
+ if (Array.isArray(sortedContext.nodes)) {
12
+ sortedContext.nodes = [...sortedContext.nodes].sort((a, b) => a.id.localeCompare(b.id));
13
+ }
14
+
15
+ // Sort edges by from, to, and relation
16
+ if (Array.isArray(sortedContext.edges)) {
17
+ sortedContext.edges = [...sortedContext.edges].sort((a, b) => {
18
+ if (a.from !== b.from) {
19
+ return a.from.localeCompare(b.from);
20
+ }
21
+ if (a.to !== b.to) {
22
+ return a.to.localeCompare(b.to);
23
+ }
24
+ return a.relation.localeCompare(b.relation);
25
+ });
26
+ }
27
+
28
+ // Sort metrics alphabetically
29
+ if (sortedContext.metrics && typeof sortedContext.metrics === 'object') {
30
+ const sortedMetrics = {};
31
+ const keys = Object.keys(sortedContext.metrics).sort();
32
+ for (const key of keys) {
33
+ sortedMetrics[key] = sortedContext.metrics[key];
34
+ }
35
+ sortedContext.metrics = sortedMetrics;
36
+ }
37
+
38
+ // Sort authority cores by authority score descending
39
+ if (Array.isArray(sortedContext.authority_cores)) {
40
+ sortedContext.authority_cores = [...sortedContext.authority_cores]
41
+ .sort((a, b) => (b.authorityScore || 0) - (a.authorityScore || 0));
42
+ }
43
+
44
+ // Sort hidden coupling by count descending
45
+ if (Array.isArray(sortedContext.hidden_coupling)) {
46
+ sortedContext.hidden_coupling = [...sortedContext.hidden_coupling]
47
+ .sort((a, b) => (b.count || 0) - (a.count || 0));
48
+ }
49
+
50
+ // Sort context surface by score descending
51
+ if (sortedContext.contextSurface && Array.isArray(sortedContext.contextSurface)) {
52
+ sortedContext.contextSurface = [...sortedContext.contextSurface]
53
+ .sort((a, b) => (b.score || 0) - (a.score || 0));
54
+ }
55
+
56
+ return sortedContext;
57
+ }
58
+
59
+ module.exports = { sortContext };
@@ -0,0 +1,200 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const Ajv = require('ajv');
4
+
5
+ // Create AJV instance with options for detailed error reporting
6
+ const ajv = new Ajv({ allErrors: true, strict: false });
7
+
8
+ /**
9
+ * Validate the context object against the Arcvision schema and invariants using AJV
10
+ * @param {Object} context - The context object to validate
11
+ * @param {string} schemaPath - Path to the schema file (optional, defaults to standard schema)
12
+ * @returns {Object} - Validation result with valid flag and errors
13
+ */
14
+ function validateContext(context, schemaPath = null) {
15
+ try {
16
+ // Use provided schema path or default to the standard schema
17
+ // When bundled, the schema file is located differently
18
+ let schemaFilePath;
19
+ if (schemaPath) {
20
+ schemaFilePath = schemaPath;
21
+ } else {
22
+ // Try multiple possible locations for the schema file
23
+ const possiblePaths = [
24
+ path.join(__dirname, '../../schema/arcvision_context_schema_v1.json'), // Standard path
25
+ path.join(__dirname, '../schema/arcvision_context_schema_v1.json'), // Bundled path
26
+ path.join(__dirname, 'schema/arcvision_context_schema_v1.json'), // Alternative bundled path
27
+ path.join(process.cwd(), 'schema/arcvision_context_schema_v1.json'), // Current working directory
28
+ ];
29
+
30
+ for (const possiblePath of possiblePaths) {
31
+ if (fs.existsSync(possiblePath)) {
32
+ schemaFilePath = possiblePath;
33
+ break;
34
+ }
35
+ }
36
+
37
+ if (!schemaFilePath) {
38
+ console.warn('⚠️ Schema file not found, using basic validation only');
39
+ // Return early with basic validation if schema not found
40
+ return { valid: true, warnings: ['Schema validation skipped - schema file not found'] };
41
+ }
42
+ }
43
+
44
+ if (!fs.existsSync(schemaFilePath)) {
45
+ console.warn(`⚠️ Schema file not found: ${schemaFilePath}, using basic validation only`);
46
+ // Return early with basic validation if schema not found
47
+ return { valid: true, warnings: ['Schema validation skipped - schema file not found'] };
48
+ }
49
+
50
+ let schema;
51
+ try {
52
+ schema = JSON.parse(fs.readFileSync(schemaFilePath, 'utf8'));
53
+ } catch (error) {
54
+ console.warn(`⚠️ Failed to parse schema file: ${error.message}, using basic validation only`);
55
+ return { valid: true, warnings: ['Schema validation skipped - failed to parse schema file'] };
56
+ }
57
+
58
+ // Compile the schema with AJV
59
+ let validate;
60
+ try {
61
+ validate = ajv.compile(schema);
62
+ } catch (error) {
63
+ console.warn(`⚠️ Failed to compile schema: ${error.message}, using basic validation only`);
64
+ return { valid: true, warnings: ['Schema validation skipped - failed to compile schema'] };
65
+ }
66
+
67
+ // Validate the context against schema first
68
+ const schemaValid = validate(context);
69
+
70
+ if (!schemaValid) {
71
+ // Format errors for human readability
72
+ const errors = (validate.errors || []).map(err => {
73
+ const path = err.instancePath || "root";
74
+ const message = err.message || "schema violation";
75
+ return `${path}: ${message}`;
76
+ });
77
+
78
+ return { valid: false, errors, warnings: [] };
79
+ }
80
+
81
+ // Now validate against system invariants
82
+ const invariantValidation = validateAgainstInvariants(context);
83
+
84
+ if (!invariantValidation.valid) {
85
+ return {
86
+ valid: false,
87
+ errors: invariantValidation.errors,
88
+ warnings: invariantValidation.warnings || []
89
+ };
90
+ }
91
+
92
+ return { valid: true, warnings: [] };
93
+ } catch (error) {
94
+ return {
95
+ valid: false,
96
+ errors: [`Validation error: ${error.message}`],
97
+ warnings: [],
98
+ details: null
99
+ };
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Validate the context against its own invariants
105
+ * @param {Object} context - The context object to validate
106
+ * @returns {Object} - Validation result with valid flag and errors
107
+ */
108
+ function validateAgainstInvariants(context) {
109
+ const errors = [];
110
+
111
+ // Check if invariants exist in the context
112
+ if (!context.invariants || !Array.isArray(context.invariants)) {
113
+ // If no invariants exist, we consider this valid (they may not have been generated yet)
114
+ return { valid: true };
115
+ }
116
+
117
+ // Validate each invariant
118
+ for (const invariant of context.invariants) {
119
+ // Check if required fields exist
120
+ if (!invariant.id) {
121
+ errors.push(`Invariant missing required 'id' field: ${JSON.stringify(invariant)}`);
122
+ }
123
+
124
+ if (!invariant.statement) {
125
+ errors.push(`Invariant with id '${invariant.id}' missing required 'statement' field`);
126
+ }
127
+
128
+ if (!invariant.scope) {
129
+ errors.push(`Invariant with id '${invariant.id}' missing required 'scope' field`);
130
+ }
131
+
132
+ if (typeof invariant.critical_path !== 'boolean' && invariant.critical_path !== undefined) {
133
+ errors.push(`Invariant with id '${invariant.id}' has invalid 'critical_path' field (must be boolean)`);
134
+ }
135
+
136
+ if (!invariant.status) {
137
+ errors.push(`Invariant with id '${invariant.id}' missing required 'status' field`);
138
+ }
139
+
140
+ // Validate status enum
141
+ const validStatuses = ['active', 'disabled', 'pending', 'suspected'];
142
+ if (invariant.status && !validStatuses.includes(invariant.status)) {
143
+ errors.push(`Invariant with id '${invariant.id}' has invalid 'status' field: ${invariant.status}`);
144
+ }
145
+ }
146
+
147
+ // Validate ownership section if present
148
+ if (context.ownership) {
149
+ for (const [concept, ownershipInfo] of Object.entries(context.ownership)) {
150
+ if (!ownershipInfo.source_of_truth) {
151
+ errors.push(`Ownership for concept '${concept}' missing required 'source_of_truth' field`);
152
+ }
153
+
154
+ if (ownershipInfo.derived_in && !Array.isArray(ownershipInfo.derived_in)) {
155
+ errors.push(`Ownership for concept '${concept}' has invalid 'derived_in' field (must be array)`);
156
+ }
157
+
158
+ if (ownershipInfo.consumers && !Array.isArray(ownershipInfo.consumers)) {
159
+ errors.push(`Ownership for concept '${concept}' has invalid 'consumers' field (must be array)`);
160
+ }
161
+
162
+ if (ownershipInfo.invalidators && !Array.isArray(ownershipInfo.invalidators)) {
163
+ errors.push(`Ownership for concept '${concept}' has invalid 'invalidators' field (must be array)`);
164
+ }
165
+ }
166
+ }
167
+
168
+ // Validate failure modes section if present
169
+ if (context.failure_modes && Array.isArray(context.failure_modes)) {
170
+ for (const failureMode of context.failure_modes) {
171
+ if (!failureMode.id) {
172
+ errors.push(`Failure mode missing required 'id' field: ${JSON.stringify(failureMode)}`);
173
+ }
174
+
175
+ if (!failureMode.trigger) {
176
+ errors.push(`Failure mode with id '${failureMode.id}' missing required 'trigger' field`);
177
+ }
178
+
179
+ if (!failureMode.broken_invariant) {
180
+ errors.push(`Failure mode with id '${failureMode.id}' missing required 'broken_invariant' field`);
181
+ }
182
+
183
+ if (!failureMode.observed_symptom) {
184
+ errors.push(`Failure mode with id '${failureMode.id}' missing required 'observed_symptom' field`);
185
+ }
186
+
187
+ if (!failureMode.root_cause) {
188
+ errors.push(`Failure mode with id '${failureMode.id}' missing required 'root_cause' field`);
189
+ }
190
+ }
191
+ }
192
+
193
+ return {
194
+ valid: errors.length === 0,
195
+ errors,
196
+ warnings: []
197
+ };
198
+ }
199
+
200
+ module.exports = { validateContext };
@@ -0,0 +1,16 @@
1
+ const crypto = require('crypto');
2
+
3
+ /**
4
+ * Generate a stable, deterministic ID based on the file path
5
+ * Same path will always produce the same ID
6
+ * @param {string} relativePath - The relative path to generate ID for
7
+ * @returns {string} - 16-character deterministic ID
8
+ */
9
+ function stableId(relativePath) {
10
+ // Normalize the path by replacing backslashes with forward slashes and converting to lowercase
11
+ const normalized = relativePath.replace(/\\/g, '/').toLowerCase();
12
+ // Generate SHA-256 hash and take first 16 characters
13
+ return crypto.createHash('sha256').update(normalized).digest('hex').substring(0, 16);
14
+ }
15
+
16
+ module.exports = { stableId };
@@ -0,0 +1,260 @@
1
+ /**
2
+ * Pass 1: Syntactic Fact Emission
3
+ *
4
+ * Objectives:
5
+ * - content-agnostic parsing (delegates to parser-enhanced)
6
+ * - emits raw indisputable facts: "File A imports string 'B'", "File A calls method 'foo'"
7
+ * - NO resolution of what 'B' is (that's Pass 2)
8
+ */
9
+
10
+ const { glob } = require('glob');
11
+ const path = require('path');
12
+ const fs = require('fs');
13
+ const parser = require('../core/parser-enhanced');
14
+ const pluginManager = require('../plugins/plugin-manager');
15
+
16
+ /**
17
+ * Executes Pass 1: Scans directory and emits raw facts
18
+ * @param {string} directory - Root directory to scan
19
+ * @param {Object} options - Scan options
20
+ * @returns {Promise<Array>} List of file nodes with raw metadata (facts)
21
+ */
22
+ async function executePass1(directory, options = {}) {
23
+ console.log('🧱 PASS 1: Syntactic Fact Extraction...');
24
+
25
+ // Normalize helper
26
+ const normalize = p => p.replace(/\\/g, '/');
27
+
28
+ const scanOptions = {
29
+ ignore: ['**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**', ...options.ignore || []],
30
+ cwd: directory,
31
+ absolute: true
32
+ };
33
+
34
+ // Load plugins
35
+ const pluginDir = path.join(__dirname, '../plugins');
36
+ if (fs.existsSync(pluginDir)) {
37
+ pluginManager.loadPluginsFromDirectory(pluginDir);
38
+ }
39
+
40
+ // Find all relevant code files for structural analysis
41
+ const files = await glob('**/*.{js,jsx,ts,tsx,cjs,mjs,json,lua}', {
42
+ ...scanOptions,
43
+ ignore: [...scanOptions.ignore,
44
+ '**/*.d.ts',
45
+ '**/.next/**',
46
+ '**/coverage/**',
47
+ '**/arcvision.context.json',
48
+ '**/package-lock.json',
49
+ '**/yarn.lock',
50
+ // Additional non-code file types to exclude
51
+ '**/*.png', '**/*.jpg', '**/*.jpeg', '**/*.gif', '**/*.svg',
52
+ '**/*.pdf', '**/*.doc', '**/*.docx', '**/*.xls', '**/*.xlsx',
53
+ '**/*.zip', '**/*.tar', '**/*.gz', '**/*.exe', '**/*.dll',
54
+ '**/*.log', '**/*.tmp', '**/*.DS_Store', '**/Thumbs.db',
55
+ '**/*.lock', '**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**',
56
+ '**/target/**', '**/out/**', '**/vendor/**', '**/*.min.js', '**/*.bundle.js',
57
+ '**/public/**', '**/static/**', '**/assets/**', '**/images/**', '**/fonts/**',
58
+ '**/tests/**', '**/test/**', '**/__tests__/**', '**/__mocks__/**',
59
+ '**/*.test.js', '**/*.test.ts', '**/*.spec.js', '**/*.spec.ts',
60
+ '**/.*ignore', '**/.*config*', '**/.*rc', '**/.*lock', '**/.*cache',
61
+ '**/.*history', '**/.*backup', '**/.*temp', '**/.*tmp',
62
+ '**/README.*', '**/CHANGELOG.*', '**/TODO', '**/NOTES',
63
+ '**/Dockerfile', '**/docker-compose.yml', '**/*.dockerfile',
64
+ '**/Makefile', '**/*.mk', '**/*.pyc', '**/__pycache__/**',
65
+ '**/*.so', '**/*.dylib', '**/*.o', '**/*.obj', '**/*.jar', '**/*.war',
66
+ '**/*.lock', '**/*.pid', '**/*.seed', '**/*.pid', '**/*.sock'
67
+ ]
68
+ });
69
+
70
+ // Use only code files for analysis
71
+ const allFiles = files;
72
+
73
+ console.log(` Identified ${allFiles.length} files for analysis`);
74
+
75
+ // Optimize concurrency based on file count to balance performance and memory usage
76
+ const BASE_CONCURRENCY = 25; // Reduced base concurrency for better resource management
77
+ const OPTIMAL_CONCURRENCY = Math.min(BASE_CONCURRENCY, Math.max(5, Math.floor(allFiles.length / 10))); // Adaptive concurrency
78
+ const CONCURRENCY = OPTIMAL_CONCURRENCY;
79
+ const rawNodes = [];
80
+ let totalFacts = 0;
81
+
82
+ // Process files in parallel batches
83
+ for (let i = 0; i < allFiles.length; i += CONCURRENCY) {
84
+ const batch = allFiles.slice(i, i + CONCURRENCY);
85
+
86
+ // Process the batch in parallel
87
+ const promises = batch.map(async (file) => {
88
+ try {
89
+ const relativePath = path.relative(directory, file);
90
+ const normalizedRelativePath = normalize(relativePath);
91
+
92
+ let metadata = {};
93
+
94
+ if (file.endsWith('.json')) {
95
+ const content = fs.readFileSync(file, 'utf-8');
96
+ metadata = {
97
+ id: file,
98
+ isJson: true,
99
+ raw: content
100
+ };
101
+ } else if (file.endsWith('.lua')) {
102
+ const content = fs.readFileSync(file, 'utf-8');
103
+ metadata = {
104
+ id: file,
105
+ isLua: true,
106
+ raw: content,
107
+ // Extract potential invocations from Lua content
108
+ potentialInvocations: extractLuaInvocations(content)
109
+ };
110
+ } else {
111
+ metadata = parser.parseFile(file);
112
+ }
113
+
114
+ metadata = await pluginManager.processFile(file, metadata);
115
+
116
+ // Determine node type based on file extension
117
+ const isLuaFile = file.endsWith('.lua');
118
+ const nodeType = isLuaFile ? 'execution_script' : 'file';
119
+ const nodeRole = isLuaFile ? 'atomic_redis_operation' : 'Structure';
120
+
121
+ const node = {
122
+ id: normalizedRelativePath,
123
+ filePath: file,
124
+ type: nodeType,
125
+ role: nodeRole,
126
+ facts: {
127
+ imports: metadata.imports || [],
128
+ exports: metadata.exports || [],
129
+ functions: metadata.functions || [],
130
+ classes: metadata.classes || [],
131
+ types: metadata.types || [],
132
+ calls: {
133
+ functions: metadata.functionCalls || [],
134
+ methods: metadata.methodCalls || [],
135
+ constructors: metadata.constructorCalls || []
136
+ },
137
+ typeAnalysis: {
138
+ typeImports: metadata.typeImports || [],
139
+ interfaceDeps: metadata.interfaceDependencies || [],
140
+ genericDeps: metadata.genericDependencies || []
141
+ },
142
+ di: {
143
+ injections: metadata.constructorInjections || [],
144
+ hooks: metadata.hookDependencies || [],
145
+ context: metadata.contextUsages || []
146
+ },
147
+ react: metadata.componentUsage || [],
148
+ potentialInvocations: metadata.potentialInvocations || [],
149
+ invariantIndicators: extractInvariantIndicators(metadata.raw || '')
150
+ }
151
+ };
152
+
153
+ // Add language and execution boundary for Lua files
154
+ if (isLuaFile) {
155
+ node.language = 'lua';
156
+ node.execution_boundary = 'redis';
157
+ }
158
+
159
+ return { node, factCount: (metadata.imports?.length || 0) + (metadata.functionCalls?.length || 0) + (metadata.invariantIndicators?.length || 0) };
160
+ } catch (e) {
161
+ // Only warn if we're not in a high-performance mode
162
+ if (allFiles.length < 1000) { // Only show warnings for smaller projects
163
+ console.warn(`⚠️ Pass 1 failed for ${file}: ${e.message}`);
164
+ }
165
+ return null;
166
+ }
167
+ });
168
+
169
+ const results = await Promise.all(promises);
170
+
171
+ results.filter(Boolean).forEach(res => {
172
+ rawNodes.push(res.node);
173
+ totalFacts += res.factCount;
174
+ });
175
+
176
+ if (i % (CONCURRENCY * 4) === 0 && i > 0) {
177
+ console.log(` ... processed ${Math.min(i + CONCURRENCY, allFiles.length)} / ${allFiles.length} files`);
178
+ }
179
+ }
180
+
181
+ console.log(` ✓ Scanned ${rawNodes.length} files`);
182
+ console.log(` ✓ Extracted ${totalFacts} raw syntactic facts (including invariant indicators)`);
183
+
184
+ return rawNodes;
185
+ }
186
+
187
+ /**
188
+ * Extract potential Lua script invocations from content
189
+ * @param {string} content - Lua script content
190
+ * @returns {Array} Array of potential invocations
191
+ */
192
+ function extractLuaInvocations(content) {
193
+ const invocations = [];
194
+
195
+ // Look for common BullMQ Lua script patterns
196
+ const patterns = [
197
+ /readFileSync\(['"].*\.lua['"].*\)/gi,
198
+ /defineCommand\(\s*\{\s*lua\s*:.*\}/gi,
199
+ /redis\.call\(['"].*['"].*\)/gi,
200
+ /redis\.pcall\(['"].*['"].*\)/gi
201
+ ];
202
+
203
+ patterns.forEach(pattern => {
204
+ let match;
205
+ while ((match = pattern.exec(content)) !== null) {
206
+ invocations.push(match[0]);
207
+ }
208
+ });
209
+
210
+ return invocations;
211
+ }
212
+
213
+ /**
214
+ * Extract potential invariant indicators from content
215
+ * @param {string} content - File content
216
+ * @returns {Array} Array of potential invariant indicators
217
+ */
218
+ function extractInvariantIndicators(content) {
219
+ const indicators = [];
220
+
221
+ // Look for common invariant/pattern validation patterns
222
+ const patterns = [
223
+ /assert\(/gi, // assert() calls
224
+ /validate[A-Z]/gi, // validate functions
225
+ /check[A-Z]/gi, // check functions
226
+ /ensure[A-Z]/gi, // ensure functions
227
+ /guard[A-Z]/gi, // guard functions
228
+ /verify[A-Z]/gi, // verify functions
229
+ /Invariant/gi, // Invariant mentions
230
+ /constraint/gi, // constraint mentions
231
+ /must.*be/gi, // must-be patterns
232
+ /should.*be/gi, // should-be patterns
233
+ /expected.*to/gi, // expected-to patterns
234
+ /boundary.*check/gi, // boundary checks
235
+ /range.*check/gi, // range checks
236
+ /within.*bounds/gi, // bounds checks
237
+ /access.*control/gi, // access controls
238
+ /permission.*check/gi, // permission checks
239
+ /if.*!condition.*throw/gi, // guard clauses
240
+ /state.*transition/gi, // state transitions
241
+ /setState/gi, // state setters
242
+ /update.*state/gi, // state updaters
243
+ /transition/gi // transition patterns
244
+ ];
245
+
246
+ patterns.forEach(pattern => {
247
+ let match;
248
+ while ((match = pattern.exec(content)) !== null) {
249
+ indicators.push({
250
+ pattern: pattern.source,
251
+ match: match[0],
252
+ position: match.index
253
+ });
254
+ }
255
+ });
256
+
257
+ return indicators;
258
+ }
259
+
260
+ module.exports = { executePass1 };