@rigour-labs/core 3.0.5 → 4.0.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.
- package/dist/deep/fact-extractor.d.ts +80 -0
- package/dist/deep/fact-extractor.js +626 -0
- package/dist/deep/index.d.ts +14 -0
- package/dist/deep/index.js +12 -0
- package/dist/deep/prompts.d.ts +22 -0
- package/dist/deep/prompts.js +374 -0
- package/dist/deep/verifier.d.ts +16 -0
- package/dist/deep/verifier.js +388 -0
- package/dist/gates/deep-analysis.d.ts +28 -0
- package/dist/gates/deep-analysis.js +302 -0
- package/dist/gates/deprecated-apis-rules-lang.d.ts +21 -0
- package/dist/gates/deprecated-apis-rules-lang.js +311 -0
- package/dist/gates/deprecated-apis-rules-node.d.ts +19 -0
- package/dist/gates/deprecated-apis-rules-node.js +199 -0
- package/dist/gates/deprecated-apis-rules.d.ts +6 -0
- package/dist/gates/deprecated-apis-rules.js +6 -0
- package/dist/gates/deprecated-apis.js +1 -502
- package/dist/gates/hallucinated-imports-lang.d.ts +16 -0
- package/dist/gates/hallucinated-imports-lang.js +374 -0
- package/dist/gates/hallucinated-imports-stdlib.d.ts +12 -0
- package/dist/gates/hallucinated-imports-stdlib.js +228 -0
- package/dist/gates/hallucinated-imports.d.ts +0 -98
- package/dist/gates/hallucinated-imports.js +10 -678
- package/dist/gates/phantom-apis-data.d.ts +33 -0
- package/dist/gates/phantom-apis-data.js +398 -0
- package/dist/gates/phantom-apis.js +1 -393
- package/dist/gates/phantom-apis.test.js +52 -0
- package/dist/gates/promise-safety-helpers.d.ts +19 -0
- package/dist/gates/promise-safety-helpers.js +101 -0
- package/dist/gates/promise-safety-rules.d.ts +7 -0
- package/dist/gates/promise-safety-rules.js +19 -0
- package/dist/gates/promise-safety.d.ts +1 -21
- package/dist/gates/promise-safety.js +51 -257
- package/dist/gates/runner.d.ts +4 -2
- package/dist/gates/runner.js +46 -1
- package/dist/gates/test-quality-lang.d.ts +30 -0
- package/dist/gates/test-quality-lang.js +188 -0
- package/dist/gates/test-quality.d.ts +0 -14
- package/dist/gates/test-quality.js +13 -186
- package/dist/index.d.ts +10 -0
- package/dist/index.js +12 -2
- package/dist/inference/cloud-provider.d.ts +34 -0
- package/dist/inference/cloud-provider.js +126 -0
- package/dist/inference/index.d.ts +17 -0
- package/dist/inference/index.js +23 -0
- package/dist/inference/model-manager.d.ts +26 -0
- package/dist/inference/model-manager.js +106 -0
- package/dist/inference/sidecar-provider.d.ts +15 -0
- package/dist/inference/sidecar-provider.js +153 -0
- package/dist/inference/types.d.ts +77 -0
- package/dist/inference/types.js +19 -0
- package/dist/pattern-index/indexer-helpers.d.ts +38 -0
- package/dist/pattern-index/indexer-helpers.js +111 -0
- package/dist/pattern-index/indexer-lang.d.ts +13 -0
- package/dist/pattern-index/indexer-lang.js +244 -0
- package/dist/pattern-index/indexer-ts.d.ts +22 -0
- package/dist/pattern-index/indexer-ts.js +258 -0
- package/dist/pattern-index/indexer.d.ts +4 -106
- package/dist/pattern-index/indexer.js +58 -707
- package/dist/pattern-index/staleness-data.d.ts +6 -0
- package/dist/pattern-index/staleness-data.js +262 -0
- package/dist/pattern-index/staleness.js +1 -258
- package/dist/settings.d.ts +104 -0
- package/dist/settings.js +186 -0
- package/dist/storage/db.d.ts +16 -0
- package/dist/storage/db.js +132 -0
- package/dist/storage/findings.d.ts +14 -0
- package/dist/storage/findings.js +38 -0
- package/dist/storage/index.d.ts +9 -0
- package/dist/storage/index.js +8 -0
- package/dist/storage/patterns.d.ts +35 -0
- package/dist/storage/patterns.js +62 -0
- package/dist/storage/scans.d.ts +42 -0
- package/dist/storage/scans.js +55 -0
- package/dist/templates/index.d.ts +12 -16
- package/dist/templates/index.js +11 -527
- package/dist/templates/paradigms.d.ts +2 -0
- package/dist/templates/paradigms.js +46 -0
- package/dist/templates/presets.d.ts +14 -0
- package/dist/templates/presets.js +227 -0
- package/dist/templates/universal-config.d.ts +2 -0
- package/dist/templates/universal-config.js +190 -0
- package/dist/types/index.d.ts +438 -15
- package/dist/types/index.js +41 -1
- package/package.json +6 -2
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Indexer — Language-Specific Extractors
|
|
3
|
+
*
|
|
4
|
+
* Standalone extraction functions for Go, Rust, JVM (Java/Kotlin/C#),
|
|
5
|
+
* Python, and a generic C-style fallback. Each function is pure and
|
|
6
|
+
* receives all required context as parameters.
|
|
7
|
+
*/
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { createPatternEntry, extractKeywords, getCOMLineComments, getJavaDoc, findBraceBlockEnd, getBraceBlockContent, } from './indexer-helpers.js';
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// Go
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
export function extractGoPatterns(filePath, content, rootDir) {
|
|
14
|
+
const patterns = [];
|
|
15
|
+
const relativePath = path.relative(rootDir, filePath);
|
|
16
|
+
const lines = content.split('\n');
|
|
17
|
+
const funcRegex = /^func\s+(?:\([^)]*\)\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*([^\{]*)\s*\{/;
|
|
18
|
+
const typeRegex = /^type\s+([A-Za-z_][A-Za-z0-9_]*)\s+(struct|interface)/;
|
|
19
|
+
for (let i = 0; i < lines.length; i++) {
|
|
20
|
+
const line = lines[i];
|
|
21
|
+
const funcMatch = line.match(funcRegex);
|
|
22
|
+
if (funcMatch) {
|
|
23
|
+
const name = funcMatch[1];
|
|
24
|
+
patterns.push(createPatternEntry({
|
|
25
|
+
type: 'function',
|
|
26
|
+
name,
|
|
27
|
+
file: relativePath,
|
|
28
|
+
line: i + 1,
|
|
29
|
+
endLine: findBraceBlockEnd(lines, i),
|
|
30
|
+
signature: `func ${name}(${funcMatch[2]}) ${funcMatch[3].trim()}`,
|
|
31
|
+
description: getCOMLineComments(lines, i - 1),
|
|
32
|
+
keywords: extractKeywords(name),
|
|
33
|
+
content: getBraceBlockContent(lines, i),
|
|
34
|
+
exported: /^[A-Z]/.test(name),
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
const typeMatch = line.match(typeRegex);
|
|
38
|
+
if (typeMatch) {
|
|
39
|
+
const name = typeMatch[1];
|
|
40
|
+
patterns.push(createPatternEntry({
|
|
41
|
+
type: typeMatch[2],
|
|
42
|
+
name,
|
|
43
|
+
file: relativePath,
|
|
44
|
+
line: i + 1,
|
|
45
|
+
endLine: findBraceBlockEnd(lines, i),
|
|
46
|
+
signature: `type ${name} ${typeMatch[2]}`,
|
|
47
|
+
description: getCOMLineComments(lines, i - 1),
|
|
48
|
+
keywords: extractKeywords(name),
|
|
49
|
+
content: getBraceBlockContent(lines, i),
|
|
50
|
+
exported: /^[A-Z]/.test(name),
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return patterns;
|
|
55
|
+
}
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
// Rust
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
export function extractRustPatterns(filePath, content, rootDir) {
|
|
60
|
+
const patterns = [];
|
|
61
|
+
const relativePath = path.relative(rootDir, filePath);
|
|
62
|
+
const lines = content.split('\n');
|
|
63
|
+
const fnRegex = /^(?:pub\s+)?(?:async\s+)?fn\s+([A-Za-z_][A-Za-z0-9_]*)\s*[<(][^)]*[>)]\s*(?:->\s*[^\{]+)?\s*\{/;
|
|
64
|
+
for (let i = 0; i < lines.length; i++) {
|
|
65
|
+
const line = lines[i];
|
|
66
|
+
const fnMatch = line.match(fnRegex);
|
|
67
|
+
if (fnMatch) {
|
|
68
|
+
const name = fnMatch[1];
|
|
69
|
+
patterns.push(createPatternEntry({
|
|
70
|
+
type: 'function',
|
|
71
|
+
name,
|
|
72
|
+
file: relativePath,
|
|
73
|
+
line: i + 1,
|
|
74
|
+
endLine: findBraceBlockEnd(lines, i),
|
|
75
|
+
signature: line.split('{')[0].trim(),
|
|
76
|
+
description: getCOMLineComments(lines, i - 1),
|
|
77
|
+
keywords: extractKeywords(name),
|
|
78
|
+
content: getBraceBlockContent(lines, i),
|
|
79
|
+
exported: line.startsWith('pub'),
|
|
80
|
+
}));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return patterns;
|
|
84
|
+
}
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
// JVM-style (Java, Kotlin, C#)
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
export function extractJVMStylePatterns(filePath, content, rootDir) {
|
|
89
|
+
const patterns = [];
|
|
90
|
+
const relativePath = path.relative(rootDir, filePath);
|
|
91
|
+
const lines = content.split('\n');
|
|
92
|
+
const classRegex = /^(?:public|private|protected|internal)?\s*(?:static\s+)?(?:final\s+)?(?:class|interface|enum)\s+([A-Za-z0-9_]+)/;
|
|
93
|
+
for (let i = 0; i < lines.length; i++) {
|
|
94
|
+
const line = lines[i].trim();
|
|
95
|
+
const classMatch = line.match(classRegex);
|
|
96
|
+
if (classMatch) {
|
|
97
|
+
patterns.push(createPatternEntry({
|
|
98
|
+
type: 'class',
|
|
99
|
+
name: classMatch[1],
|
|
100
|
+
file: relativePath,
|
|
101
|
+
line: i + 1,
|
|
102
|
+
endLine: findBraceBlockEnd(lines, i),
|
|
103
|
+
signature: line,
|
|
104
|
+
description: getJavaDoc(lines, i - 1),
|
|
105
|
+
keywords: extractKeywords(classMatch[1]),
|
|
106
|
+
content: getBraceBlockContent(lines, i),
|
|
107
|
+
exported: line.includes('public'),
|
|
108
|
+
}));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return patterns;
|
|
112
|
+
}
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
// Generic C-style fallback (C++, PHP, etc.)
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
export function extractGenericCPatterns(_filePath, _content) {
|
|
117
|
+
return [];
|
|
118
|
+
}
|
|
119
|
+
// ---------------------------------------------------------------------------
|
|
120
|
+
// Python
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
export function extractPythonPatterns(filePath, content, rootDir, minNameLength) {
|
|
123
|
+
const patterns = [];
|
|
124
|
+
const relativePath = path.relative(rootDir, filePath);
|
|
125
|
+
const lines = content.split('\n');
|
|
126
|
+
const classRegex = /^class\s+([A-Za-z_][A-Za-z0-9_]*)\s*(\([^)]*\))?\s*:/;
|
|
127
|
+
const funcRegex = /^(?:async\s+)?def\s+([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)\s*(?:->\s*[^:]+)?\s*:/;
|
|
128
|
+
const constRegex = /^([A-Z][A-Z0-9_]*)\s*=\s*(.+)$/;
|
|
129
|
+
for (let i = 0; i < lines.length; i++) {
|
|
130
|
+
const originalLine = lines[i];
|
|
131
|
+
const lineNum = i + 1;
|
|
132
|
+
const classMatch = originalLine.match(classRegex);
|
|
133
|
+
if (classMatch) {
|
|
134
|
+
const name = classMatch[1];
|
|
135
|
+
if (name.length >= minNameLength) {
|
|
136
|
+
patterns.push(createPatternEntry({
|
|
137
|
+
type: detectPythonClassType(name),
|
|
138
|
+
name,
|
|
139
|
+
file: relativePath,
|
|
140
|
+
line: lineNum,
|
|
141
|
+
endLine: findPythonBlockEnd(lines, i),
|
|
142
|
+
signature: `class ${name}${classMatch[2] || ''}`,
|
|
143
|
+
description: getPythonDocstring(lines, i + 1),
|
|
144
|
+
keywords: extractKeywords(name),
|
|
145
|
+
content: getPythonBlockContent(lines, i),
|
|
146
|
+
exported: !name.startsWith('_'),
|
|
147
|
+
}));
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const funcMatch = originalLine.match(funcRegex);
|
|
152
|
+
if (funcMatch) {
|
|
153
|
+
const name = funcMatch[1];
|
|
154
|
+
if (name.length >= minNameLength) {
|
|
155
|
+
patterns.push(createPatternEntry({
|
|
156
|
+
type: detectPythonFunctionType(name),
|
|
157
|
+
name,
|
|
158
|
+
file: relativePath,
|
|
159
|
+
line: lineNum,
|
|
160
|
+
endLine: findPythonBlockEnd(lines, i),
|
|
161
|
+
signature: `def ${name}(${funcMatch[2]})`,
|
|
162
|
+
description: getPythonDocstring(lines, i + 1),
|
|
163
|
+
keywords: extractKeywords(name),
|
|
164
|
+
content: getPythonBlockContent(lines, i),
|
|
165
|
+
exported: !name.startsWith('_'),
|
|
166
|
+
}));
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
const constMatch = originalLine.match(constRegex);
|
|
171
|
+
if (constMatch) {
|
|
172
|
+
const name = constMatch[1];
|
|
173
|
+
if (name.length >= minNameLength) {
|
|
174
|
+
patterns.push(createPatternEntry({
|
|
175
|
+
type: 'constant',
|
|
176
|
+
name,
|
|
177
|
+
file: relativePath,
|
|
178
|
+
line: lineNum,
|
|
179
|
+
endLine: lineNum,
|
|
180
|
+
signature: `${name} = ...`,
|
|
181
|
+
description: '',
|
|
182
|
+
keywords: extractKeywords(name),
|
|
183
|
+
content: originalLine,
|
|
184
|
+
exported: !name.startsWith('_'),
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return patterns;
|
|
190
|
+
}
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Python helpers (private to this module)
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
function detectPythonClassType(name) {
|
|
195
|
+
if (name.endsWith('Error') || name.endsWith('Exception'))
|
|
196
|
+
return 'error';
|
|
197
|
+
if (name.endsWith('Model'))
|
|
198
|
+
return 'model';
|
|
199
|
+
if (name.endsWith('Schema'))
|
|
200
|
+
return 'schema';
|
|
201
|
+
return 'class';
|
|
202
|
+
}
|
|
203
|
+
function detectPythonFunctionType(name) {
|
|
204
|
+
if (name.includes('middleware'))
|
|
205
|
+
return 'middleware';
|
|
206
|
+
if (name.includes('handler'))
|
|
207
|
+
return 'handler';
|
|
208
|
+
return 'function';
|
|
209
|
+
}
|
|
210
|
+
function getPythonDocstring(lines, startIndex) {
|
|
211
|
+
if (startIndex >= lines.length)
|
|
212
|
+
return '';
|
|
213
|
+
const nextLine = lines[startIndex].trim();
|
|
214
|
+
if (nextLine.startsWith('"""') || nextLine.startsWith("'''")) {
|
|
215
|
+
const quote = nextLine.startsWith('"""') ? '"""' : "'''";
|
|
216
|
+
let doc = nextLine.replace(quote, '');
|
|
217
|
+
if (doc.endsWith(quote))
|
|
218
|
+
return doc.replace(quote, '').trim();
|
|
219
|
+
for (let i = startIndex + 1; i < lines.length; i++) {
|
|
220
|
+
if (lines[i].includes(quote)) {
|
|
221
|
+
doc += ' ' + lines[i].split(quote)[0].trim();
|
|
222
|
+
break;
|
|
223
|
+
}
|
|
224
|
+
doc += ' ' + lines[i].trim();
|
|
225
|
+
}
|
|
226
|
+
return doc.trim();
|
|
227
|
+
}
|
|
228
|
+
return '';
|
|
229
|
+
}
|
|
230
|
+
function findPythonBlockEnd(lines, startIndex) {
|
|
231
|
+
const startIndent = lines[startIndex].search(/\S/);
|
|
232
|
+
for (let i = startIndex + 1; i < lines.length; i++) {
|
|
233
|
+
if (lines[i].trim() === '')
|
|
234
|
+
continue;
|
|
235
|
+
const currentIndent = lines[i].search(/\S/);
|
|
236
|
+
if (currentIndent <= startIndent)
|
|
237
|
+
return i;
|
|
238
|
+
}
|
|
239
|
+
return lines.length;
|
|
240
|
+
}
|
|
241
|
+
function getPythonBlockContent(lines, startIndex) {
|
|
242
|
+
const endLine = findPythonBlockEnd(lines, startIndex);
|
|
243
|
+
return lines.slice(startIndex, endLine).join('\n');
|
|
244
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Indexer — TypeScript / JavaScript AST Helpers
|
|
3
|
+
*
|
|
4
|
+
* Standalone functions for converting TypeScript AST nodes into PatternEntry
|
|
5
|
+
* records and extracting type metadata. No class state is referenced here.
|
|
6
|
+
*/
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import type { PatternEntry, PatternType } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Attempt to convert an AST node into a PatternEntry.
|
|
11
|
+
* Returns `null` for unrecognised node kinds.
|
|
12
|
+
*/
|
|
13
|
+
export declare function nodeToPattern(node: ts.Node, sourceFile: ts.SourceFile, filePath: string, _content: string, minNameLength: number): PatternEntry | null;
|
|
14
|
+
export declare function detectFunctionType(name: string, node: ts.Node): PatternType;
|
|
15
|
+
export declare function detectClassType(name: string, node: ts.ClassDeclaration): PatternType;
|
|
16
|
+
export declare function getFunctionSignature(node: ts.FunctionDeclaration, sourceFile: ts.SourceFile): string;
|
|
17
|
+
export declare function getArrowFunctionSignature(node: ts.ArrowFunction | ts.FunctionExpression, sourceFile: ts.SourceFile): string;
|
|
18
|
+
export declare function getClassSignature(node: ts.ClassDeclaration, sourceFile: ts.SourceFile): string;
|
|
19
|
+
export declare function getInterfaceSignature(node: ts.InterfaceDeclaration, sourceFile: ts.SourceFile): string;
|
|
20
|
+
export declare function getJSDocDescription(node: ts.Node, sourceFile: ts.SourceFile): string;
|
|
21
|
+
export declare function isExported(node: ts.Node): boolean;
|
|
22
|
+
export declare function containsJSX(node: ts.Node): boolean;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Indexer — TypeScript / JavaScript AST Helpers
|
|
3
|
+
*
|
|
4
|
+
* Standalone functions for converting TypeScript AST nodes into PatternEntry
|
|
5
|
+
* records and extracting type metadata. No class state is referenced here.
|
|
6
|
+
*/
|
|
7
|
+
import ts from 'typescript';
|
|
8
|
+
import { createPatternEntry, extractKeywords } from './indexer-helpers.js';
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Main node → PatternEntry dispatcher
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
/**
|
|
13
|
+
* Attempt to convert an AST node into a PatternEntry.
|
|
14
|
+
* Returns `null` for unrecognised node kinds.
|
|
15
|
+
*/
|
|
16
|
+
export function nodeToPattern(node, sourceFile, filePath, _content, minNameLength) {
|
|
17
|
+
const startPos = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
18
|
+
const endPos = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
|
|
19
|
+
const line = startPos.line + 1;
|
|
20
|
+
const endLine = endPos.line + 1;
|
|
21
|
+
// Function declarations
|
|
22
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
23
|
+
const name = node.name.text;
|
|
24
|
+
if (name.length < minNameLength)
|
|
25
|
+
return null;
|
|
26
|
+
return createPatternEntry({
|
|
27
|
+
type: detectFunctionType(name, node),
|
|
28
|
+
name,
|
|
29
|
+
file: filePath,
|
|
30
|
+
line,
|
|
31
|
+
endLine,
|
|
32
|
+
signature: getFunctionSignature(node, sourceFile),
|
|
33
|
+
description: getJSDocDescription(node, sourceFile),
|
|
34
|
+
keywords: extractKeywords(name),
|
|
35
|
+
content: node.getText(sourceFile),
|
|
36
|
+
exported: isExported(node),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// Variable declarations with arrow functions or function expressions
|
|
40
|
+
if (ts.isVariableStatement(node)) {
|
|
41
|
+
for (const decl of node.declarationList.declarations) {
|
|
42
|
+
if (ts.isIdentifier(decl.name) && decl.initializer) {
|
|
43
|
+
const name = decl.name.text;
|
|
44
|
+
if (name.length < minNameLength)
|
|
45
|
+
continue;
|
|
46
|
+
if (ts.isArrowFunction(decl.initializer) ||
|
|
47
|
+
ts.isFunctionExpression(decl.initializer)) {
|
|
48
|
+
return createPatternEntry({
|
|
49
|
+
type: detectFunctionType(name, decl.initializer),
|
|
50
|
+
name,
|
|
51
|
+
file: filePath,
|
|
52
|
+
line,
|
|
53
|
+
endLine,
|
|
54
|
+
signature: getArrowFunctionSignature(decl.initializer, sourceFile),
|
|
55
|
+
description: getJSDocDescription(node, sourceFile),
|
|
56
|
+
keywords: extractKeywords(name),
|
|
57
|
+
content: node.getText(sourceFile),
|
|
58
|
+
exported: isExported(node),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// Constants — UPPER_CASE `const` declarations with a simple initialiser
|
|
62
|
+
const isConst = node.declarationList.flags & ts.NodeFlags.Const;
|
|
63
|
+
if (isConst &&
|
|
64
|
+
name === name.toUpperCase() &&
|
|
65
|
+
(ts.isStringLiteral(decl.initializer) ||
|
|
66
|
+
ts.isNumericLiteral(decl.initializer) ||
|
|
67
|
+
ts.isObjectLiteralExpression(decl.initializer))) {
|
|
68
|
+
return createPatternEntry({
|
|
69
|
+
type: 'constant',
|
|
70
|
+
name,
|
|
71
|
+
file: filePath,
|
|
72
|
+
line,
|
|
73
|
+
endLine,
|
|
74
|
+
signature: '',
|
|
75
|
+
description: getJSDocDescription(node, sourceFile),
|
|
76
|
+
keywords: extractKeywords(name),
|
|
77
|
+
content: node.getText(sourceFile),
|
|
78
|
+
exported: isExported(node),
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Class declarations
|
|
85
|
+
if (ts.isClassDeclaration(node) && node.name) {
|
|
86
|
+
const name = node.name.text;
|
|
87
|
+
if (name.length < minNameLength)
|
|
88
|
+
return null;
|
|
89
|
+
return createPatternEntry({
|
|
90
|
+
type: detectClassType(name, node),
|
|
91
|
+
name,
|
|
92
|
+
file: filePath,
|
|
93
|
+
line,
|
|
94
|
+
endLine,
|
|
95
|
+
signature: getClassSignature(node, sourceFile),
|
|
96
|
+
description: getJSDocDescription(node, sourceFile),
|
|
97
|
+
keywords: extractKeywords(name),
|
|
98
|
+
content: node.getText(sourceFile),
|
|
99
|
+
exported: isExported(node),
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
// Interface declarations
|
|
103
|
+
if (ts.isInterfaceDeclaration(node)) {
|
|
104
|
+
const name = node.name.text;
|
|
105
|
+
if (name.length < minNameLength)
|
|
106
|
+
return null;
|
|
107
|
+
return createPatternEntry({
|
|
108
|
+
type: 'interface',
|
|
109
|
+
name,
|
|
110
|
+
file: filePath,
|
|
111
|
+
line,
|
|
112
|
+
endLine,
|
|
113
|
+
signature: getInterfaceSignature(node, sourceFile),
|
|
114
|
+
description: getJSDocDescription(node, sourceFile),
|
|
115
|
+
keywords: extractKeywords(name),
|
|
116
|
+
content: node.getText(sourceFile),
|
|
117
|
+
exported: isExported(node),
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
// Type alias declarations
|
|
121
|
+
if (ts.isTypeAliasDeclaration(node)) {
|
|
122
|
+
const name = node.name.text;
|
|
123
|
+
if (name.length < minNameLength)
|
|
124
|
+
return null;
|
|
125
|
+
return createPatternEntry({
|
|
126
|
+
type: 'type',
|
|
127
|
+
name,
|
|
128
|
+
file: filePath,
|
|
129
|
+
line,
|
|
130
|
+
endLine,
|
|
131
|
+
signature: node.getText(sourceFile).split('=')[0].trim(),
|
|
132
|
+
description: getJSDocDescription(node, sourceFile),
|
|
133
|
+
keywords: extractKeywords(name),
|
|
134
|
+
content: node.getText(sourceFile),
|
|
135
|
+
exported: isExported(node),
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
// Enum declarations
|
|
139
|
+
if (ts.isEnumDeclaration(node)) {
|
|
140
|
+
const name = node.name.text;
|
|
141
|
+
if (name.length < minNameLength)
|
|
142
|
+
return null;
|
|
143
|
+
return createPatternEntry({
|
|
144
|
+
type: 'enum',
|
|
145
|
+
name,
|
|
146
|
+
file: filePath,
|
|
147
|
+
line,
|
|
148
|
+
endLine,
|
|
149
|
+
signature: `enum ${name}`,
|
|
150
|
+
description: getJSDocDescription(node, sourceFile),
|
|
151
|
+
keywords: extractKeywords(name),
|
|
152
|
+
content: node.getText(sourceFile),
|
|
153
|
+
exported: isExported(node),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
// ---------------------------------------------------------------------------
|
|
159
|
+
// Type detection
|
|
160
|
+
// ---------------------------------------------------------------------------
|
|
161
|
+
export function detectFunctionType(name, node) {
|
|
162
|
+
// React hooks — useXxx
|
|
163
|
+
if (name.startsWith('use') && name.length > 3 && name[3] === name[3].toUpperCase()) {
|
|
164
|
+
return 'hook';
|
|
165
|
+
}
|
|
166
|
+
// React components — PascalCase returning JSX
|
|
167
|
+
if (name[0] === name[0].toUpperCase() && containsJSX(node)) {
|
|
168
|
+
return 'component';
|
|
169
|
+
}
|
|
170
|
+
if (name.includes('Middleware') || name.includes('middleware'))
|
|
171
|
+
return 'middleware';
|
|
172
|
+
if (name.includes('Handler') || name.includes('handler'))
|
|
173
|
+
return 'handler';
|
|
174
|
+
if (name.startsWith('create') || name.startsWith('make') || name.includes('Factory'))
|
|
175
|
+
return 'factory';
|
|
176
|
+
return 'function';
|
|
177
|
+
}
|
|
178
|
+
export function detectClassType(name, node) {
|
|
179
|
+
if (name.endsWith('Error') || name.endsWith('Exception'))
|
|
180
|
+
return 'error';
|
|
181
|
+
if (node.heritageClauses) {
|
|
182
|
+
for (const clause of node.heritageClauses) {
|
|
183
|
+
const text = clause.getText();
|
|
184
|
+
if (text.includes('Component') || text.includes('PureComponent'))
|
|
185
|
+
return 'component';
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (name.endsWith('Store') || name.endsWith('State'))
|
|
189
|
+
return 'store';
|
|
190
|
+
if (name.endsWith('Model') || name.endsWith('Entity'))
|
|
191
|
+
return 'model';
|
|
192
|
+
return 'class';
|
|
193
|
+
}
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Signature helpers
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
export function getFunctionSignature(node, sourceFile) {
|
|
198
|
+
const params = node.parameters.map(p => p.getText(sourceFile)).join(', ');
|
|
199
|
+
const returnType = node.type ? `: ${node.type.getText(sourceFile)}` : '';
|
|
200
|
+
return `(${params})${returnType}`;
|
|
201
|
+
}
|
|
202
|
+
export function getArrowFunctionSignature(node, sourceFile) {
|
|
203
|
+
const params = node.parameters.map(p => p.getText(sourceFile)).join(', ');
|
|
204
|
+
const returnType = node.type ? `: ${node.type.getText(sourceFile)}` : '';
|
|
205
|
+
return `(${params})${returnType}`;
|
|
206
|
+
}
|
|
207
|
+
export function getClassSignature(node, sourceFile) {
|
|
208
|
+
let sig = `class ${node.name?.text || 'Anonymous'}`;
|
|
209
|
+
if (node.heritageClauses) {
|
|
210
|
+
sig += ' ' + node.heritageClauses.map(c => c.getText(sourceFile)).join(' ');
|
|
211
|
+
}
|
|
212
|
+
return sig;
|
|
213
|
+
}
|
|
214
|
+
export function getInterfaceSignature(node, sourceFile) {
|
|
215
|
+
let sig = `interface ${node.name.text}`;
|
|
216
|
+
if (node.typeParameters) {
|
|
217
|
+
sig += `<${node.typeParameters.map(p => p.getText(sourceFile)).join(', ')}>`;
|
|
218
|
+
}
|
|
219
|
+
return sig;
|
|
220
|
+
}
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// JSDoc / export helpers
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
export function getJSDocDescription(node, sourceFile) {
|
|
225
|
+
const jsDocComment = ts.getJSDocCommentsAndTags(node);
|
|
226
|
+
for (const tag of jsDocComment) {
|
|
227
|
+
if (ts.isJSDoc(tag) && tag.comment) {
|
|
228
|
+
if (typeof tag.comment === 'string')
|
|
229
|
+
return tag.comment;
|
|
230
|
+
return tag.comment.map(c => c.getText(sourceFile)).join(' ');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return '';
|
|
234
|
+
}
|
|
235
|
+
export function isExported(node) {
|
|
236
|
+
if (ts.canHaveModifiers(node)) {
|
|
237
|
+
const modifiers = ts.getModifiers(node);
|
|
238
|
+
if (modifiers) {
|
|
239
|
+
return modifiers.some(m => m.kind === ts.SyntaxKind.ExportKeyword);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
// ---------------------------------------------------------------------------
|
|
245
|
+
// JSX detection
|
|
246
|
+
// ---------------------------------------------------------------------------
|
|
247
|
+
export function containsJSX(node) {
|
|
248
|
+
let hasJSX = false;
|
|
249
|
+
const visit = (n) => {
|
|
250
|
+
if (ts.isJsxElement(n) || ts.isJsxSelfClosingElement(n) || ts.isJsxFragment(n)) {
|
|
251
|
+
hasJSX = true;
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
ts.forEachChild(n, visit);
|
|
255
|
+
};
|
|
256
|
+
visit(node);
|
|
257
|
+
return hasJSX;
|
|
258
|
+
}
|
|
@@ -3,125 +3,23 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Scans the codebase and extracts patterns using AST parsing.
|
|
5
5
|
* This is the core engine of the Pattern Index system.
|
|
6
|
+
*
|
|
7
|
+
* Language-specific extractors live in indexer-lang.ts.
|
|
8
|
+
* TypeScript/JS AST helpers live in indexer-ts.ts.
|
|
9
|
+
* Pure utility functions live in indexer-helpers.ts.
|
|
6
10
|
*/
|
|
7
11
|
import type { PatternIndex, PatternIndexConfig } from './types.js';
|
|
8
|
-
/**
|
|
9
|
-
* Pattern Indexer class.
|
|
10
|
-
* Responsible for scanning and indexing code patterns.
|
|
11
|
-
*/
|
|
12
12
|
export declare class PatternIndexer {
|
|
13
13
|
private config;
|
|
14
14
|
private rootDir;
|
|
15
15
|
constructor(rootDir: string, config?: Partial<PatternIndexConfig>);
|
|
16
16
|
buildIndex(): Promise<PatternIndex>;
|
|
17
|
-
/**
|
|
18
|
-
* Incremental index update - only reindex changed files.
|
|
19
|
-
*/
|
|
20
17
|
updateIndex(existingIndex: PatternIndex): Promise<PatternIndex>;
|
|
21
|
-
/**
|
|
22
|
-
* Find all files to index based on configuration.
|
|
23
|
-
*/
|
|
24
18
|
private findFiles;
|
|
25
|
-
/**
|
|
26
|
-
* Extract patterns from a single file using TypeScript AST.
|
|
27
|
-
*/
|
|
28
19
|
private extractPatterns;
|
|
29
|
-
/**
|
|
30
|
-
* Extract patterns from Go files.
|
|
31
|
-
*/
|
|
32
|
-
private extractGoPatterns;
|
|
33
|
-
/**
|
|
34
|
-
* Extract patterns from Rust files.
|
|
35
|
-
*/
|
|
36
|
-
private extractRustPatterns;
|
|
37
|
-
/**
|
|
38
|
-
* Generic extraction for C-style languages (Java, C++, PHP, etc.)
|
|
39
|
-
*/
|
|
40
|
-
private extractJVMStylePatterns;
|
|
41
|
-
private extractGenericCPatterns;
|
|
42
|
-
private getCOMLineComments;
|
|
43
|
-
private getJavaDoc;
|
|
44
|
-
private findBraceBlockEnd;
|
|
45
|
-
private getBraceBlockContent;
|
|
46
|
-
/**
|
|
47
|
-
* Extract patterns from Python files using regex.
|
|
48
|
-
*/
|
|
49
|
-
private extractPythonPatterns;
|
|
50
|
-
private detectPythonClassType;
|
|
51
|
-
private detectPythonFunctionType;
|
|
52
|
-
private getPythonDocstring;
|
|
53
|
-
private findPythonBlockEnd;
|
|
54
|
-
private getPythonBlockContent;
|
|
55
|
-
/**
|
|
56
|
-
* Convert an AST node to a PatternEntry if applicable.
|
|
57
|
-
*/
|
|
58
|
-
private nodeToPattern;
|
|
59
|
-
/**
|
|
60
|
-
* Detect the specific type of a function based on naming conventions.
|
|
61
|
-
*/
|
|
62
|
-
private detectFunctionType;
|
|
63
|
-
/**
|
|
64
|
-
* Detect the specific type of a class.
|
|
65
|
-
*/
|
|
66
|
-
private detectClassType;
|
|
67
|
-
/**
|
|
68
|
-
* Check if a node contains JSX.
|
|
69
|
-
*/
|
|
70
|
-
private containsJSX;
|
|
71
|
-
/**
|
|
72
|
-
* Get function signature.
|
|
73
|
-
*/
|
|
74
|
-
private getFunctionSignature;
|
|
75
|
-
/**
|
|
76
|
-
* Get arrow function signature.
|
|
77
|
-
*/
|
|
78
|
-
private getArrowFunctionSignature;
|
|
79
|
-
/**
|
|
80
|
-
* Get class signature.
|
|
81
|
-
*/
|
|
82
|
-
private getClassSignature;
|
|
83
|
-
/**
|
|
84
|
-
* Get interface signature.
|
|
85
|
-
*/
|
|
86
|
-
private getInterfaceSignature;
|
|
87
|
-
/**
|
|
88
|
-
* Extract JSDoc description from a node.
|
|
89
|
-
*/
|
|
90
|
-
private getJSDocDescription;
|
|
91
|
-
/**
|
|
92
|
-
* Check if a node is exported.
|
|
93
|
-
*/
|
|
94
|
-
private isExported;
|
|
95
|
-
/**
|
|
96
|
-
* Extract keywords from a name for semantic matching.
|
|
97
|
-
*/
|
|
98
|
-
private extractKeywords;
|
|
99
|
-
/**
|
|
100
|
-
* Create a PatternEntry with computed fields.
|
|
101
|
-
*/
|
|
102
|
-
private createPatternEntry;
|
|
103
|
-
/**
|
|
104
|
-
* Get the TypeScript ScriptKind for a file.
|
|
105
|
-
*/
|
|
106
20
|
private getScriptKind;
|
|
107
|
-
/**
|
|
108
|
-
* Calculate index statistics.
|
|
109
|
-
*/
|
|
110
21
|
private calculateStats;
|
|
111
|
-
/**
|
|
112
|
-
* Hash content using SHA-256.
|
|
113
|
-
*/
|
|
114
|
-
private hashContent;
|
|
115
22
|
}
|
|
116
|
-
/**
|
|
117
|
-
* Save a pattern index to disk.
|
|
118
|
-
*/
|
|
119
23
|
export declare function savePatternIndex(index: PatternIndex, outputPath: string): Promise<void>;
|
|
120
|
-
/**
|
|
121
|
-
* Load a pattern index from disk.
|
|
122
|
-
*/
|
|
123
24
|
export declare function loadPatternIndex(indexPath: string): Promise<PatternIndex | null>;
|
|
124
|
-
/**
|
|
125
|
-
* Get the default index path for a project.
|
|
126
|
-
*/
|
|
127
25
|
export declare function getDefaultIndexPath(rootDir: string): string;
|