@rigour-labs/core 3.0.4 → 3.0.6

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 (47) hide show
  1. package/dist/gates/deprecated-apis-rules-lang.d.ts +21 -0
  2. package/dist/gates/deprecated-apis-rules-lang.js +311 -0
  3. package/dist/gates/deprecated-apis-rules-node.d.ts +19 -0
  4. package/dist/gates/deprecated-apis-rules-node.js +199 -0
  5. package/dist/gates/deprecated-apis-rules.d.ts +6 -0
  6. package/dist/gates/deprecated-apis-rules.js +6 -0
  7. package/dist/gates/deprecated-apis.js +1 -502
  8. package/dist/gates/hallucinated-imports-lang.d.ts +16 -0
  9. package/dist/gates/hallucinated-imports-lang.js +374 -0
  10. package/dist/gates/hallucinated-imports-stdlib.d.ts +12 -0
  11. package/dist/gates/hallucinated-imports-stdlib.js +228 -0
  12. package/dist/gates/hallucinated-imports.d.ts +0 -98
  13. package/dist/gates/hallucinated-imports.js +10 -678
  14. package/dist/gates/phantom-apis-data.d.ts +33 -0
  15. package/dist/gates/phantom-apis-data.js +398 -0
  16. package/dist/gates/phantom-apis.js +1 -393
  17. package/dist/gates/phantom-apis.test.js +52 -0
  18. package/dist/gates/promise-safety-helpers.d.ts +19 -0
  19. package/dist/gates/promise-safety-helpers.js +101 -0
  20. package/dist/gates/promise-safety-rules.d.ts +7 -0
  21. package/dist/gates/promise-safety-rules.js +19 -0
  22. package/dist/gates/promise-safety.d.ts +1 -21
  23. package/dist/gates/promise-safety.js +51 -257
  24. package/dist/gates/test-quality-lang.d.ts +30 -0
  25. package/dist/gates/test-quality-lang.js +188 -0
  26. package/dist/gates/test-quality.d.ts +0 -14
  27. package/dist/gates/test-quality.js +13 -186
  28. package/dist/pattern-index/indexer-helpers.d.ts +38 -0
  29. package/dist/pattern-index/indexer-helpers.js +111 -0
  30. package/dist/pattern-index/indexer-lang.d.ts +13 -0
  31. package/dist/pattern-index/indexer-lang.js +244 -0
  32. package/dist/pattern-index/indexer-ts.d.ts +22 -0
  33. package/dist/pattern-index/indexer-ts.js +258 -0
  34. package/dist/pattern-index/indexer.d.ts +4 -106
  35. package/dist/pattern-index/indexer.js +58 -707
  36. package/dist/pattern-index/staleness-data.d.ts +6 -0
  37. package/dist/pattern-index/staleness-data.js +262 -0
  38. package/dist/pattern-index/staleness.js +1 -258
  39. package/dist/templates/index.d.ts +12 -16
  40. package/dist/templates/index.js +11 -527
  41. package/dist/templates/paradigms.d.ts +2 -0
  42. package/dist/templates/paradigms.js +46 -0
  43. package/dist/templates/presets.d.ts +14 -0
  44. package/dist/templates/presets.js +227 -0
  45. package/dist/templates/universal-config.d.ts +2 -0
  46. package/dist/templates/universal-config.js +171 -0
  47. package/package.json +1 -1
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Language-specific test quality checks for Go and Java/Kotlin.
3
+ * Extracted from test-quality.ts to keep it under 500 lines.
4
+ */
5
+ /**
6
+ * Go test quality checks.
7
+ * Go tests use func TestXxx(t *testing.T) pattern.
8
+ * Assertions via t.Fatal, t.Error, t.Fatalf, t.Errorf, t.Fail, t.FailNow.
9
+ * Also checks for t.Run subtests and table-driven patterns.
10
+ */
11
+ export function checkGoTestQuality(content, file, issues, config) {
12
+ const lines = content.split('\n');
13
+ let inTestFunc = false;
14
+ let testStartLine = 0;
15
+ let braceDepth = 0;
16
+ let hasAssertion = false;
17
+ let testContent = '';
18
+ for (let i = 0; i < lines.length; i++) {
19
+ const line = lines[i];
20
+ const trimmed = line.trim();
21
+ // Detect test function: func TestXxx(t *testing.T) {
22
+ const testMatch = trimmed.match(/^func\s+(Test\w+|Benchmark\w+)\s*\(/);
23
+ if (testMatch && !inTestFunc) {
24
+ inTestFunc = true;
25
+ testStartLine = i + 1;
26
+ braceDepth = 0;
27
+ hasAssertion = false;
28
+ testContent = '';
29
+ // Count braces on this line
30
+ for (const ch of line) {
31
+ if (ch === '{')
32
+ braceDepth++;
33
+ if (ch === '}')
34
+ braceDepth--;
35
+ }
36
+ continue;
37
+ }
38
+ if (inTestFunc) {
39
+ testContent += line + '\n';
40
+ for (const ch of line) {
41
+ if (ch === '{')
42
+ braceDepth++;
43
+ if (ch === '}')
44
+ braceDepth--;
45
+ }
46
+ // Go test assertions: t.Fatal, t.Error, t.Fatalf, t.Errorf, t.Fail, t.FailNow
47
+ // Also: assert/require from testify, t.Run for subtests
48
+ if (/\bt\.\s*(?:Fatal|Error|Fatalf|Errorf|Fail|FailNow|Log|Logf|Skip|Skipf|Helper)\s*\(/.test(line) ||
49
+ /\bt\.Run\s*\(/.test(line) ||
50
+ /\bassert\.\w+\s*\(/.test(line) || /\brequire\.\w+\s*\(/.test(line) ||
51
+ /\bif\b.*\bt\./.test(line)) {
52
+ hasAssertion = true;
53
+ }
54
+ // Tautological: if true { t.Fatal... } or assert.True(t, true)
55
+ if (config.check_tautological) {
56
+ if (/assert\.True\s*\(\s*\w+\s*,\s*true\s*\)/.test(line) ||
57
+ /assert\.Equal\s*\(\s*\w+\s*,\s*(\d+|"[^"]*")\s*,\s*\1\s*\)/.test(line)) {
58
+ issues.push({
59
+ file, line: i + 1, pattern: 'tautological-assertion',
60
+ reason: 'Tautological assertion — comparing a constant to itself proves nothing',
61
+ });
62
+ }
63
+ }
64
+ // End of function
65
+ if (braceDepth === 0 && testContent.trim()) {
66
+ const meaningful = testContent.split('\n').filter(l => {
67
+ const t = l.trim();
68
+ return t && t !== '{' && t !== '}' && !t.startsWith('//');
69
+ });
70
+ if (config.check_empty_tests && meaningful.length === 0) {
71
+ issues.push({
72
+ file, line: testStartLine, pattern: 'empty-test',
73
+ reason: 'Empty test function — no test logic',
74
+ });
75
+ }
76
+ else if (config.check_empty_tests && !hasAssertion && meaningful.length > 0) {
77
+ issues.push({
78
+ file, line: testStartLine, pattern: 'no-assertion',
79
+ reason: 'Test has no assertions (t.Error, t.Fatal, assert.*) — executes code but never verifies',
80
+ });
81
+ }
82
+ inTestFunc = false;
83
+ }
84
+ }
85
+ }
86
+ }
87
+ /**
88
+ * Java/Kotlin test quality checks.
89
+ * JUnit 4: @Test + Assert.assertEquals, assertTrue, etc.
90
+ * JUnit 5: @Test + Assertions.assertEquals, assertThrows, etc.
91
+ * Kotlin: @Test + kotlin.test assertEquals, etc.
92
+ */
93
+ export function checkJavaKotlinTestQuality(content, file, ext, issues, config) {
94
+ const lines = content.split('\n');
95
+ const isKotlin = ext === '.kt';
96
+ let inTestMethod = false;
97
+ let testStartLine = 0;
98
+ let braceDepth = 0;
99
+ let hasAssertion = false;
100
+ let mockCount = 0;
101
+ for (let i = 0; i < lines.length; i++) {
102
+ const line = lines[i];
103
+ const trimmed = line.trim();
104
+ // Detect @Test annotation (next non-empty line is the method)
105
+ if (trimmed === '@Test' || /^@Test\s*(\(|$)/.test(trimmed)) {
106
+ // Look for the method signature on this or next lines
107
+ for (let j = i + 1; j < Math.min(i + 3, lines.length); j++) {
108
+ const methodLine = lines[j].trim();
109
+ const methodMatch = isKotlin
110
+ ? methodLine.match(/^(?:fun|suspend\s+fun)\s+(\w+)\s*\(/)
111
+ : methodLine.match(/^(?:public\s+|private\s+|protected\s+)?(?:static\s+)?void\s+(\w+)\s*\(/);
112
+ if (methodMatch) {
113
+ inTestMethod = true;
114
+ testStartLine = j + 1;
115
+ braceDepth = 0;
116
+ hasAssertion = false;
117
+ mockCount = 0;
118
+ // Count braces
119
+ for (const ch of lines[j]) {
120
+ if (ch === '{')
121
+ braceDepth++;
122
+ if (ch === '}')
123
+ braceDepth--;
124
+ }
125
+ i = j;
126
+ break;
127
+ }
128
+ }
129
+ continue;
130
+ }
131
+ if (inTestMethod) {
132
+ for (const ch of line) {
133
+ if (ch === '{')
134
+ braceDepth++;
135
+ if (ch === '}')
136
+ braceDepth--;
137
+ }
138
+ // JUnit 4/5 assertions
139
+ if (/\b(?:assert(?:Equals|True|False|NotNull|Null|That|Throws|DoesNotThrow|Same|NotSame|ArrayEquals)|assertEquals|assertTrue|assertFalse|assertNotNull|assertNull|assertThrows)\s*\(/.test(line)) {
140
+ hasAssertion = true;
141
+ }
142
+ // Kotlin test assertions
143
+ if (isKotlin && /\b(?:assertEquals|assertTrue|assertFalse|assertNotNull|assertNull|assertFailsWith|assertIs|assertContains|expect)\s*[({]/.test(line)) {
144
+ hasAssertion = true;
145
+ }
146
+ // Hamcrest / AssertJ
147
+ if (/\bassertThat\s*\(/.test(line) || /\.should\w*\(/.test(line)) {
148
+ hasAssertion = true;
149
+ }
150
+ // Verify (Mockito)
151
+ if (/\bverify\s*\(/.test(line)) {
152
+ hasAssertion = true;
153
+ }
154
+ // Mock counting
155
+ if (/\b(?:mock|spy|when|doReturn|doThrow|doNothing|Mockito\.\w+)\s*\(/.test(line) ||
156
+ /@Mock\b/.test(line) || /@InjectMocks\b/.test(line)) {
157
+ mockCount++;
158
+ }
159
+ // Tautological
160
+ if (config.check_tautological) {
161
+ if (/assertEquals\s*\(\s*true\s*,\s*true\s*\)/.test(line) ||
162
+ /assertTrue\s*\(\s*true\s*\)/.test(line) ||
163
+ /assertEquals\s*\(\s*(\d+)\s*,\s*\1\s*\)/.test(line)) {
164
+ issues.push({
165
+ file, line: i + 1, pattern: 'tautological-assertion',
166
+ reason: 'Tautological assertion — comparing a constant to itself proves nothing',
167
+ });
168
+ }
169
+ }
170
+ // End of method
171
+ if (braceDepth === 0) {
172
+ if (config.check_empty_tests && !hasAssertion) {
173
+ issues.push({
174
+ file, line: testStartLine, pattern: 'no-assertion',
175
+ reason: 'Test has no assertions — executes code but never verifies results',
176
+ });
177
+ }
178
+ if (config.check_mock_heavy && mockCount > config.max_mocks_per_test) {
179
+ issues.push({
180
+ file, line: testStartLine, pattern: 'mock-heavy',
181
+ reason: `Test uses ${mockCount} mocks (max: ${config.max_mocks_per_test}) — may be testing mocks, not real behavior`,
182
+ });
183
+ }
184
+ inTestMethod = false;
185
+ }
186
+ }
187
+ }
188
+ }
@@ -50,18 +50,4 @@ export declare class TestQualityGate extends Gate {
50
50
  private analyzeJSTestBlock;
51
51
  private checkPythonTestQuality;
52
52
  private analyzePythonTestBlock;
53
- /**
54
- * Go test quality checks.
55
- * Go tests use func TestXxx(t *testing.T) pattern.
56
- * Assertions via t.Fatal, t.Error, t.Fatalf, t.Errorf, t.Fail, t.FailNow.
57
- * Also checks for t.Run subtests and table-driven patterns.
58
- */
59
- private checkGoTestQuality;
60
- /**
61
- * Java/Kotlin test quality checks.
62
- * JUnit 4: @Test + Assert.assertEquals, assertTrue, etc.
63
- * JUnit 5: @Test + Assertions.assertEquals, assertThrows, etc.
64
- * Kotlin: @Test + kotlin.test assertEquals, etc.
65
- */
66
- private checkJavaKotlinTestQuality;
67
53
  }
@@ -26,6 +26,7 @@
26
26
  import { Gate } from './base.js';
27
27
  import { FileScanner } from '../utils/scanner.js';
28
28
  import { Logger } from '../utils/logger.js';
29
+ import { checkGoTestQuality, checkJavaKotlinTestQuality } from './test-quality-lang.js';
29
30
  import fs from 'fs-extra';
30
31
  import path from 'path';
31
32
  export class TestQualityGate extends Gate {
@@ -76,10 +77,20 @@ export class TestQualityGate extends Gate {
76
77
  this.checkPythonTestQuality(content, file, issues);
77
78
  }
78
79
  else if (ext === '.go') {
79
- this.checkGoTestQuality(content, file, issues);
80
+ checkGoTestQuality(content, file, issues, {
81
+ check_empty_tests: this.config.check_empty_tests,
82
+ check_tautological: this.config.check_tautological,
83
+ check_mock_heavy: this.config.check_mock_heavy,
84
+ max_mocks_per_test: this.config.max_mocks_per_test,
85
+ });
80
86
  }
81
87
  else if (ext === '.java' || ext === '.kt') {
82
- this.checkJavaKotlinTestQuality(content, file, ext, issues);
88
+ checkJavaKotlinTestQuality(content, file, ext, issues, {
89
+ check_empty_tests: this.config.check_empty_tests,
90
+ check_tautological: this.config.check_tautological,
91
+ check_mock_heavy: this.config.check_mock_heavy,
92
+ max_mocks_per_test: this.config.max_mocks_per_test,
93
+ });
83
94
  }
84
95
  }
85
96
  catch { /* skip */ }
@@ -325,188 +336,4 @@ export class TestQualityGate extends Gate {
325
336
  });
326
337
  }
327
338
  }
328
- /**
329
- * Go test quality checks.
330
- * Go tests use func TestXxx(t *testing.T) pattern.
331
- * Assertions via t.Fatal, t.Error, t.Fatalf, t.Errorf, t.Fail, t.FailNow.
332
- * Also checks for t.Run subtests and table-driven patterns.
333
- */
334
- checkGoTestQuality(content, file, issues) {
335
- const lines = content.split('\n');
336
- let inTestFunc = false;
337
- let testStartLine = 0;
338
- let braceDepth = 0;
339
- let hasAssertion = false;
340
- let testContent = '';
341
- for (let i = 0; i < lines.length; i++) {
342
- const line = lines[i];
343
- const trimmed = line.trim();
344
- // Detect test function: func TestXxx(t *testing.T) {
345
- const testMatch = trimmed.match(/^func\s+(Test\w+|Benchmark\w+)\s*\(/);
346
- if (testMatch && !inTestFunc) {
347
- inTestFunc = true;
348
- testStartLine = i + 1;
349
- braceDepth = 0;
350
- hasAssertion = false;
351
- testContent = '';
352
- // Count braces on this line
353
- for (const ch of line) {
354
- if (ch === '{')
355
- braceDepth++;
356
- if (ch === '}')
357
- braceDepth--;
358
- }
359
- continue;
360
- }
361
- if (inTestFunc) {
362
- testContent += line + '\n';
363
- for (const ch of line) {
364
- if (ch === '{')
365
- braceDepth++;
366
- if (ch === '}')
367
- braceDepth--;
368
- }
369
- // Go test assertions: t.Fatal, t.Error, t.Fatalf, t.Errorf, t.Fail, t.FailNow
370
- // Also: assert/require from testify, t.Run for subtests
371
- if (/\bt\.\s*(?:Fatal|Error|Fatalf|Errorf|Fail|FailNow|Log|Logf|Skip|Skipf|Helper)\s*\(/.test(line) ||
372
- /\bt\.Run\s*\(/.test(line) ||
373
- /\bassert\.\w+\s*\(/.test(line) || /\brequire\.\w+\s*\(/.test(line) ||
374
- /\bif\b.*\bt\./.test(line)) {
375
- hasAssertion = true;
376
- }
377
- // Tautological: if true { t.Fatal... } or assert.True(t, true)
378
- if (this.config.check_tautological) {
379
- if (/assert\.True\s*\(\s*\w+\s*,\s*true\s*\)/.test(line) ||
380
- /assert\.Equal\s*\(\s*\w+\s*,\s*(\d+|"[^"]*")\s*,\s*\1\s*\)/.test(line)) {
381
- issues.push({
382
- file, line: i + 1, pattern: 'tautological-assertion',
383
- reason: 'Tautological assertion — comparing a constant to itself proves nothing',
384
- });
385
- }
386
- }
387
- // End of function
388
- if (braceDepth === 0 && testContent.trim()) {
389
- const meaningful = testContent.split('\n').filter(l => {
390
- const t = l.trim();
391
- return t && t !== '{' && t !== '}' && !t.startsWith('//');
392
- });
393
- if (this.config.check_empty_tests && meaningful.length === 0) {
394
- issues.push({
395
- file, line: testStartLine, pattern: 'empty-test',
396
- reason: 'Empty test function — no test logic',
397
- });
398
- }
399
- else if (this.config.check_empty_tests && !hasAssertion && meaningful.length > 0) {
400
- issues.push({
401
- file, line: testStartLine, pattern: 'no-assertion',
402
- reason: 'Test has no assertions (t.Error, t.Fatal, assert.*) — executes code but never verifies',
403
- });
404
- }
405
- inTestFunc = false;
406
- }
407
- }
408
- }
409
- }
410
- /**
411
- * Java/Kotlin test quality checks.
412
- * JUnit 4: @Test + Assert.assertEquals, assertTrue, etc.
413
- * JUnit 5: @Test + Assertions.assertEquals, assertThrows, etc.
414
- * Kotlin: @Test + kotlin.test assertEquals, etc.
415
- */
416
- checkJavaKotlinTestQuality(content, file, ext, issues) {
417
- const lines = content.split('\n');
418
- const isKotlin = ext === '.kt';
419
- let inTestMethod = false;
420
- let testStartLine = 0;
421
- let braceDepth = 0;
422
- let hasAssertion = false;
423
- let mockCount = 0;
424
- for (let i = 0; i < lines.length; i++) {
425
- const line = lines[i];
426
- const trimmed = line.trim();
427
- // Detect @Test annotation (next non-empty line is the method)
428
- if (trimmed === '@Test' || /^@Test\s*(\(|$)/.test(trimmed)) {
429
- // Look for the method signature on this or next lines
430
- for (let j = i + 1; j < Math.min(i + 3, lines.length); j++) {
431
- const methodLine = lines[j].trim();
432
- const methodMatch = isKotlin
433
- ? methodLine.match(/^(?:fun|suspend\s+fun)\s+(\w+)\s*\(/)
434
- : methodLine.match(/^(?:public\s+|private\s+|protected\s+)?(?:static\s+)?void\s+(\w+)\s*\(/);
435
- if (methodMatch) {
436
- inTestMethod = true;
437
- testStartLine = j + 1;
438
- braceDepth = 0;
439
- hasAssertion = false;
440
- mockCount = 0;
441
- // Count braces
442
- for (const ch of lines[j]) {
443
- if (ch === '{')
444
- braceDepth++;
445
- if (ch === '}')
446
- braceDepth--;
447
- }
448
- i = j;
449
- break;
450
- }
451
- }
452
- continue;
453
- }
454
- if (inTestMethod) {
455
- for (const ch of line) {
456
- if (ch === '{')
457
- braceDepth++;
458
- if (ch === '}')
459
- braceDepth--;
460
- }
461
- // JUnit 4/5 assertions
462
- if (/\b(?:assert(?:Equals|True|False|NotNull|Null|That|Throws|DoesNotThrow|Same|NotSame|ArrayEquals)|assertEquals|assertTrue|assertFalse|assertNotNull|assertNull|assertThrows)\s*\(/.test(line)) {
463
- hasAssertion = true;
464
- }
465
- // Kotlin test assertions
466
- if (isKotlin && /\b(?:assertEquals|assertTrue|assertFalse|assertNotNull|assertNull|assertFailsWith|assertIs|assertContains|expect)\s*[({]/.test(line)) {
467
- hasAssertion = true;
468
- }
469
- // Hamcrest / AssertJ
470
- if (/\bassertThat\s*\(/.test(line) || /\.should\w*\(/.test(line)) {
471
- hasAssertion = true;
472
- }
473
- // Verify (Mockito)
474
- if (/\bverify\s*\(/.test(line)) {
475
- hasAssertion = true;
476
- }
477
- // Mock counting
478
- if (/\b(?:mock|spy|when|doReturn|doThrow|doNothing|Mockito\.\w+)\s*\(/.test(line) ||
479
- /@Mock\b/.test(line) || /@InjectMocks\b/.test(line)) {
480
- mockCount++;
481
- }
482
- // Tautological
483
- if (this.config.check_tautological) {
484
- if (/assertEquals\s*\(\s*true\s*,\s*true\s*\)/.test(line) ||
485
- /assertTrue\s*\(\s*true\s*\)/.test(line) ||
486
- /assertEquals\s*\(\s*(\d+)\s*,\s*\1\s*\)/.test(line)) {
487
- issues.push({
488
- file, line: i + 1, pattern: 'tautological-assertion',
489
- reason: 'Tautological assertion — comparing a constant to itself proves nothing',
490
- });
491
- }
492
- }
493
- // End of method
494
- if (braceDepth === 0) {
495
- if (this.config.check_empty_tests && !hasAssertion) {
496
- issues.push({
497
- file, line: testStartLine, pattern: 'no-assertion',
498
- reason: 'Test has no assertions — executes code but never verifies results',
499
- });
500
- }
501
- if (this.config.check_mock_heavy && mockCount > this.config.max_mocks_per_test) {
502
- issues.push({
503
- file, line: testStartLine, pattern: 'mock-heavy',
504
- reason: `Test uses ${mockCount} mocks (max: ${this.config.max_mocks_per_test}) — may be testing mocks, not real behavior`,
505
- });
506
- }
507
- inTestMethod = false;
508
- }
509
- }
510
- }
511
- }
512
339
  }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Pattern Indexer — Pure Utility Helpers
3
+ *
4
+ * Standalone pure functions shared across language extractors and the main
5
+ * indexer class. No class state is referenced here.
6
+ */
7
+ import type { PatternEntry, PatternType } from './types.js';
8
+ /** SHA-256 of `content`, truncated to 16 hex chars. */
9
+ export declare function hashContent(content: string): string;
10
+ export interface PatternEntryParams {
11
+ type: PatternType;
12
+ name: string;
13
+ file: string;
14
+ line: number;
15
+ endLine: number;
16
+ signature: string;
17
+ description: string;
18
+ keywords: string[];
19
+ content: string;
20
+ exported: boolean;
21
+ }
22
+ /** Build a complete PatternEntry from constituent parts. */
23
+ export declare function createPatternEntry(params: PatternEntryParams): PatternEntry;
24
+ /** Split camelCase / PascalCase / snake_case names into unique lowercase words. */
25
+ export declare function extractKeywords(name: string): string[];
26
+ /** Walk forward from `startIndex` and return the line index after the closing brace. */
27
+ export declare function findBraceBlockEnd(lines: string[], startIndex: number): number;
28
+ /** Return the source lines for a brace-delimited block starting at `startIndex`. */
29
+ export declare function getBraceBlockContent(lines: string[], startIndex: number): string;
30
+ /**
31
+ * Collect consecutive `//` comments immediately above `startIndex` (Go / Rust style).
32
+ * Walks upward until a non-comment line is encountered.
33
+ */
34
+ export declare function getCOMLineComments(lines: string[], startIndex: number): string;
35
+ /**
36
+ * Extract the first JavaDoc `/** … *\/` comment block found above `startIndex`.
37
+ */
38
+ export declare function getJavaDoc(lines: string[], startIndex: number): string;
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Pattern Indexer — Pure Utility Helpers
3
+ *
4
+ * Standalone pure functions shared across language extractors and the main
5
+ * indexer class. No class state is referenced here.
6
+ */
7
+ import { createHash } from 'crypto';
8
+ // ---------------------------------------------------------------------------
9
+ // Hashing / ID generation
10
+ // ---------------------------------------------------------------------------
11
+ /** SHA-256 of `content`, truncated to 16 hex chars. */
12
+ export function hashContent(content) {
13
+ return createHash('sha256').update(content).digest('hex').slice(0, 16);
14
+ }
15
+ /** Build a complete PatternEntry from constituent parts. */
16
+ export function createPatternEntry(params) {
17
+ const id = hashContent(`${params.file}:${params.name}:${params.line}`);
18
+ const hash = hashContent(params.content);
19
+ return {
20
+ id,
21
+ type: params.type,
22
+ name: params.name,
23
+ file: params.file,
24
+ line: params.line,
25
+ endLine: params.endLine,
26
+ signature: params.signature,
27
+ description: params.description,
28
+ keywords: params.keywords,
29
+ hash,
30
+ exported: params.exported,
31
+ usageCount: 0,
32
+ indexedAt: new Date().toISOString(),
33
+ };
34
+ }
35
+ // ---------------------------------------------------------------------------
36
+ // Keyword extraction
37
+ // ---------------------------------------------------------------------------
38
+ /** Split camelCase / PascalCase / snake_case names into unique lowercase words. */
39
+ export function extractKeywords(name) {
40
+ const words = name
41
+ .replace(/([a-z])([A-Z])/g, '$1 $2')
42
+ .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
43
+ .toLowerCase()
44
+ .split(/[\s_-]+/)
45
+ .filter(w => w.length > 1);
46
+ return [...new Set(words)];
47
+ }
48
+ // ---------------------------------------------------------------------------
49
+ // Brace-based block helpers (Go, Rust, JVM, C-style)
50
+ // ---------------------------------------------------------------------------
51
+ /** Walk forward from `startIndex` and return the line index after the closing brace. */
52
+ export function findBraceBlockEnd(lines, startIndex) {
53
+ let braceCount = 0;
54
+ let started = false;
55
+ for (let i = startIndex; i < lines.length; i++) {
56
+ const line = lines[i];
57
+ if (line.includes('{')) {
58
+ braceCount += (line.match(/\{/g) || []).length;
59
+ started = true;
60
+ }
61
+ if (line.includes('}')) {
62
+ braceCount -= (line.match(/\}/g) || []).length;
63
+ }
64
+ if (started && braceCount === 0)
65
+ return i + 1;
66
+ }
67
+ return lines.length;
68
+ }
69
+ /** Return the source lines for a brace-delimited block starting at `startIndex`. */
70
+ export function getBraceBlockContent(lines, startIndex) {
71
+ const end = findBraceBlockEnd(lines, startIndex);
72
+ return lines.slice(startIndex, end).join('\n');
73
+ }
74
+ // ---------------------------------------------------------------------------
75
+ // Comment extraction helpers
76
+ // ---------------------------------------------------------------------------
77
+ /**
78
+ * Collect consecutive `//` comments immediately above `startIndex` (Go / Rust style).
79
+ * Walks upward until a non-comment line is encountered.
80
+ */
81
+ export function getCOMLineComments(lines, startIndex) {
82
+ const comments = [];
83
+ for (let i = startIndex; i >= 0; i--) {
84
+ const line = lines[i].trim();
85
+ if (line.startsWith('//')) {
86
+ comments.unshift(line.replace('//', '').trim());
87
+ }
88
+ else {
89
+ break;
90
+ }
91
+ }
92
+ return comments.join(' ');
93
+ }
94
+ /**
95
+ * Extract the first JavaDoc `/** … *\/` comment block found above `startIndex`.
96
+ */
97
+ export function getJavaDoc(lines, startIndex) {
98
+ const comments = [];
99
+ let inDoc = false;
100
+ for (let i = startIndex; i >= 0; i--) {
101
+ const line = lines[i].trim();
102
+ if (line.endsWith('*/'))
103
+ inDoc = true;
104
+ if (inDoc) {
105
+ comments.unshift(line.replace('/**', '').replace('*/', '').replace(/^\*/, '').trim());
106
+ }
107
+ if (line.startsWith('/**'))
108
+ break;
109
+ }
110
+ return comments.join(' ');
111
+ }
@@ -0,0 +1,13 @@
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 type { PatternEntry } from './types.js';
9
+ export declare function extractGoPatterns(filePath: string, content: string, rootDir: string): PatternEntry[];
10
+ export declare function extractRustPatterns(filePath: string, content: string, rootDir: string): PatternEntry[];
11
+ export declare function extractJVMStylePatterns(filePath: string, content: string, rootDir: string): PatternEntry[];
12
+ export declare function extractGenericCPatterns(_filePath: string, _content: string): PatternEntry[];
13
+ export declare function extractPythonPatterns(filePath: string, content: string, rootDir: string, minNameLength: number): PatternEntry[];