@rigour-labs/core 5.0.1 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +9 -1
  2. package/dist/gates/agent-team.d.ts +0 -1
  3. package/dist/gates/agent-team.js +0 -1
  4. package/dist/gates/checkpoint.d.ts +0 -2
  5. package/dist/gates/checkpoint.js +0 -2
  6. package/dist/gates/context-window-artifacts.d.ts +6 -2
  7. package/dist/gates/context-window-artifacts.js +107 -31
  8. package/dist/gates/deep-analysis.d.ts +2 -0
  9. package/dist/gates/deep-analysis.js +41 -11
  10. package/dist/gates/dependency.d.ts +0 -2
  11. package/dist/gates/dependency.js +23 -5
  12. package/dist/gates/deprecated-apis.d.ts +0 -2
  13. package/dist/gates/deprecated-apis.js +33 -20
  14. package/dist/gates/duplication-drift/index.d.ts +61 -0
  15. package/dist/gates/duplication-drift/index.js +240 -0
  16. package/dist/gates/duplication-drift/similarity.d.ts +68 -0
  17. package/dist/gates/duplication-drift/similarity.js +177 -0
  18. package/dist/gates/duplication-drift/tokenizer.d.ts +55 -0
  19. package/dist/gates/duplication-drift/tokenizer.js +195 -0
  20. package/dist/gates/frontend-secret-exposure.d.ts +0 -3
  21. package/dist/gates/frontend-secret-exposure.js +1 -114
  22. package/dist/gates/frontend-secret-patterns.d.ts +33 -0
  23. package/dist/gates/frontend-secret-patterns.js +119 -0
  24. package/dist/gates/{hallucinated-imports.d.ts → hallucinated-imports/index.d.ts} +2 -29
  25. package/dist/gates/hallucinated-imports/index.js +174 -0
  26. package/dist/gates/hallucinated-imports/js-resolver.d.ts +45 -0
  27. package/dist/gates/hallucinated-imports/js-resolver.js +320 -0
  28. package/dist/gates/hallucinated-imports/manifest-discovery.d.ts +28 -0
  29. package/dist/gates/hallucinated-imports/manifest-discovery.js +114 -0
  30. package/dist/gates/hallucinated-imports/python-resolver.d.ts +24 -0
  31. package/dist/gates/hallucinated-imports/python-resolver.js +306 -0
  32. package/dist/gates/hallucinated-imports-lang.d.ts +2 -2
  33. package/dist/gates/hallucinated-imports-lang.js +269 -34
  34. package/dist/gates/hallucinated-imports.test.js +1 -2
  35. package/dist/gates/inconsistent-error-handling.d.ts +0 -5
  36. package/dist/gates/inconsistent-error-handling.js +15 -144
  37. package/dist/gates/language-adapters/csharp-adapter.d.ts +16 -0
  38. package/dist/gates/language-adapters/csharp-adapter.js +211 -0
  39. package/dist/gates/language-adapters/go-adapter.d.ts +26 -0
  40. package/dist/gates/language-adapters/go-adapter.js +195 -0
  41. package/dist/gates/language-adapters/index.d.ts +15 -0
  42. package/dist/gates/language-adapters/index.js +16 -0
  43. package/dist/gates/language-adapters/java-adapter.d.ts +16 -0
  44. package/dist/gates/language-adapters/java-adapter.js +237 -0
  45. package/dist/gates/language-adapters/js-adapter.d.ts +26 -0
  46. package/dist/gates/language-adapters/js-adapter.js +279 -0
  47. package/dist/gates/language-adapters/python-adapter.d.ts +25 -0
  48. package/dist/gates/language-adapters/python-adapter.js +183 -0
  49. package/dist/gates/language-adapters/registry.d.ts +26 -0
  50. package/dist/gates/language-adapters/registry.js +65 -0
  51. package/dist/gates/language-adapters/ruby-adapter.d.ts +25 -0
  52. package/dist/gates/language-adapters/ruby-adapter.js +217 -0
  53. package/dist/gates/language-adapters/rust-adapter.d.ts +27 -0
  54. package/dist/gates/language-adapters/rust-adapter.js +235 -0
  55. package/dist/gates/language-adapters/types.d.ts +60 -0
  56. package/dist/gates/language-adapters/types.js +22 -0
  57. package/dist/gates/logic-drift-extractors.d.ts +15 -0
  58. package/dist/gates/logic-drift-extractors.js +34 -0
  59. package/dist/gates/logic-drift.d.ts +0 -30
  60. package/dist/gates/logic-drift.js +39 -129
  61. package/dist/gates/phantom-apis.d.ts +0 -2
  62. package/dist/gates/phantom-apis.js +49 -20
  63. package/dist/gates/promise-safety.d.ts +0 -1
  64. package/dist/gates/promise-safety.js +14 -2
  65. package/dist/gates/runner.js +51 -22
  66. package/dist/gates/security-patterns-data.d.ts +14 -0
  67. package/dist/gates/security-patterns-data.js +235 -0
  68. package/dist/gates/security-patterns.d.ts +17 -3
  69. package/dist/gates/security-patterns.js +80 -211
  70. package/dist/gates/side-effect-analysis/categorizer.d.ts +32 -0
  71. package/dist/gates/side-effect-analysis/categorizer.js +83 -0
  72. package/dist/gates/{side-effect-analysis.d.ts → side-effect-analysis/index.d.ts} +3 -5
  73. package/dist/gates/{side-effect-analysis.js → side-effect-analysis/index.js} +33 -45
  74. package/dist/gates/side-effect-analysis/scope-tracker.d.ts +37 -0
  75. package/dist/gates/side-effect-analysis/scope-tracker.js +40 -0
  76. package/dist/gates/side-effect-helpers/index.d.ts +4 -0
  77. package/dist/gates/side-effect-helpers/index.js +4 -0
  78. package/dist/gates/side-effect-helpers/pattern-detection.d.ts +123 -0
  79. package/dist/gates/{side-effect-helpers.js → side-effect-helpers/pattern-detection.js} +22 -468
  80. package/dist/gates/side-effect-helpers/resource-tracking.d.ts +80 -0
  81. package/dist/gates/side-effect-helpers/resource-tracking.js +281 -0
  82. package/dist/gates/side-effect-helpers/scope-analysis.d.ts +21 -0
  83. package/dist/gates/side-effect-helpers/scope-analysis.js +146 -0
  84. package/dist/gates/side-effect-helpers/types.d.ts +38 -0
  85. package/dist/gates/side-effect-helpers/types.js +41 -0
  86. package/dist/gates/side-effect-rules.d.ts +0 -1
  87. package/dist/gates/side-effect-rules.js +0 -1
  88. package/dist/gates/style-drift-rules.d.ts +86 -0
  89. package/dist/gates/style-drift-rules.js +103 -0
  90. package/dist/gates/style-drift.d.ts +7 -16
  91. package/dist/gates/style-drift.js +101 -119
  92. package/dist/gates/test-quality-matchers.d.ts +53 -0
  93. package/dist/gates/test-quality-matchers.js +86 -0
  94. package/dist/gates/test-quality.d.ts +0 -3
  95. package/dist/gates/test-quality.js +47 -44
  96. package/dist/hooks/checker.d.ts +0 -1
  97. package/dist/hooks/checker.js +0 -2
  98. package/dist/hooks/dlp-templates.d.ts +0 -1
  99. package/dist/hooks/dlp-templates.js +0 -4
  100. package/dist/hooks/index.d.ts +0 -2
  101. package/dist/hooks/index.js +0 -2
  102. package/dist/hooks/input-validator.d.ts +0 -1
  103. package/dist/hooks/input-validator.js +0 -1
  104. package/dist/hooks/input-validator.test.js +0 -1
  105. package/dist/hooks/standalone-checker.d.ts +0 -1
  106. package/dist/hooks/standalone-checker.js +0 -1
  107. package/dist/hooks/standalone-dlp-checker.d.ts +0 -1
  108. package/dist/hooks/standalone-dlp-checker.js +0 -1
  109. package/dist/hooks/templates.d.ts +0 -1
  110. package/dist/hooks/templates.js +0 -1
  111. package/dist/hooks/types.d.ts +0 -1
  112. package/dist/hooks/types.js +0 -1
  113. package/dist/index.d.ts +1 -1
  114. package/dist/index.js +1 -1
  115. package/dist/services/adaptive-thresholds.d.ts +0 -2
  116. package/dist/services/adaptive-thresholds.js +0 -2
  117. package/dist/services/filesystem-cache.d.ts +0 -1
  118. package/dist/services/filesystem-cache.js +0 -1
  119. package/dist/services/score-history.d.ts +0 -1
  120. package/dist/services/score-history.js +0 -1
  121. package/dist/services/temporal-drift.d.ts +1 -2
  122. package/dist/services/temporal-drift.js +7 -8
  123. package/dist/storage/db.d.ts +23 -7
  124. package/dist/storage/db.js +116 -55
  125. package/dist/storage/findings.d.ts +4 -3
  126. package/dist/storage/findings.js +13 -20
  127. package/dist/storage/local-memory.d.ts +4 -4
  128. package/dist/storage/local-memory.js +20 -22
  129. package/dist/storage/patterns.d.ts +5 -5
  130. package/dist/storage/patterns.js +20 -26
  131. package/dist/storage/scans.d.ts +6 -6
  132. package/dist/storage/scans.js +12 -21
  133. package/dist/types/index.d.ts +1 -0
  134. package/dist/utils/scanner.js +1 -1
  135. package/package.json +7 -8
  136. package/dist/gates/duplication-drift.d.ts +0 -128
  137. package/dist/gates/duplication-drift.js +0 -585
  138. package/dist/gates/hallucinated-imports.js +0 -641
  139. package/dist/gates/side-effect-helpers.d.ts +0 -260
@@ -0,0 +1,237 @@
1
+ import { classifyCasing } from './types.js';
2
+ class JavaAdapter {
3
+ id = 'java';
4
+ name = 'Java/Kotlin';
5
+ extensions = ['.java', '.kt'];
6
+ extractFunctions(source) {
7
+ const lines = source.split('\n');
8
+ const functions = [];
9
+ // Java: public/private/protected static? ReturnType methodName(
10
+ const javaMethodRegex = /^\s*(public|private|protected)?\s*(static)?\s*(\w+)\s+(\w+)\s*\(/;
11
+ // Kotlin: fun methodName( or suspend fun methodName(
12
+ const kotlinFunRegex = /^\s*(suspend\s+)?fun\s+(\w+)\s*\(/;
13
+ let i = 0;
14
+ while (i < lines.length) {
15
+ let match = lines[i].match(javaMethodRegex);
16
+ let isKotlin = false;
17
+ let isAsync = false;
18
+ let methodName = '';
19
+ if (!match) {
20
+ match = lines[i].match(kotlinFunRegex);
21
+ if (match) {
22
+ isKotlin = true;
23
+ isAsync = !!match[1];
24
+ methodName = match[2];
25
+ }
26
+ }
27
+ else {
28
+ isAsync = false;
29
+ methodName = match[4];
30
+ }
31
+ if (match && methodName) {
32
+ const startLine = i + 1;
33
+ // Find closing brace
34
+ let braceCount = 0;
35
+ let foundOpening = false;
36
+ let j = i;
37
+ let body = '';
38
+ while (j < lines.length) {
39
+ const line = lines[j];
40
+ for (let k = 0; k < line.length; k++) {
41
+ if (line[k] === '{') {
42
+ braceCount++;
43
+ foundOpening = true;
44
+ }
45
+ else if (line[k] === '}') {
46
+ braceCount--;
47
+ }
48
+ }
49
+ body += line + '\n';
50
+ if (foundOpening && braceCount === 0)
51
+ break;
52
+ j++;
53
+ }
54
+ functions.push({
55
+ name: methodName,
56
+ startLine,
57
+ endLine: j + 1,
58
+ body: body.trim(),
59
+ isAsync,
60
+ isExported: lines[i].includes('public'),
61
+ });
62
+ i = j + 1;
63
+ }
64
+ else {
65
+ i++;
66
+ }
67
+ }
68
+ return functions;
69
+ }
70
+ extractImports(source) {
71
+ const imports = [];
72
+ const lines = source.split('\n');
73
+ // Java/Kotlin: import package.Class; or import package.*
74
+ const importRegex = /^\s*import\s+([^;]+);/;
75
+ lines.forEach((line, idx) => {
76
+ const match = line.match(importRegex);
77
+ if (match) {
78
+ const fullModule = match[1].trim();
79
+ // Check if wildcard import
80
+ const isWildcard = fullModule.endsWith('.*');
81
+ const module = isWildcard
82
+ ? fullModule.slice(0, -2)
83
+ : fullModule.split('.').slice(0, -1).join('.');
84
+ const className = isWildcard
85
+ ? '*'
86
+ : fullModule.split('.').pop() || '';
87
+ imports.push({
88
+ module,
89
+ names: className ? [className] : [],
90
+ line: idx + 1,
91
+ isDynamic: false,
92
+ });
93
+ }
94
+ });
95
+ return imports;
96
+ }
97
+ extractErrorHandlers(source) {
98
+ const handlers = [];
99
+ const lines = source.split('\n');
100
+ // Match: catch (ExceptionType e) {
101
+ const catchRegex = /catch\s*\(\s*(\w+)\s+\w+\s*\)\s*{/;
102
+ let i = 0;
103
+ while (i < lines.length) {
104
+ const match = lines[i].match(catchRegex);
105
+ if (match) {
106
+ const exceptionType = match[1];
107
+ const startLine = i + 1;
108
+ // Extract catch block
109
+ let braceCount = 0;
110
+ let foundOpening = false;
111
+ let j = i;
112
+ let body = '';
113
+ while (j < lines.length) {
114
+ const line = lines[j];
115
+ for (let k = 0; k < line.length; k++) {
116
+ if (line[k] === '{') {
117
+ braceCount++;
118
+ foundOpening = true;
119
+ }
120
+ else if (line[k] === '}') {
121
+ braceCount--;
122
+ }
123
+ }
124
+ body += line + '\n';
125
+ if (foundOpening && braceCount === 0)
126
+ break;
127
+ j++;
128
+ }
129
+ // Classify strategy
130
+ let strategy = 'swallow';
131
+ if (/\bthrow\s*;/.test(body)) {
132
+ strategy = 'rethrow';
133
+ }
134
+ else if (/\bthrow\s+new\s+/.test(body)) {
135
+ strategy = 'wrap';
136
+ }
137
+ else if (/System\.out\.(print|println)|Log\.(d|e|i|w)/.test(body)) {
138
+ strategy = 'log';
139
+ }
140
+ handlers.push({
141
+ type: 'try-catch',
142
+ strategy,
143
+ startLine,
144
+ body: body.trim(),
145
+ });
146
+ i = j + 1;
147
+ }
148
+ else {
149
+ i++;
150
+ }
151
+ }
152
+ return handlers;
153
+ }
154
+ extractNamingPatterns(source) {
155
+ const patterns = [];
156
+ // Extract camelCase methods
157
+ const methodRegex = /\b(public|private|protected)?\s*(static)?\s*\w+\s+([a-z]\w*)\s*\(/g;
158
+ let match;
159
+ while ((match = methodRegex.exec(source)) !== null) {
160
+ patterns.push({
161
+ name: match[3],
162
+ kind: 'method',
163
+ convention: classifyCasing(match[3]),
164
+ });
165
+ }
166
+ // Extract PascalCase class declarations
167
+ const classRegex = /\b(class|interface)\s+([A-Z]\w+)/g;
168
+ while ((match = classRegex.exec(source)) !== null) {
169
+ patterns.push({
170
+ name: match[2],
171
+ kind: 'class',
172
+ convention: classifyCasing(match[2]),
173
+ });
174
+ }
175
+ // Extract camelCase variables
176
+ const varRegex = /\b(private|protected)?\s*\w+\s+([a-z]\w+)\s*[=;]/g;
177
+ while ((match = varRegex.exec(source)) !== null) {
178
+ patterns.push({
179
+ name: match[2],
180
+ kind: 'variable',
181
+ convention: classifyCasing(match[2]),
182
+ });
183
+ }
184
+ // Extract SCREAMING_SNAKE constants
185
+ const constRegex = /\bfinal\s+\w+\s+([A-Z][A-Z0-9_]*)\s*=/g;
186
+ while ((match = constRegex.exec(source)) !== null) {
187
+ patterns.push({
188
+ name: match[1],
189
+ kind: 'constant',
190
+ convention: classifyCasing(match[1]),
191
+ });
192
+ }
193
+ return patterns;
194
+ }
195
+ stripComments(source) {
196
+ // Remove // comments
197
+ let result = source.replace(/\/\/.*$/gm, '');
198
+ // Remove /* */ comments (handles multiline)
199
+ result = result.replace(/\/\*[\s\S]*?\*\//g, '');
200
+ // Kotlin: remove /** */ doc comments
201
+ result = result.replace(/\/\*\*[\s\S]*?\*\//g, '');
202
+ return result;
203
+ }
204
+ extractComparisonOps(source) {
205
+ const ops = new Set();
206
+ const opsRegex = /(>=|<=|==|!=|>|<)/g;
207
+ let match;
208
+ while ((match = opsRegex.exec(source)) !== null) {
209
+ ops.add(match[1]);
210
+ }
211
+ return Array.from(ops);
212
+ }
213
+ countBranches(source) {
214
+ const stripped = this.stripComments(source);
215
+ const ifMatches = stripped.match(/\bif\s*\(/g) || [];
216
+ const elseIfMatches = stripped.match(/\belse\s+if\s*\(/g) || [];
217
+ const elseMatches = stripped.match(/\belse\s*{/g) || [];
218
+ const switchMatches = stripped.match(/\bswitch\s*\(/g) || [];
219
+ const caseMatches = stripped.match(/\bcase\s+/g) || [];
220
+ // Kotlin-specific
221
+ const whenMatches = stripped.match(/\bwhen\s*\(/g) || [];
222
+ const isMatches = stripped.match(/\bis\s+/g) || [];
223
+ return (ifMatches.length +
224
+ elseIfMatches.length +
225
+ elseMatches.length +
226
+ switchMatches.length +
227
+ caseMatches.length +
228
+ whenMatches.length +
229
+ isMatches.length);
230
+ }
231
+ countReturns(source) {
232
+ const stripped = this.stripComments(source);
233
+ const returnMatches = stripped.match(/\breturn\b/g) || [];
234
+ return returnMatches.length;
235
+ }
236
+ }
237
+ export const javaAdapter = new JavaAdapter();
@@ -0,0 +1,26 @@
1
+ /**
2
+ * JavaScript/TypeScript Language Adapter
3
+ *
4
+ * Handles: .ts, .tsx, .js, .jsx, .mjs, .cjs
5
+ *
6
+ * Regex-based static analysis for JS/TS code patterns.
7
+ */
8
+ import type { LanguageAdapter, FunctionFact, ImportFact, ErrorHandlerFact, NamingPattern } from './types.js';
9
+ declare class JsAdapter implements LanguageAdapter {
10
+ readonly id = "js";
11
+ readonly name = "JavaScript/TypeScript";
12
+ readonly extensions: string[];
13
+ extractFunctions(source: string, filePath?: string): FunctionFact[];
14
+ extractImports(source: string): ImportFact[];
15
+ extractErrorHandlers(source: string): ErrorHandlerFact[];
16
+ extractNamingPatterns(source: string): NamingPattern[];
17
+ stripComments(source: string): string;
18
+ extractComparisonOps(source: string): string[];
19
+ countBranches(source: string): number;
20
+ countReturns(source: string): number;
21
+ private extractBraceBody;
22
+ private extractCatchCallbackBody;
23
+ private classifyJsErrorStrategy;
24
+ }
25
+ export declare const jsAdapter: JsAdapter;
26
+ export {};
@@ -0,0 +1,279 @@
1
+ /**
2
+ * JavaScript/TypeScript Language Adapter
3
+ *
4
+ * Handles: .ts, .tsx, .js, .jsx, .mjs, .cjs
5
+ *
6
+ * Regex-based static analysis for JS/TS code patterns.
7
+ */
8
+ import { classifyCasing } from './types.js';
9
+ class JsAdapter {
10
+ id = 'js';
11
+ name = 'JavaScript/TypeScript';
12
+ extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs'];
13
+ extractFunctions(source, filePath) {
14
+ const functions = [];
15
+ const lines = source.split('\n');
16
+ // Match: function foo() { ... }, async function foo() { ... }
17
+ for (let i = 0; i < lines.length; i++) {
18
+ const line = lines[i];
19
+ // Traditional function declaration
20
+ let match = line.match(/(?:^|\s)(async\s+)?function\s+(\w+)\s*\(/);
21
+ if (match) {
22
+ const isAsync = !!match[1];
23
+ const name = match[2];
24
+ const body = this.extractBraceBody(lines, i);
25
+ const endLine = i + body.length;
26
+ const isExported = /\bexport\b/.test(lines.slice(Math.max(0, i - 2), i + 1).join(' '));
27
+ functions.push({
28
+ name, startLine: i + 1, endLine: endLine + 1,
29
+ body: body.join('\n'), isAsync, isExported,
30
+ });
31
+ }
32
+ // Arrow function: const foo = () => { ... }
33
+ match = line.match(/(?:const|let|var)\s+(\w+)\s*=\s*(async\s+)?\([^)]*\)\s*=>\s*\{/);
34
+ if (match) {
35
+ const name = match[1];
36
+ const isAsync = !!match[2];
37
+ const body = this.extractBraceBody(lines, i);
38
+ const endLine = i + body.length;
39
+ const isExported = /\bexport\b/.test(lines.slice(Math.max(0, i - 2), i + 1).join(' '));
40
+ functions.push({
41
+ name, startLine: i + 1, endLine: endLine + 1,
42
+ body: body.join('\n'), isAsync, isExported,
43
+ });
44
+ }
45
+ // Export function: export function foo() { ... }
46
+ match = line.match(/\bexport\s+(?:async\s+)?function\s+(\w+)\s*\(/);
47
+ if (match) {
48
+ const name = match[1];
49
+ const isAsync = /\basync\b/.test(line);
50
+ const body = this.extractBraceBody(lines, i);
51
+ const endLine = i + body.length;
52
+ functions.push({
53
+ name, startLine: i + 1, endLine: endLine + 1,
54
+ body: body.join('\n'), isAsync, isExported: true,
55
+ });
56
+ }
57
+ // Class method: methodName() { ... }
58
+ match = line.match(/^\s*(?:async\s+)?(\w+)\s*\([^)]*\)\s*\{/);
59
+ if (match && i > 0) {
60
+ const prevLine = lines[i - 1];
61
+ if (/\bclass\s+\w+/.test(prevLine) || /^\s+constructor\s*\(/.test(line) || !prevLine.includes('function')) {
62
+ const name = match[1];
63
+ const isAsync = /\basync\b/.test(line);
64
+ const body = this.extractBraceBody(lines, i);
65
+ const endLine = i + body.length;
66
+ functions.push({
67
+ name, startLine: i + 1, endLine: endLine + 1,
68
+ body: body.join('\n'), isAsync, isExported: false,
69
+ });
70
+ }
71
+ }
72
+ }
73
+ return functions;
74
+ }
75
+ extractImports(source) {
76
+ const imports = [];
77
+ const lines = source.split('\n');
78
+ for (let i = 0; i < lines.length; i++) {
79
+ const line = lines[i];
80
+ // import x from 'module'
81
+ let match = line.match(/^\s*import\s+(?:\{([^}]+)\}|(\w+)|(\*\s+as\s+(\w+)))\s+from\s+['"]([^'"]+)['"]/);
82
+ if (match) {
83
+ const names = match[1] ? match[1].split(',').map(s => s.trim()) : (match[4] ? [match[4]] : (match[2] ? [match[2]] : []));
84
+ imports.push({
85
+ module: match[5], names, line: i + 1, isDynamic: false,
86
+ });
87
+ }
88
+ // require('module')
89
+ match = line.match(/(?:const|let|var)\s+(?:\{([^}]+)\}|(\w+))\s*=\s*require\s*\(\s*['"]([^'"]+)['"]\s*\)/);
90
+ if (match) {
91
+ const names = match[1] ? match[1].split(',').map(s => s.trim()) : [match[2]];
92
+ imports.push({
93
+ module: match[3], names, line: i + 1, isDynamic: false,
94
+ });
95
+ }
96
+ // import('module') - dynamic
97
+ match = line.match(/import\s*\(\s*['"]([^'"]+)['"]\s*\)/);
98
+ if (match) {
99
+ imports.push({
100
+ module: match[1], names: [], line: i + 1, isDynamic: true,
101
+ });
102
+ }
103
+ }
104
+ return imports;
105
+ }
106
+ extractErrorHandlers(source) {
107
+ const handlers = [];
108
+ const lines = source.split('\n');
109
+ for (let i = 0; i < lines.length; i++) {
110
+ const line = lines[i];
111
+ // try { ... } catch (e) { ... }
112
+ if (/\btry\s*\{/.test(line)) {
113
+ for (let j = i + 1; j < lines.length; j++) {
114
+ if (/\bcatch\s*\(/.test(lines[j])) {
115
+ const body = this.extractBraceBody(lines, j);
116
+ const strategy = this.classifyJsErrorStrategy(body.join('\n'));
117
+ handlers.push({
118
+ type: 'try-catch', strategy,
119
+ startLine: j + 1, body: body.join('\n'),
120
+ });
121
+ break;
122
+ }
123
+ }
124
+ }
125
+ // .catch(e => { ... })
126
+ if (/\.catch\s*\(/.test(line)) {
127
+ const body = this.extractCatchCallbackBody(lines, i);
128
+ if (body) {
129
+ const strategy = this.classifyJsErrorStrategy(body);
130
+ handlers.push({
131
+ type: 'try-catch', strategy,
132
+ startLine: i + 1, body,
133
+ });
134
+ }
135
+ }
136
+ }
137
+ return handlers;
138
+ }
139
+ extractNamingPatterns(source) {
140
+ const patterns = [];
141
+ const lines = source.split('\n');
142
+ for (let i = 0; i < lines.length; i++) {
143
+ const line = lines[i];
144
+ // function foo() { ... }
145
+ let match = line.match(/\bfunction\s+(\w+)\s*\(/);
146
+ if (match) {
147
+ patterns.push({
148
+ name: match[1], kind: 'function',
149
+ convention: classifyCasing(match[1]),
150
+ });
151
+ }
152
+ // class Foo { ... }
153
+ match = line.match(/\bclass\s+(\w+)/);
154
+ if (match) {
155
+ patterns.push({
156
+ name: match[1], kind: 'class',
157
+ convention: classifyCasing(match[1]),
158
+ });
159
+ }
160
+ // const foo = ..., let foo = ..., var foo = ...
161
+ match = line.match(/(?:const|let|var)\s+(\w+)\s*=/);
162
+ if (match && !line.includes('function')) {
163
+ const isFunctionExpr = /=\s*(?:async\s+)?\(/.test(line) || /=>\s*/.test(line);
164
+ const kind = isFunctionExpr ? 'function' : 'variable';
165
+ patterns.push({
166
+ name: match[1], kind,
167
+ convention: classifyCasing(match[1]),
168
+ });
169
+ }
170
+ // Method names in classes
171
+ match = line.match(/^\s+(\w+)\s*\([^)]*\)\s*\{/);
172
+ if (match && /\bclass\b/.test(lines.slice(Math.max(0, i - 10), i).join(' '))) {
173
+ patterns.push({
174
+ name: match[1], kind: 'method',
175
+ convention: classifyCasing(match[1]),
176
+ });
177
+ }
178
+ }
179
+ return patterns;
180
+ }
181
+ stripComments(source) {
182
+ // Remove line comments
183
+ let result = source.replace(/\/\/.*/g, '');
184
+ // Remove block comments
185
+ result = result.replace(/\/\*[\s\S]*?\*\//g, '');
186
+ return result;
187
+ }
188
+ extractComparisonOps(source) {
189
+ const ops = [];
190
+ const cleaned = this.stripComments(source);
191
+ const matches = cleaned.matchAll(/(===|!==|==|!=|>=|<=|>(?!=)|<(?!=))/g);
192
+ for (const m of matches) {
193
+ ops.push(m[1]);
194
+ }
195
+ return ops;
196
+ }
197
+ countBranches(source) {
198
+ let count = 0;
199
+ const cleaned = this.stripComments(source);
200
+ count += (cleaned.match(/\bif\s*\(/g) || []).length;
201
+ count += (cleaned.match(/\belse\s+if\s*\(/g) || []).length;
202
+ count += (cleaned.match(/\belse\s*\{/g) || []).length;
203
+ count += (cleaned.match(/\bswitch\s*\(/g) || []).length;
204
+ count += (cleaned.match(/\bcase\s+/g) || []).length;
205
+ count += (cleaned.match(/\?\s*[^?]/g) || []).length;
206
+ return count;
207
+ }
208
+ countReturns(source) {
209
+ return (source.match(/\breturn\b/g) || []).length;
210
+ }
211
+ extractBraceBody(lines, startIndex) {
212
+ let braceDepth = 0;
213
+ let started = false;
214
+ const body = [];
215
+ for (let i = startIndex; i < lines.length; i++) {
216
+ const line = lines[i];
217
+ for (const ch of line) {
218
+ if (ch === '{') {
219
+ braceDepth++;
220
+ started = true;
221
+ }
222
+ if (ch === '}')
223
+ braceDepth--;
224
+ }
225
+ if (started)
226
+ body.push(line);
227
+ if (started && braceDepth === 0)
228
+ break;
229
+ }
230
+ return body;
231
+ }
232
+ extractCatchCallbackBody(lines, startLine) {
233
+ const hasArrow = lines[startLine]?.includes('=>');
234
+ let depth = 0;
235
+ let started = false;
236
+ const body = [];
237
+ for (let i = startLine; i < Math.min(startLine + 20, lines.length); i++) {
238
+ for (const ch of lines[i]) {
239
+ if (hasArrow) {
240
+ if (ch === '{') {
241
+ depth++;
242
+ started = true;
243
+ }
244
+ if (ch === '}')
245
+ depth--;
246
+ }
247
+ else {
248
+ if (ch === '{' || ch === '(') {
249
+ depth++;
250
+ started = true;
251
+ }
252
+ if (ch === '}' || ch === ')')
253
+ depth--;
254
+ }
255
+ }
256
+ if (started && i > startLine)
257
+ body.push(lines[i]);
258
+ if (started && depth <= 0)
259
+ break;
260
+ }
261
+ return body.length > 0 ? body.join('\n') : null;
262
+ }
263
+ classifyJsErrorStrategy(body) {
264
+ const trimmed = body.trim();
265
+ if (!trimmed || trimmed === '{}')
266
+ return 'swallow';
267
+ if (/\bthrow\b/.test(trimmed)) {
268
+ if (/\bthrow\s+new\b/.test(trimmed))
269
+ return 'wrap';
270
+ return 'rethrow';
271
+ }
272
+ if (/\breturn\s+null\b|\breturn\s+undefined\b|\breturn\s*;/.test(trimmed))
273
+ return 'return-error';
274
+ if (/console\.(error|warn|log)\b/.test(trimmed))
275
+ return 'log';
276
+ return 'other';
277
+ }
278
+ }
279
+ export const jsAdapter = new JsAdapter();
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Python Language Adapter
3
+ *
4
+ * Handles: .py, .pyw
5
+ *
6
+ * Regex-based static analysis for Python code patterns.
7
+ */
8
+ import type { LanguageAdapter, FunctionFact, ImportFact, ErrorHandlerFact, NamingPattern } from './types.js';
9
+ declare class PythonAdapter implements LanguageAdapter {
10
+ readonly id = "python";
11
+ readonly name = "Python";
12
+ readonly extensions: string[];
13
+ extractFunctions(source: string, filePath?: string): FunctionFact[];
14
+ extractImports(source: string): ImportFact[];
15
+ extractErrorHandlers(source: string): ErrorHandlerFact[];
16
+ extractNamingPatterns(source: string): NamingPattern[];
17
+ stripComments(source: string): string;
18
+ extractComparisonOps(source: string): string[];
19
+ countBranches(source: string): number;
20
+ countReturns(source: string): number;
21
+ private extractIndentBody;
22
+ private classifyPythonErrorStrategy;
23
+ }
24
+ export declare const pythonAdapter: PythonAdapter;
25
+ export {};